Some checks failed
Build docker and publish / build (20.15.1) (push) Failing after 8m2s
164 lines
4.9 KiB
Go
164 lines
4.9 KiB
Go
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/pkg/logger"
|
|
"github.com/perfect-panel/server/pkg/xerr"
|
|
"github.com/pkg/errors"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
type accountMergeResult struct {
|
|
OwnerUserID int64
|
|
DeviceUserID int64
|
|
MovedDevices []modelUser.Device
|
|
RemovedSubscribes []modelUser.Subscribe
|
|
}
|
|
|
|
type accountMergeHelper struct {
|
|
ctx context.Context
|
|
svcCtx *svc.ServiceContext
|
|
}
|
|
|
|
func newAccountMergeHelper(ctx context.Context, svcCtx *svc.ServiceContext) *accountMergeHelper {
|
|
return &accountMergeHelper{
|
|
ctx: ctx,
|
|
svcCtx: svcCtx,
|
|
}
|
|
}
|
|
|
|
func (h *accountMergeHelper) mergeIntoOwner(ownerUserID, deviceUserID int64, source string) (*accountMergeResult, error) {
|
|
if ownerUserID == 0 || deviceUserID == 0 {
|
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "merge user id is empty")
|
|
}
|
|
if ownerUserID == deviceUserID {
|
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "cannot merge same user id")
|
|
}
|
|
|
|
result := &accountMergeResult{
|
|
OwnerUserID: ownerUserID,
|
|
DeviceUserID: deviceUserID,
|
|
}
|
|
|
|
err := h.svcCtx.DB.WithContext(h.ctx).Transaction(func(tx *gorm.DB) error {
|
|
var owner modelUser.User
|
|
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
|
Where("id = ?", ownerUserID).
|
|
First(&owner).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.UserNotExist), "owner user %d not found", ownerUserID)
|
|
}
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query owner user failed")
|
|
}
|
|
|
|
var device modelUser.User
|
|
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
|
Where("id = ?", deviceUserID).
|
|
First(&device).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.UserNotExist), "device user %d not found", deviceUserID)
|
|
}
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query device user failed")
|
|
}
|
|
|
|
exitHelper := newFamilyExitHelper(h.ctx, h.svcCtx)
|
|
if err := exitHelper.removeUserFromActiveFamily(tx, deviceUserID, false); err != nil {
|
|
return err
|
|
}
|
|
|
|
removedSubscribes, err := clearMemberSubscribes(tx, deviceUserID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
result.RemovedSubscribes = removedSubscribes
|
|
|
|
var devices []modelUser.Device
|
|
if err := tx.Where("user_id = ?", deviceUserID).Find(&devices).Error; err != nil {
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query device list failed")
|
|
}
|
|
if len(devices) > 0 {
|
|
if err := tx.Model(&modelUser.Device{}).
|
|
Where("user_id = ?", deviceUserID).
|
|
Updates(map[string]interface{}{
|
|
"user_id": ownerUserID,
|
|
"updated_at": time.Now(),
|
|
}).Error; err != nil {
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update device ownership failed")
|
|
}
|
|
}
|
|
result.MovedDevices = devices
|
|
|
|
if err := tx.Model(&modelUser.AuthMethods{}).
|
|
Where("user_id = ?", deviceUserID).
|
|
Update("user_id", ownerUserID).Error; err != nil {
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "migrate auth methods failed")
|
|
}
|
|
|
|
if err := tx.Model(&modelUser.User{}).
|
|
Where("id = ?", deviceUserID).
|
|
Update("enable", false).Error; err != nil {
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "disable device user failed")
|
|
}
|
|
if err := tx.Where("id = ?", deviceUserID).Delete(&modelUser.User{}).Error; err != nil {
|
|
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "soft delete device user failed")
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := h.clearCaches(result); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
logger.WithContext(h.ctx).Infow("device account merged into owner",
|
|
logger.Field("owner_user_id", ownerUserID),
|
|
logger.Field("device_user_id", deviceUserID),
|
|
logger.Field("moved_devices", len(result.MovedDevices)),
|
|
logger.Field("removed_subscribes", len(result.RemovedSubscribes)),
|
|
logger.Field("source", source),
|
|
)
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (h *accountMergeHelper) clearCaches(result *accountMergeResult) error {
|
|
if result == nil {
|
|
return nil
|
|
}
|
|
|
|
if err := h.svcCtx.UserModel.ClearUserCache(h.ctx,
|
|
&modelUser.User{Id: result.OwnerUserID},
|
|
&modelUser.User{Id: result.DeviceUserID},
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(result.MovedDevices) > 0 {
|
|
deviceModels := make([]*modelUser.Device, 0, len(result.MovedDevices))
|
|
for i := range result.MovedDevices {
|
|
device := result.MovedDevices[i]
|
|
deviceModels = append(deviceModels, &device)
|
|
}
|
|
if err := h.svcCtx.UserModel.ClearDeviceCache(h.ctx, deviceModels...); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if len(result.RemovedSubscribes) > 0 {
|
|
familyHelper := newFamilyBindingHelper(h.ctx, h.svcCtx)
|
|
if err := familyHelper.clearRemovedMemberSubscribeCache(result.RemovedSubscribes); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|