fix(user): move bind-email subscriptions to owner
Build docker and publish / build (20.15.1) (push) Has been cancelled

This commit is contained in:
2026-05-01 05:24:11 -07:00
parent cf70838142
commit d748a7e75d
3 changed files with 400 additions and 9 deletions
@@ -2,10 +2,13 @@ package user
import (
"context"
"fmt"
"time"
modelOrder "github.com/perfect-panel/server/internal/model/order"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/uuidx"
"github.com/perfect-panel/server/pkg/xerr"
"github.com/pkg/errors"
"gorm.io/gorm"
@@ -22,6 +25,13 @@ type familyBindingHelper struct {
svcCtx *svc.ServiceContext
}
type familySubscribeTransferMode uint8
const (
familySubscribeDiscard familySubscribeTransferMode = iota
familySubscribeMoveToOwner
)
func newFamilyBindingHelper(ctx context.Context, svcCtx *svc.ServiceContext) *familyBindingHelper {
return &familyBindingHelper{
ctx: ctx,
@@ -111,6 +121,14 @@ func (h *familyBindingHelper) validateJoinFamily(ownerUserId, memberUserId int64
}
func (h *familyBindingHelper) joinFamily(ownerUserId, memberUserId int64, source string) (*familyJoinResult, error) {
return h.joinFamilyWithSubscribeMode(ownerUserId, memberUserId, source, familySubscribeDiscard)
}
func (h *familyBindingHelper) joinFamilyAndMoveSubscribesToOwner(ownerUserId, memberUserId int64, source string) (*familyJoinResult, error) {
return h.joinFamilyWithSubscribeMode(ownerUserId, memberUserId, source, familySubscribeMoveToOwner)
}
func (h *familyBindingHelper) joinFamilyWithSubscribeMode(ownerUserId, memberUserId int64, source string, subscribeMode familySubscribeTransferMode) (*familyJoinResult, error) {
if ownerUserId == memberUserId {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.FamilyAlreadyBound), "user already bound to this family")
}
@@ -118,7 +136,7 @@ func (h *familyBindingHelper) joinFamily(ownerUserId, memberUserId int64, source
result := &familyJoinResult{
OwnerUserId: ownerUserId,
}
removedSubscribes := make([]user.Subscribe, 0)
affectedSubscribes := make([]user.Subscribe, 0)
err := h.svcCtx.DB.WithContext(h.ctx).Transaction(func(tx *gorm.DB) error {
ownerFamily, err := h.getOrCreateOwnerFamily(tx, ownerUserId)
@@ -182,9 +200,17 @@ func (h *familyBindingHelper) joinFamily(ownerUserId, memberUserId int64, source
}
}
removedSubscribes, err = transferMemberSubscribesToOwner(tx, memberUserId, ownerUserId)
if err != nil {
return err
switch subscribeMode {
case familySubscribeMoveToOwner:
affectedSubscribes, err = moveMemberSubscribesToOwner(tx, memberUserId, ownerUserId)
if err != nil {
return err
}
default:
affectedSubscribes, err = transferMemberSubscribesToOwner(tx, memberUserId, ownerUserId)
if err != nil {
return err
}
}
return nil
})
@@ -193,12 +219,56 @@ func (h *familyBindingHelper) joinFamily(ownerUserId, memberUserId int64, source
return nil, err
}
if err = h.clearRemovedMemberSubscribeCache(removedSubscribes); err != nil {
return nil, err
if subscribeMode == familySubscribeMoveToOwner {
if err = h.clearMovedMemberSubscribeCache(affectedSubscribes, ownerUserId); err != nil {
return nil, err
}
} else {
if err = h.clearRemovedMemberSubscribeCache(affectedSubscribes); err != nil {
return nil, err
}
}
return result, nil
}
func moveMemberSubscribesToOwner(tx *gorm.DB, memberUserId, ownerUserId int64) ([]user.Subscribe, error) {
var subscribes []user.Subscribe
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
Model(&user.Subscribe{}).
Where("user_id = ?", memberUserId).
Find(&subscribes).Error; err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query member subscribe list failed")
}
if len(subscribes) == 0 {
return nil, nil
}
for _, sub := range subscribes {
newToken := uuidx.SubscribeToken(fmt.Sprintf("familyMove:%d:%d:%s", ownerUserId, sub.Id, uuidx.NewUUID().String()))
if err := tx.Model(&user.Subscribe{}).
Where("id = ? AND user_id = ?", sub.Id, memberUserId).
Updates(map[string]interface{}{
"user_id": ownerUserId,
"token": newToken,
"uuid": uuidx.NewUUID().String(),
"updated_at": time.Now(),
}).Error; err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "move member subscribe to owner failed")
}
if sub.OrderId > 0 {
if err := tx.Model(&modelOrder.Order{}).
Where("id = ? AND subscription_user_id = ?", sub.OrderId, memberUserId).
Updates(map[string]interface{}{
"subscription_user_id": ownerUserId,
"subscribe_token": newToken,
}).Error; err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "move member subscribe order to owner failed")
}
}
}
return subscribes, nil
}
func transferMemberSubscribesToOwner(tx *gorm.DB, memberUserId, ownerUserId int64) ([]user.Subscribe, error) {
var subscribes []user.Subscribe
if err := tx.Model(&user.Subscribe{}).
@@ -238,6 +308,37 @@ func (h *familyBindingHelper) clearRemovedMemberSubscribeCache(removedSubscribes
return nil
}
func (h *familyBindingHelper) clearMovedMemberSubscribeCache(movedSubscribes []user.Subscribe, ownerUserId int64) error {
if len(movedSubscribes) == 0 {
return nil
}
cacheModels := make([]*user.Subscribe, 0, len(movedSubscribes)*2)
ownerCopies := make([]user.Subscribe, len(movedSubscribes))
for i := range movedSubscribes {
cacheModels = append(cacheModels, &movedSubscribes[i])
ownerCopies[i] = movedSubscribes[i]
ownerCopies[i].UserId = ownerUserId
cacheModels = append(cacheModels, &ownerCopies[i])
}
if err := h.svcCtx.UserModel.ClearSubscribeCache(h.ctx, cacheModels...); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "clear moved subscribe cache failed")
}
_, subscribeIDSet := buildRemovedSubscribeCacheMeta(movedSubscribes)
for subscribeID := range subscribeIDSet {
if err := h.svcCtx.SubscribeModel.ClearCache(h.ctx, subscribeID); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "clear subscribe cache failed")
}
}
if err := h.svcCtx.NodeModel.ClearServerAllCache(h.ctx); err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "clear node cache failed")
}
return nil
}
func buildRemovedSubscribeCacheMeta(removedSubscribes []user.Subscribe) ([]*user.Subscribe, map[int64]struct{}) {
subscribeModels := make([]*user.Subscribe, 0, len(removedSubscribes))
subscribeIDSet := make(map[int64]struct{}, len(removedSubscribes))