fix(order): align invite gift ownership
Build docker and publish / build (20.15.1) (push) Has been cancelled
Build docker and publish / build (20.15.1) (push) Has been cancelled
This commit is contained in:
@@ -1090,7 +1090,9 @@ func (l *ActivateOrderLogic) handleCommission(ctx context.Context, userInfo *use
|
||||
// 有佣金路径:邀请人拿佣金,被邀请用户(首单)拿天数
|
||||
if orderInfo.IsNew {
|
||||
giftTarget := l.resolveGiftTargetUser(ctx, userInfo, orderInfo.SubscriptionUserId)
|
||||
_ = l.grantGiftDays(ctx, giftTarget, int(l.svc.Config.Invite.GiftDays), orderInfo.OrderNo, "邀请赠送")
|
||||
if giftErr := l.grantGiftDays(ctx, giftTarget, int(l.svc.Config.Invite.GiftDays), orderInfo.OrderNo, "邀请赠送"); giftErr != nil {
|
||||
l.logGiftDaysError(ctx, giftErr, giftTarget, orderInfo, "commission_path_referee")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1100,7 +1102,9 @@ func (l *ActivateOrderLogic) grantGiftDaysToBothParties(ctx context.Context, ref
|
||||
return
|
||||
}
|
||||
refereeTarget := l.resolveGiftTargetUser(ctx, referee, orderInfo.SubscriptionUserId)
|
||||
_ = l.grantGiftDays(ctx, refereeTarget, int(giftDays), orderInfo.OrderNo, "邀请赠送")
|
||||
if err := l.grantGiftDays(ctx, refereeTarget, int(giftDays), orderInfo.OrderNo, "邀请赠送"); err != nil {
|
||||
l.logGiftDaysError(ctx, err, refereeTarget, orderInfo, "no_commission_referee")
|
||||
}
|
||||
if referee.RefererId == 0 {
|
||||
return
|
||||
}
|
||||
@@ -1109,7 +1113,9 @@ func (l *ActivateOrderLogic) grantGiftDaysToBothParties(ctx context.Context, ref
|
||||
return
|
||||
}
|
||||
refererTarget := l.resolveGiftTargetUser(ctx, referer, 0)
|
||||
_ = l.grantGiftDays(ctx, refererTarget, int(giftDays), orderInfo.OrderNo, "邀请赠送")
|
||||
if err = l.grantGiftDays(ctx, refererTarget, int(giftDays), orderInfo.OrderNo, "邀请赠送"); err != nil {
|
||||
l.logGiftDaysError(ctx, err, refererTarget, orderInfo, "no_commission_referer")
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ActivateOrderLogic) resolveGiftTargetUser(ctx context.Context, source *user.User, forcedOwnerID int64) *user.User {
|
||||
@@ -1136,6 +1142,26 @@ func (l *ActivateOrderLogic) resolveGiftTargetUser(ctx context.Context, source *
|
||||
return target
|
||||
}
|
||||
|
||||
func (l *ActivateOrderLogic) logGiftDaysError(ctx context.Context, err error, target *user.User, orderInfo *order.Order, stage string) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
var targetID int64
|
||||
if target != nil {
|
||||
targetID = target.Id
|
||||
}
|
||||
var orderNo string
|
||||
if orderInfo != nil {
|
||||
orderNo = orderInfo.OrderNo
|
||||
}
|
||||
logger.WithContext(ctx).Error("Grant invite gift days failed",
|
||||
logger.Field("error", err.Error()),
|
||||
logger.Field("stage", stage),
|
||||
logger.Field("target_user_id", targetID),
|
||||
logger.Field("order_no", orderNo),
|
||||
)
|
||||
}
|
||||
|
||||
func (l *ActivateOrderLogic) grantGiftDays(ctx context.Context, u *user.User, days int, orderNo string, remark string) error {
|
||||
if u == nil || days <= 0 {
|
||||
return nil
|
||||
@@ -1179,7 +1205,9 @@ func (l *ActivateOrderLogic) grantGiftDays(ctx context.Context, u *user.User, da
|
||||
}
|
||||
return err
|
||||
}
|
||||
activeSubscribe.ExpireTime = activeSubscribe.ExpireTime.Add(time.Duration(days) * 24 * time.Hour)
|
||||
if !activeSubscribe.ExpireTime.Equal(time.UnixMilli(0)) {
|
||||
activeSubscribe.ExpireTime = activeSubscribe.ExpireTime.Add(time.Duration(days) * 24 * time.Hour)
|
||||
}
|
||||
err = l.svc.UserModel.UpdateSubscribe(ctx, activeSubscribe)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -312,6 +312,134 @@ func TestInviteFlow_OrderThenBind_NoGiftDays(t *testing.T) {
|
||||
assertExpireIncreasedByDays(t, db, refererSub.Id, baseExpire, 0)
|
||||
}
|
||||
|
||||
func TestHandleCommission_GiftDaysToRefereeFamilyOwnerWhenChannelFirstOrder(t *testing.T) {
|
||||
logic, db, cleanup := setupInviteTestLogic(t, config.InviteConfig{
|
||||
ReferralPercentage: 10,
|
||||
OnlyFirstPurchase: false,
|
||||
GiftDays: 2,
|
||||
})
|
||||
defer cleanup()
|
||||
|
||||
refereeOwner := seedUser(t, db, 0, false)
|
||||
referee := seedUser(t, db, 0, false)
|
||||
referer := seedUser(t, db, 0, false)
|
||||
seedFamily(t, db, refereeOwner.Id, referee.Id)
|
||||
referee.RefererId = referer.Id
|
||||
|
||||
ownerBaseExpire := time.Now().Add(96 * time.Hour).Truncate(time.Second)
|
||||
ownerSub := seedActiveSubscribe(t, db, refereeOwner.Id, ownerBaseExpire)
|
||||
|
||||
logic.handleCommission(context.Background(), referee, &order.Order{
|
||||
OrderNo: "ORD-FAMILY-REFEREE-001",
|
||||
Type: OrderTypeSubscribe,
|
||||
IsNew: true,
|
||||
Amount: 100,
|
||||
FeeAmount: 0,
|
||||
SubscriptionUserId: refereeOwner.Id,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
|
||||
assertExpireIncreasedByDays(t, db, ownerSub.Id, ownerBaseExpire, 2)
|
||||
assertUserCommission(t, db, referer.Id, 10)
|
||||
}
|
||||
|
||||
func TestHandleCommission_GiftDaysToRefererFamilyOwnerWhenCommissionDisabled(t *testing.T) {
|
||||
logic, db, cleanup := setupInviteTestLogic(t, config.InviteConfig{
|
||||
ReferralPercentage: 0,
|
||||
OnlyFirstPurchase: false,
|
||||
GiftDays: 2,
|
||||
})
|
||||
defer cleanup()
|
||||
|
||||
referee := seedUser(t, db, 0, false)
|
||||
refererOwner := seedUser(t, db, 0, false)
|
||||
referer := seedUser(t, db, 0, false)
|
||||
seedFamily(t, db, refererOwner.Id, referer.Id)
|
||||
referee.RefererId = referer.Id
|
||||
|
||||
refereeBaseExpire := time.Now().Add(72 * time.Hour).Truncate(time.Second)
|
||||
refererOwnerBaseExpire := time.Now().Add(96 * time.Hour).Truncate(time.Second)
|
||||
refereeSub := seedActiveSubscribe(t, db, referee.Id, refereeBaseExpire)
|
||||
refererOwnerSub := seedActiveSubscribe(t, db, refererOwner.Id, refererOwnerBaseExpire)
|
||||
|
||||
logic.handleCommission(context.Background(), referee, &order.Order{
|
||||
OrderNo: "ORD-FAMILY-REFERER-001",
|
||||
Type: OrderTypeSubscribe,
|
||||
IsNew: true,
|
||||
Amount: 100,
|
||||
FeeAmount: 0,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
|
||||
assertExpireIncreasedByDays(t, db, refereeSub.Id, refereeBaseExpire, 2)
|
||||
assertExpireIncreasedByDays(t, db, refererOwnerSub.Id, refererOwnerBaseExpire, 2)
|
||||
}
|
||||
|
||||
func TestHandleCommission_RefererFamilyMemberCommissionBehaviorUnchanged(t *testing.T) {
|
||||
logic, db, cleanup := setupInviteTestLogic(t, config.InviteConfig{
|
||||
ReferralPercentage: 10,
|
||||
OnlyFirstPurchase: false,
|
||||
GiftDays: 2,
|
||||
})
|
||||
defer cleanup()
|
||||
|
||||
referee := seedUser(t, db, 0, false)
|
||||
refererOwner := seedUser(t, db, 0, false)
|
||||
referer := seedUser(t, db, 0, false)
|
||||
seedFamily(t, db, refererOwner.Id, referer.Id)
|
||||
referee.RefererId = referer.Id
|
||||
|
||||
refereeBaseExpire := time.Now().Add(96 * time.Hour).Truncate(time.Second)
|
||||
refereeSub := seedActiveSubscribe(t, db, referee.Id, refereeBaseExpire)
|
||||
|
||||
logic.handleCommission(context.Background(), referee, &order.Order{
|
||||
OrderNo: "ORD-FAMILY-COMMISSION-001",
|
||||
Type: OrderTypeSubscribe,
|
||||
IsNew: true,
|
||||
Amount: 100,
|
||||
FeeAmount: 0,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
|
||||
assertExpireIncreasedByDays(t, db, refereeSub.Id, refereeBaseExpire, 2)
|
||||
assertUserCommission(t, db, referer.Id, 10)
|
||||
assertUserCommission(t, db, refererOwner.Id, 0)
|
||||
}
|
||||
|
||||
func TestHandleCommission_GiftDaysRecognizesUnlimitedSubscription(t *testing.T) {
|
||||
logic, db, cleanup := setupInviteTestLogic(t, config.InviteConfig{
|
||||
ReferralPercentage: 10,
|
||||
OnlyFirstPurchase: false,
|
||||
GiftDays: 2,
|
||||
})
|
||||
defer cleanup()
|
||||
|
||||
referee := seedUser(t, db, 0, false)
|
||||
referer := seedUser(t, db, 0, false)
|
||||
referee.RefererId = referer.Id
|
||||
|
||||
unlimitedExpire := time.UnixMilli(0)
|
||||
refereeSub := seedActiveSubscribe(t, db, referee.Id, unlimitedExpire)
|
||||
|
||||
logic.handleCommission(context.Background(), referee, &order.Order{
|
||||
OrderNo: "ORD-UNLIMITED-001",
|
||||
Type: OrderTypeSubscribe,
|
||||
IsNew: true,
|
||||
Amount: 100,
|
||||
FeeAmount: 0,
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
|
||||
assertExpireIncreasedByDays(t, db, refereeSub.Id, unlimitedExpire, 0)
|
||||
var giftCount int64
|
||||
if err := db.Model(&modelLog.SystemLog{}).Where("type = ? AND object_id = ?", modelLog.TypeGift.Uint8(), referee.Id).Count(&giftCount).Error; err != nil {
|
||||
t.Fatalf("count gift logs failed: %v", err)
|
||||
}
|
||||
if giftCount != 1 {
|
||||
t.Fatalf("expected 1 gift log for unlimited subscription, got %d", giftCount)
|
||||
}
|
||||
}
|
||||
|
||||
func setupInviteTestLogic(t *testing.T, inviteCfg config.InviteConfig) (*ActivateOrderLogic, *gorm.DB, func()) {
|
||||
t.Helper()
|
||||
|
||||
@@ -336,7 +464,7 @@ func setupInviteTestLogic(t *testing.T, inviteCfg config.InviteConfig) (*Activat
|
||||
t.Fatalf("open test database failed: %v", err)
|
||||
}
|
||||
|
||||
if err := db.AutoMigrate(&user.User{}, &user.Device{}, &user.AuthMethods{}, &user.Subscribe{}, &modelLog.SystemLog{}); err != nil {
|
||||
if err := db.AutoMigrate(&user.User{}, &user.Device{}, &user.AuthMethods{}, &user.Subscribe{}, &user.UserFamily{}, &user.UserFamilyMember{}, &modelLog.SystemLog{}); err != nil {
|
||||
t.Fatalf("auto migrate failed: %v", err)
|
||||
}
|
||||
|
||||
@@ -392,6 +520,40 @@ func seedUser(t *testing.T, db *gorm.DB, referralPercentage uint8, onlyFirstPurc
|
||||
return u
|
||||
}
|
||||
|
||||
func seedFamily(t *testing.T, db *gorm.DB, ownerID int64, memberID int64) {
|
||||
t.Helper()
|
||||
family := &user.UserFamily{
|
||||
OwnerUserId: ownerID,
|
||||
MaxMembers: 3,
|
||||
Status: user.FamilyStatusActive,
|
||||
}
|
||||
if err := db.Create(family).Error; err != nil {
|
||||
t.Fatalf("seed family failed: %v", err)
|
||||
}
|
||||
now := time.Now()
|
||||
members := []user.UserFamilyMember{
|
||||
{
|
||||
FamilyId: family.Id,
|
||||
UserId: ownerID,
|
||||
Role: user.FamilyRoleOwner,
|
||||
Status: user.FamilyMemberActive,
|
||||
JoinSource: "test",
|
||||
JoinedAt: now,
|
||||
},
|
||||
{
|
||||
FamilyId: family.Id,
|
||||
UserId: memberID,
|
||||
Role: user.FamilyRoleMember,
|
||||
Status: user.FamilyMemberActive,
|
||||
JoinSource: "test",
|
||||
JoinedAt: now,
|
||||
},
|
||||
}
|
||||
if err := db.Create(&members).Error; err != nil {
|
||||
t.Fatalf("seed family members failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func seedActiveSubscribe(t *testing.T, db *gorm.DB, userID int64, expireAt time.Time) *user.Subscribe {
|
||||
t.Helper()
|
||||
sub := &user.Subscribe{
|
||||
@@ -423,6 +585,17 @@ func assertExpireIncreasedByDays(t *testing.T, db *gorm.DB, subscribeID int64, b
|
||||
}
|
||||
}
|
||||
|
||||
func assertUserCommission(t *testing.T, db *gorm.DB, userID int64, expected int64) {
|
||||
t.Helper()
|
||||
var u user.User
|
||||
if err := db.First(&u, userID).Error; err != nil {
|
||||
t.Fatalf("query user failed: %v", err)
|
||||
}
|
||||
if u.Commission != expected {
|
||||
t.Fatalf("expected user %d commission=%d, got %d", userID, expected, u.Commission)
|
||||
}
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user