邀请
This commit is contained in:
@@ -96,6 +96,8 @@ func getServers() *service.Group {
|
||||
|
||||
// init service context
|
||||
ctx := svc.NewServiceContext(c)
|
||||
// init system config
|
||||
initialize.StartInitSystemConfig(ctx)
|
||||
services := service.NewServiceGroup()
|
||||
services.Add(internal.NewService(ctx))
|
||||
services.Add(queue.NewService(ctx))
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/perfect-panel/server/internal/config"
|
||||
"github.com/perfect-panel/server/internal/model/order"
|
||||
"github.com/perfect-panel/server/internal/model/subscribe"
|
||||
"github.com/perfect-panel/server/internal/model/user"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/pkg/orm"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
orderLogic "github.com/perfect-panel/server/queue/logic/order"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 1. Setup Configuration
|
||||
c := config.Config{
|
||||
MySQL: orm.Config{
|
||||
Addr: "127.0.0.1:3306",
|
||||
Dbname: "dev_ppanel", // Using dev_ppanel as default, change if needed
|
||||
Username: "root",
|
||||
Password: "rootpassword",
|
||||
Config: "charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai",
|
||||
MaxIdleConns: 10,
|
||||
MaxOpenConns: 10,
|
||||
},
|
||||
Redis: config.RedisConfig{
|
||||
Host: "127.0.0.1:6379",
|
||||
DB: 0,
|
||||
},
|
||||
Invite: config.InviteConfig{
|
||||
GiftDays: 3, // Default gift days
|
||||
},
|
||||
}
|
||||
|
||||
// 2. Connect to Database & Redis
|
||||
db, err := orm.ConnectMysql(orm.Mysql{Config: c.MySQL})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("DB Connection failed: %v", err))
|
||||
}
|
||||
rds := redis.NewClient(&redis.Options{
|
||||
Addr: c.Redis.Host,
|
||||
DB: c.Redis.DB,
|
||||
})
|
||||
|
||||
// 3. Initialize ServiceContext
|
||||
serviceCtx := svc.NewServiceContext(c)
|
||||
serviceCtx.DB = db
|
||||
serviceCtx.Redis = rds
|
||||
// We don't need queue/scheduler for this unit test
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// 4. Run Scenarios
|
||||
fmt.Println("=== Starting Invite Reward Test ===")
|
||||
|
||||
// Scenario 1: Commission 0 (Expect Gift Days)
|
||||
runScenario(ctx, serviceCtx, "Scenario_0_Commission", 0)
|
||||
|
||||
// Scenario 2: Commission 10 (Expect Money)
|
||||
runScenario(ctx, serviceCtx, "Scenario_10_Commission", 10)
|
||||
}
|
||||
|
||||
func runScenario(ctx context.Context, s *svc.ServiceContext, name string, referralPercentage int64) {
|
||||
fmt.Printf("\n--- Running %s (ReferralPercentage: %d%%) ---\n", name, referralPercentage)
|
||||
|
||||
// Update Config
|
||||
s.Config.Invite.ReferralPercentage = referralPercentage
|
||||
|
||||
// Cleanup old data (Partial cleanup since we don't have email to query)
|
||||
// We'll rely on unique ReferCode / UUIDs to avoid collisions but DB might grow.
|
||||
// Actually we should try to clean up.
|
||||
// Since we removed Email from struct, we can't use it to query easily unless we check `auth_methods`.
|
||||
// For this test, let's just create new users.
|
||||
|
||||
// Create Referrer
|
||||
referrer := &user.User{
|
||||
Password: tool.EncodePassWord("123456"),
|
||||
ReferCode: fmt.Sprintf("REF%d", time.Now().UnixNano())[:20],
|
||||
ReferralPercentage: 0, // Use global settings
|
||||
Commission: 0,
|
||||
}
|
||||
// Use DB directly to ensure ID is updated in struct
|
||||
if err := s.DB.Create(referrer).Error; err != nil {
|
||||
fmt.Printf("Create Referrer Failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
// Force active subscription for referrer so they can receive gift time
|
||||
createActiveSubscription(ctx, s, referrer.Id)
|
||||
|
||||
fmt.Printf("Created Referrer: ID=%d, Commission=%d\n", referrer.Id, referrer.Commission)
|
||||
|
||||
// Create User (Invitee)
|
||||
invitee := &user.User{
|
||||
Password: tool.EncodePassWord("123456"),
|
||||
RefererId: referrer.Id,
|
||||
}
|
||||
if err := s.DB.Create(invitee).Error; err != nil {
|
||||
fmt.Printf("Create Invitee Failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
// Force active subscription for invitee to receive gift time
|
||||
_ = createActiveSubscription(ctx, s, invitee.Id)
|
||||
|
||||
fmt.Printf("Created Invitee: ID=%d, RefererID=%d\n", invitee.Id, invitee.RefererId)
|
||||
|
||||
// Create Order
|
||||
orderInfo := &order.Order{
|
||||
OrderNo: tool.GenerateTradeNo(),
|
||||
UserId: invitee.Id,
|
||||
Amount: 10000, // 100.00
|
||||
Price: 10000,
|
||||
FeeAmount: 0,
|
||||
Status: 2, // Paid
|
||||
Type: 1, // Subscribe
|
||||
IsNew: true,
|
||||
SubscribeId: 1, // Assume plan 1 exists
|
||||
Quantity: 1,
|
||||
}
|
||||
// We need a dummy subscribe plan in DB or use existing
|
||||
ensureSubscribePlan(ctx, s, 1)
|
||||
|
||||
// Execute Logic
|
||||
logic := orderLogic.NewActivateOrderLogic(s)
|
||||
|
||||
// We only simulate the commission part logic or NewPurchase
|
||||
// logic.NewPurchase does a lot of things.
|
||||
// Let's call NewPurchase to be realistic, but we need to ensure dependencies exist.
|
||||
// Instead of full NewPurchase which might fail on other things,
|
||||
// let's verify if we can just call handleCommission? No it's private.
|
||||
// So we call NewPurchase.
|
||||
|
||||
err := logic.NewPurchase(ctx, orderInfo)
|
||||
if err != nil {
|
||||
fmt.Printf("NewPurchase failed (expected for mocked env): %v\n", err)
|
||||
// If it failed because of things we don't care (like sending email), check data anyway
|
||||
} else {
|
||||
fmt.Println("NewPurchase executed successfully.")
|
||||
}
|
||||
|
||||
// Wait for async goroutines
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Check Results
|
||||
// 1. Check Referrer Commission
|
||||
refRes, _ := s.UserModel.FindOne(ctx, referrer.Id)
|
||||
fmt.Printf("Result Referrer Commission: %d (Expected: %d)\n", refRes.Commission, int64(float64(orderInfo.Amount)*float64(referralPercentage)/100))
|
||||
|
||||
// 2. Check Gift Days (Check expiration time changes)
|
||||
// We compare with the initial subscription time
|
||||
// But since we just created it, it's simpler to check if 'ExpiryTime' is far in the future or extended.
|
||||
// For 0 commission, we expect gift days.
|
||||
|
||||
refSub, _ := s.UserModel.FindActiveSubscribe(ctx, referrer.Id)
|
||||
invSub, _ := s.UserModel.FindActiveSubscribe(ctx, invitee.Id)
|
||||
// Avoid panic if sub not found
|
||||
if refSub != nil {
|
||||
fmt.Printf("Result Referrer Sub Expire: %v\n", refSub.ExpireTime)
|
||||
} else {
|
||||
fmt.Println("Result Referrer Sub Expire: nil")
|
||||
}
|
||||
if invSub != nil {
|
||||
// NewPurchase renews/creates sub, so it should be valid + duration
|
||||
fmt.Printf("Result Invitee Sub Expire: %v\n", invSub.ExpireTime)
|
||||
} else {
|
||||
fmt.Println("Result Invitee Sub Expire: nil")
|
||||
}
|
||||
}
|
||||
|
||||
func createActiveSubscription(ctx context.Context, s *svc.ServiceContext, userId int64) *user.Subscribe {
|
||||
sub := &user.Subscribe{
|
||||
UserId: userId,
|
||||
Status: 1,
|
||||
ExpireTime: time.Now().Add(30 * 24 * time.Hour), // 30 days initial
|
||||
Token: uuid.New().String(),
|
||||
UUID: uuid.New().String(),
|
||||
}
|
||||
s.UserModel.InsertSubscribe(ctx, sub)
|
||||
return sub
|
||||
}
|
||||
|
||||
func ensureSubscribePlan(ctx context.Context, s *svc.ServiceContext, id int64) {
|
||||
_, err := s.SubscribeModel.FindOne(ctx, id)
|
||||
if err != nil {
|
||||
s.SubscribeModel.Insert(ctx, &subscribe.Subscribe{
|
||||
Id: id,
|
||||
Name: "Test Plan",
|
||||
UnitTime: "Day", // Days
|
||||
UnitPrice: 100,
|
||||
Sell: &[]bool{true}[0],
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user