fix family member renewal target
Some checks failed
Build docker and publish / build (20.15.1) (push) Has been cancelled
Some checks failed
Build docker and publish / build (20.15.1) (push) Has been cancelled
This commit is contained in:
parent
769622f087
commit
59b7056a20
@ -699,6 +699,85 @@ func TestPurchase_NewUserOnly_BindEmailScopeSharesHistory(t *testing.T) {
|
|||||||
assert.Equal(t, int64(0), newOrder.Discount)
|
assert.Equal(t, int64(0), newOrder.Discount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ensureRenewalSubscribeColumns(t *testing.T, db *gorm.DB) {
|
||||||
|
t.Helper()
|
||||||
|
for _, sql := range []string{
|
||||||
|
`ALTER TABLE "user_subscribe" ADD COLUMN node_group_id INTEGER NOT NULL DEFAULT 0`,
|
||||||
|
`ALTER TABLE "user_subscribe" ADD COLUMN group_locked TINYINT NOT NULL DEFAULT 0`,
|
||||||
|
`ALTER TABLE "user_subscribe" ADD COLUMN expired_download INTEGER NOT NULL DEFAULT 0`,
|
||||||
|
`ALTER TABLE "user_subscribe" ADD COLUMN expired_upload INTEGER NOT NULL DEFAULT 0`,
|
||||||
|
} {
|
||||||
|
require.NoError(t, db.Exec(sql).Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertRenewalUserSubscribe(t *testing.T, db *gorm.DB, id, userID, orderID, subscribeID int64, token, uuid string, start, expire time.Time) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, db.Exec(`INSERT INTO "user_subscribe"
|
||||||
|
(id, user_id, order_id, subscribe_id, start_time, expire_time, traffic, download, upload, token, uuid, status, created_at, updated_at)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, ?, ?, 1, ?, ?)`,
|
||||||
|
id,
|
||||||
|
userID,
|
||||||
|
orderID,
|
||||||
|
subscribeID,
|
||||||
|
start.UTC().Format("2006-01-02 15:04:05"),
|
||||||
|
expire.UTC().Format("2006-01-02 15:04:05"),
|
||||||
|
token,
|
||||||
|
uuid,
|
||||||
|
start.UTC().Format("2006-01-02 15:04:05"),
|
||||||
|
time.Now().UTC().Format("2006-01-02 15:04:05"),
|
||||||
|
).Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenewalMemberRequestRedirectsToOwnerSubscribe(t *testing.T) {
|
||||||
|
db := setupNewUserOnlyDB(t)
|
||||||
|
ensureRenewalSubscribeColumns(t, db)
|
||||||
|
rds, mr := setupNewUserOnlyRedis(t)
|
||||||
|
svcCtx := buildNewUserOnlySvcCtx(db, rds, mr)
|
||||||
|
|
||||||
|
const (
|
||||||
|
planID = int64(1)
|
||||||
|
paymentID = int64(2)
|
||||||
|
ownerUserID = int64(29650)
|
||||||
|
memberID = int64(20003)
|
||||||
|
familyID = int64(88001)
|
||||||
|
memberSubID = int64(10013)
|
||||||
|
ownerSubID = int64(14074)
|
||||||
|
)
|
||||||
|
|
||||||
|
insertTestSubscribe(t, db, planID, false)
|
||||||
|
insertTestPayment(t, db, paymentID)
|
||||||
|
member := insertTestUser(t, db, memberID, time.Now().Add(-24*time.Hour))
|
||||||
|
insertTestUser(t, db, ownerUserID, time.Now().Add(-24*time.Hour))
|
||||||
|
insertTestFamily(t, db, familyID, ownerUserID)
|
||||||
|
insertTestFamilyMember(t, db, familyID, ownerUserID, user.FamilyRoleOwner, user.FamilyMemberActive, "owner_init")
|
||||||
|
insertTestFamilyMember(t, db, familyID, memberID, user.FamilyRoleMember, user.FamilyMemberActive, "manual_invite")
|
||||||
|
|
||||||
|
insertRenewalUserSubscribe(t, db, memberSubID, memberID, 7448, planID, "member-token-10013", "member-uuid-10013",
|
||||||
|
time.Date(2026, 4, 23, 19, 6, 40, 0, time.UTC),
|
||||||
|
time.Date(2026, 4, 30, 19, 6, 40, 0, time.UTC),
|
||||||
|
)
|
||||||
|
insertRenewalUserSubscribe(t, db, ownerSubID, ownerUserID, 9999, planID, "owner-token-14074", "owner-uuid-14074",
|
||||||
|
time.Date(2026, 4, 30, 13, 39, 33, 0, time.UTC),
|
||||||
|
time.Date(2026, 5, 30, 16, 0, 0, 0, time.UTC),
|
||||||
|
)
|
||||||
|
|
||||||
|
resp, err := NewRenewalLogic(buildPurchaseCtx(member), svcCtx).Renewal(&types.RenewalOrderRequest{
|
||||||
|
UserSubscribeID: memberSubID,
|
||||||
|
Payment: paymentID,
|
||||||
|
Quantity: 30,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, resp)
|
||||||
|
|
||||||
|
var created modelOrder.Order
|
||||||
|
require.NoError(t, db.Where("order_no = ?", resp.OrderNo).First(&created).Error)
|
||||||
|
assert.Equal(t, memberID, created.UserId)
|
||||||
|
assert.Equal(t, ownerUserID, created.SubscriptionUserId)
|
||||||
|
assert.Equal(t, int64(9999), created.ParentId)
|
||||||
|
assert.Equal(t, "owner-token-14074", created.SubscribeToken)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPreCreateOrder_NewUserOnly_BindEmailScopeUsesEarliestDeviceTime(t *testing.T) {
|
func TestPreCreateOrder_NewUserOnly_BindEmailScopeUsesEarliestDeviceTime(t *testing.T) {
|
||||||
db := setupNewUserOnlyDB(t)
|
db := setupNewUserOnlyDB(t)
|
||||||
rds, mr := setupNewUserOnlyRedis(t)
|
rds, mr := setupNewUserOnlyRedis(t)
|
||||||
|
|||||||
@ -41,6 +41,67 @@ func NewRenewalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RenewalLo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *RenewalLogic) resolveRenewalTargetSubscribe(requested *user.SubscribeDetails, entitlement *commonLogic.EntitlementContext, currentUserID int64) (*user.SubscribeDetails, error) {
|
||||||
|
if requested == nil {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "requested user subscribe is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
effectiveUserID := currentUserID
|
||||||
|
if entitlement != nil && entitlement.EffectiveUserID > 0 {
|
||||||
|
effectiveUserID = entitlement.EffectiveUserID
|
||||||
|
}
|
||||||
|
|
||||||
|
if effectiveUserID == currentUserID {
|
||||||
|
if requested.UserId != currentUserID {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "user subscribe does not belong to current user")
|
||||||
|
}
|
||||||
|
return requested, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if requested.UserId != currentUserID && requested.UserId != effectiveUserID {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "user subscribe does not belong to current family entitlement")
|
||||||
|
}
|
||||||
|
if requested.UserId == effectiveUserID {
|
||||||
|
return requested, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ownerSubscribe, err := l.findOwnerRenewalSubscribe(effectiveUserID, requested.SubscribeId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
commonLogic.SubscriptionTraceInfo(l.Logger, commonLogic.SubscriptionTraceFlowOrder, "renewal_target_resolved",
|
||||||
|
"[SubscriptionFlow] renewal target redirected to family owner subscription",
|
||||||
|
logger.Field("user_id", currentUserID),
|
||||||
|
logger.Field("effective_user_id", effectiveUserID),
|
||||||
|
logger.Field("requested_user_subscribe_id", requested.Id),
|
||||||
|
logger.Field("requested_subscribe_owner_user_id", requested.UserId),
|
||||||
|
logger.Field("resolved_user_subscribe_id", ownerSubscribe.Id),
|
||||||
|
logger.Field("resolved_subscribe_owner_user_id", ownerSubscribe.UserId),
|
||||||
|
logger.Field("subscribe_id", requested.SubscribeId),
|
||||||
|
)
|
||||||
|
return ownerSubscribe, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *RenewalLogic) findOwnerRenewalSubscribe(ownerUserID, subscribeID int64) (*user.SubscribeDetails, error) {
|
||||||
|
var target user.SubscribeDetails
|
||||||
|
err := l.svcCtx.DB.WithContext(l.ctx).
|
||||||
|
Model(&user.Subscribe{}).
|
||||||
|
Preload("Subscribe").
|
||||||
|
Where("user_id = ? AND subscribe_id = ? AND token != ''", ownerUserID, 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 {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "owner subscribe not found for renewal")
|
||||||
|
}
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find owner subscribe error: %v", err.Error())
|
||||||
|
}
|
||||||
|
return &target, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Renewal processes subscription renewal orders including discount calculation,
|
// Renewal processes subscription renewal orders including discount calculation,
|
||||||
// coupon validation, gift amount deduction, fee calculation, and order creation
|
// coupon validation, gift amount deduction, fee calculation, and order creation
|
||||||
func (l *RenewalLogic) Renewal(req *types.RenewalOrderRequest) (resp *types.RenewalOrderResponse, err error) {
|
func (l *RenewalLogic) Renewal(req *types.RenewalOrderRequest) (resp *types.RenewalOrderResponse, err error) {
|
||||||
@ -77,11 +138,15 @@ func (l *RenewalLogic) Renewal(req *types.RenewalOrderRequest) (resp *types.Rene
|
|||||||
}
|
}
|
||||||
|
|
||||||
orderNo := tool.GenerateTradeNo()
|
orderNo := tool.GenerateTradeNo()
|
||||||
// find user subscribe
|
// find requested user subscribe, then resolve it to the real entitlement owner.
|
||||||
userSubscribe, err := l.svcCtx.UserModel.FindOneUserSubscribe(l.ctx, req.UserSubscribeID)
|
requestedSubscribe, err := l.svcCtx.UserModel.FindOneUserSubscribe(l.ctx, req.UserSubscribeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find user subscribe error: %v", err.Error())
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find user subscribe error: %v", err.Error())
|
||||||
}
|
}
|
||||||
|
userSubscribe, err := l.resolveRenewalTargetSubscribe(requestedSubscribe, entitlement, u.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
// find subscription
|
// find subscription
|
||||||
sub, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, userSubscribe.SubscribeId)
|
sub, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, userSubscribe.SubscribeId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -251,7 +316,9 @@ func (l *RenewalLogic) Renewal(req *types.RenewalOrderRequest) (resp *types.Rene
|
|||||||
"[SubscriptionFlow] renewal order persisted",
|
"[SubscriptionFlow] renewal order persisted",
|
||||||
append(commonLogic.OrderTraceFields(&orderInfo),
|
append(commonLogic.OrderTraceFields(&orderInfo),
|
||||||
logger.Field("requested_user_subscribe_id", req.UserSubscribeID),
|
logger.Field("requested_user_subscribe_id", req.UserSubscribeID),
|
||||||
|
logger.Field("requested_subscribe_owner_user_id", requestedSubscribe.UserId),
|
||||||
logger.Field("resolved_user_subscribe_id", userSubscribe.Id),
|
logger.Field("resolved_user_subscribe_id", userSubscribe.Id),
|
||||||
|
logger.Field("resolved_subscribe_owner_user_id", userSubscribe.UserId),
|
||||||
)...,
|
)...,
|
||||||
)
|
)
|
||||||
// Deferred task
|
// Deferred task
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user