feat(pickup): 线下提号完成时发放号主成长积分

- 提号完成按号主实际结算金额发放积分,使用提号单维度幂等键防重复
- 增加线下提号积分补发脚本,默认预演,支持指定起始时间和单号
- 补充提号完成与积分幂等回归测试
This commit is contained in:
yml2213
2026-11-08 15:06:47 +08:00
parent d39f45621b
commit 732f34b23e
6 changed files with 348 additions and 0 deletions
@@ -16,6 +16,7 @@ import (
"hfb_sys/backend/internal/model"
"hfb_sys/backend/internal/modules/invitation"
"hfb_sys/backend/internal/modules/notification"
"hfb_sys/backend/internal/modules/rentergrowth"
"hfb_sys/backend/internal/modules/wallet"
"hfb_sys/backend/internal/timeutil"
"hfb_sys/backend/pkg/money"
@@ -307,6 +308,17 @@ func (r *Repository) Complete(ctx context.Context, pickupID uint64, req Complete
}
}
if _, err := rentergrowth.AwardPickupOwnerCompleted(
tx,
pickup.ID,
pickup.PickupNo,
pickup.OwnerID,
pickup.SettleAmountCent,
rentergrowth.SourcePickupCompleted,
); err != nil {
return err
}
// 邀请返利:线下提号不走正常订单结账流程,必须在这里补发号主(卖家)侧返利,
// 否则号主邀请人永远拿不到提号产生的分成。
// 基准取「网站加价」与「线下利润」的较小值,避免按虚高的单一利润口径多算。
@@ -3,6 +3,7 @@ package pickup
import (
"encoding/json"
"errors"
"strconv"
"strings"
"testing"
"time"
@@ -363,6 +364,20 @@ func TestRepositoryCompleteInternalPickupCreditsWallet(t *testing.T) {
t.Fatalf("站内提号应生成钱包流水: %v", err)
}
assertInt64(t, "站内提号钱包入账", ledger.AmountCent, 12000)
var owner model.User
if err := db.First(&owner, listing.OwnerID).Error; err != nil {
t.Fatalf("读取提号号主失败: %v", err)
}
assertInt64(t, "提号号主成长积分", owner.RenterGrowthPoints, 120)
var growthLedger model.RenterGrowthLedger
if err := db.Where("biz_key = ?", "pickup:"+strconv.FormatUint(pickup.ID, 10)+":owner:pickup_completed").
First(&growthLedger).Error; err != nil {
t.Fatalf("提号应生成号主积分流水: %v", err)
}
if growthLedger.Role != "owner" || growthLedger.Source != "pickup_completed" || growthLedger.Points != 120 {
t.Fatalf("提号积分流水 = %#v", growthLedger)
}
}
// 线下提号不走正常订单结账流程,完成时必须同步给号主邀请人发放返利,
@@ -736,6 +751,8 @@ func newPickupTestRepo(t *testing.T) (*Repository, *gorm.DB) {
&model.ProcessEvent{},
&model.Notification{},
&model.ListingStatusEvent{},
&model.SystemConfig{},
&model.RenterGrowthLedger{},
); err != nil {
t.Fatalf("迁移测试表失败: %v", err)
}