diff --git a/queue/logic/order/activateOrderLogic.go b/queue/logic/order/activateOrderLogic.go index 2c8362c..7a810bd 100644 --- a/queue/logic/order/activateOrderLogic.go +++ b/queue/logic/order/activateOrderLogic.go @@ -1356,7 +1356,7 @@ func (l *ActivateOrderLogic) Renewal(ctx context.Context, orderInfo *order.Order return err } - userSub, err := l.getUserSubscription(ctx, orderInfo.SubscribeToken) + userSub, err := l.resolveRenewalActivationSubscription(ctx, orderInfo) if err != nil { return err } @@ -1378,7 +1378,7 @@ func (l *ActivateOrderLogic) Renewal(ctx context.Context, orderInfo *order.Order } // Trigger user group recalculation (needed when renewing an expired subscription) - l.triggerUserGroupRecalculation(ctx, userInfo.Id) + l.triggerUserGroupRecalculation(ctx, userSub.UserId) // Clear user subscription cache err = l.svc.UserModel.ClearSubscribeCache(ctx, userSub) @@ -1386,7 +1386,7 @@ func (l *ActivateOrderLogic) Renewal(ctx context.Context, orderInfo *order.Order logger.WithContext(ctx).Error("Clear user subscribe cache failed", logger.Field("error", err.Error()), logger.Field("subscribe_id", userSub.Id), - logger.Field("user_id", userInfo.Id), + logger.Field("user_id", userSub.UserId), ) } @@ -1408,6 +1408,75 @@ func (l *ActivateOrderLogic) Renewal(ctx context.Context, orderInfo *order.Order return nil } +func (l *ActivateOrderLogic) resolveRenewalActivationSubscription(ctx context.Context, orderInfo *order.Order) (*user.Subscribe, error) { + if orderInfo == nil { + return nil, fmt.Errorf("renewal activation order is nil") + } + userSub, err := l.getUserSubscription(ctx, orderInfo.SubscribeToken) + if err != nil { + return nil, err + } + if orderInfo.UserId <= 0 { + return userSub, nil + } + + expectedUserID := orderInfo.SubscriptionUserId + entitlement, err := commonLogic.ResolveEntitlementUser(ctx, l.svc.DB, orderInfo.UserId) + if err != nil { + return nil, err + } + if entitlement != nil && entitlement.Source == commonLogic.EntitlementSourceFamilyOwner && entitlement.EffectiveUserID > 0 { + expectedUserID = entitlement.EffectiveUserID + } + if expectedUserID <= 0 { + if entitlement != nil && entitlement.EffectiveUserID > 0 { + expectedUserID = entitlement.EffectiveUserID + } else { + expectedUserID = orderInfo.UserId + } + } + if userSub.UserId == expectedUserID { + return userSub, nil + } + + var target user.Subscribe + err = l.svc.DB.WithContext(ctx). + Model(&user.Subscribe{}). + Where("user_id = ? AND subscribe_id = ? AND token != ''", expectedUserID, orderInfo.SubscribeId). + Where("status IN ?", []int64{0, 1, 2, 3}). + Order("expire_time DESC"). + Order("updated_at DESC"). + Order("id DESC"). + First(&target).Error + if err != nil { + logger.WithContext(ctx).Error("Resolve renewal activation target failed", + logger.Field("error", err.Error()), + logger.Field("order_no", orderInfo.OrderNo), + logger.Field("order_user_id", orderInfo.UserId), + logger.Field("expected_user_id", expectedUserID), + logger.Field("token_owner_user_id", userSub.UserId), + logger.Field("token_user_subscribe_id", userSub.Id), + logger.Field("subscribe_id", orderInfo.SubscribeId), + ) + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("renewal activation target mismatch: expected user %d subscription for subscribe %d not found", expectedUserID, orderInfo.SubscribeId) + } + return nil, err + } + + commonLogic.SubscriptionTraceInfo(logger.WithContext(ctx), commonLogic.SubscriptionTraceFlowOrder, "renewal_activation_target_redirected", + "[SubscriptionFlow] renewal activation redirected to entitlement owner subscription", + append(commonLogic.OrderTraceFields(orderInfo), + logger.Field("expected_user_id", expectedUserID), + logger.Field("original_user_subscribe_id", userSub.Id), + logger.Field("original_owner_user_id", userSub.UserId), + logger.Field("resolved_user_subscribe_id", target.Id), + logger.Field("resolved_owner_user_id", target.UserId), + )..., + ) + return &target, nil +} + // getUserSubscription retrieves user subscription by token func (l *ActivateOrderLogic) getUserSubscription(ctx context.Context, token string) (*user.Subscribe, error) { userSub, err := l.svc.UserModel.FindOneSubscribeByToken(ctx, token)