feat: 新增多密码验证支持及架构文档
Build docker and publish / build (20.15.1) (push) Has been cancelled

refactor: 重构用户模型和密码验证逻辑
feat(epay): 添加支付类型支持
docs: 添加安装和配置指南文档
fix: 修复优惠券过期检查逻辑
perf: 优化设备解绑缓存清理流程
test: 添加密码验证测试用例
chore: 更新依赖版本
This commit is contained in:
2025-10-27 18:54:07 -07:00
parent fde3210a88
commit 00255a7118
65 changed files with 1523 additions and 504 deletions
+19 -96
View File
@@ -179,8 +179,8 @@ func (l *ActivateOrderLogic) NewPurchase(ctx context.Context, orderInfo *order.O
return err
}
// Handle referral reward in separate goroutine to avoid blocking
go l.handleReferralReward(context.Background(), userInfo, orderInfo)
// Handle commission in separate goroutine to avoid blocking
go l.handleCommission(context.Background(), userInfo, orderInfo)
// Clear cache
l.clearServerCache(ctx, sub)
@@ -192,12 +192,12 @@ func (l *ActivateOrderLogic) NewPurchase(ctx context.Context, orderInfo *order.O
return nil
}
// getUserOrCreate retrieves an existing user or creates a new user based on order details
// getUserOrCreate retrieves an existing user or creates a new guest user based on order details
func (l *ActivateOrderLogic) getUserOrCreate(ctx context.Context, orderInfo *order.Order) (*user.User, error) {
if orderInfo.UserId != 0 {
return l.getExistingUser(ctx, orderInfo.UserId)
}
return l.createUserFromTempOrder(ctx, orderInfo)
return l.createGuestUser(ctx, orderInfo)
}
// getExistingUser retrieves user information by user ID
@@ -213,9 +213,9 @@ func (l *ActivateOrderLogic) getExistingUser(ctx context.Context, userId int64)
return userInfo, nil
}
// createUserFromTempOrder creates a new user account using temporary order information
// stored in Redis cache. All users created this way are formal users, not guests.
func (l *ActivateOrderLogic) createUserFromTempOrder(ctx context.Context, orderInfo *order.Order) (*user.User, error) {
// createGuestUser creates a new user account for guest orders using temporary order information
// stored in Redis cache
func (l *ActivateOrderLogic) createGuestUser(ctx context.Context, orderInfo *order.Order) (*user.User, error) {
tempOrder, err := l.getTempOrderInfo(ctx, orderInfo.OrderNo)
if err != nil {
return nil, err
@@ -223,6 +223,7 @@ func (l *ActivateOrderLogic) createUserFromTempOrder(ctx context.Context, orderI
userInfo := &user.User{
Password: tool.EncodePassWord(tempOrder.Password),
Algo: "default",
AuthMethods: []user.AuthMethods{
{
AuthType: tempOrder.AuthType,
@@ -253,7 +254,7 @@ func (l *ActivateOrderLogic) createUserFromTempOrder(ctx context.Context, orderI
// Handle referrer relationship
l.handleReferrer(ctx, userInfo, tempOrder.InviteCode)
logger.WithContext(ctx).Info("Create user success",
logger.WithContext(ctx).Info("Create guest user success",
logger.Field("user_id", userInfo.Id),
logger.Field("identifier", tempOrder.Identifier),
logger.Field("auth_type", tempOrder.AuthType),
@@ -349,12 +350,10 @@ func (l *ActivateOrderLogic) createUserSubscription(ctx context.Context, orderIn
return userSub, nil
}
// handleReferralReward processes referral rewards for the referrer if applicable.
// handleCommission processes referral commission for the referrer if applicable.
// This runs asynchronously to avoid blocking the main order processing flow.
// If referral percentage > 0: commission reward
// If referral percentage = 0: gift days to both parties
func (l *ActivateOrderLogic) handleReferralReward(ctx context.Context, userInfo *user.User, orderInfo *order.Order) {
if !l.shouldProcessReferralReward(userInfo, orderInfo.IsNew) {
func (l *ActivateOrderLogic) handleCommission(ctx context.Context, userInfo *user.User, orderInfo *order.Order) {
if !l.shouldProcessCommission(userInfo, orderInfo.IsNew) {
return
}
@@ -374,25 +373,13 @@ func (l *ActivateOrderLogic) handleReferralReward(ctx context.Context, userInfo
referralPercentage = uint8(l.svc.Config.Invite.ReferralPercentage)
}
// Check if this is commission reward or gift days reward
if referralPercentage > 0 {
// Commission reward mode
l.processCommissionReward(ctx, referer, orderInfo, referralPercentage)
} else {
// Gift days reward mode
l.processGiftDaysReward(ctx, referer, userInfo, orderInfo)
}
}
// processCommissionReward handles commission-based rewards
func (l *ActivateOrderLogic) processCommissionReward(ctx context.Context, referer *user.User, orderInfo *order.Order, percentage uint8) {
// Order commission calculation (Order Amount - Order Fee) * Referral Percentage
amount := l.calculateCommission(orderInfo.Amount-orderInfo.FeeAmount, percentage)
amount := l.calculateCommission(orderInfo.Amount-orderInfo.FeeAmount, referralPercentage)
// Use transaction for commission updates
err := l.svc.DB.Transaction(func(tx *gorm.DB) error {
err = l.svc.DB.Transaction(func(tx *gorm.DB) error {
referer.Commission += amount
if err := l.svc.UserModel.Update(ctx, referer, tx); err != nil {
if err = l.svc.UserModel.Update(ctx, referer, tx); err != nil {
return err
}
@@ -434,73 +421,9 @@ func (l *ActivateOrderLogic) processCommissionReward(ctx context.Context, refere
}
}
// processGiftDaysReward handles gift days rewards for both parties
func (l *ActivateOrderLogic) processGiftDaysReward(ctx context.Context, referer *user.User, referee *user.User, orderInfo *order.Order) {
giftDays := l.svc.Config.Invite.GiftDays
if giftDays <= 0 {
giftDays = 3 // Default to 3 days
}
// Get the subscription info to determine the unit time
sub, err := l.getSubscribeInfo(ctx, orderInfo.SubscribeId)
if err != nil {
logger.WithContext(ctx).Error("Get subscribe info failed for gift days",
logger.Field("error", err.Error()),
logger.Field("subscribe_id", orderInfo.SubscribeId),
)
return
}
// Grant gift days to both referer and referee
l.grantGiftDays(ctx, referer, giftDays, sub.UnitTime, "referer")
l.grantGiftDays(ctx, referee, giftDays, sub.UnitTime, "referee")
}
// grantGiftDays grants gift days to a user by extending their subscription
func (l *ActivateOrderLogic) grantGiftDays(ctx context.Context, user *user.User, days int64, unitTime string, role string) {
// Find user's active subscription
userSub, err := l.svc.UserModel.FindActiveSubscribe(ctx, user.Id)
if err != nil {
logger.WithContext(ctx).Error("Find user active subscription failed for gift days",
logger.Field("error", err.Error()),
logger.Field("user_id", user.Id),
logger.Field("role", role),
)
return
}
if userSub == nil {
logger.WithContext(ctx).Info("User has no active subscription for gift days",
logger.Field("user_id", user.Id),
logger.Field("role", role),
)
return
}
// Extend subscription by gift days
userSub.ExpireTime = tool.AddTime("day", days, userSub.ExpireTime)
err = l.svc.UserModel.UpdateSubscribe(ctx, userSub)
if err != nil {
logger.WithContext(ctx).Error("Update user subscription for gift days failed",
logger.Field("error", err.Error()),
logger.Field("user_id", user.Id),
logger.Field("role", role),
)
return
}
logger.WithContext(ctx).Info("Gift days granted successfully",
logger.Field("user_id", user.Id),
logger.Field("role", role),
logger.Field("days", days),
logger.Field("new_expire_time", userSub.ExpireTime),
)
}
// shouldProcessReferralReward determines if referral reward should be processed based on
// shouldProcessCommission determines if commission should be processed based on
// referrer existence, commission settings, and order type
func (l *ActivateOrderLogic) shouldProcessReferralReward(userInfo *user.User, isFirstPurchase bool) bool {
func (l *ActivateOrderLogic) shouldProcessCommission(userInfo *user.User, isFirstPurchase bool) bool {
if userInfo == nil || userInfo.RefererId == 0 {
return false
}
@@ -582,8 +505,8 @@ func (l *ActivateOrderLogic) Renewal(ctx context.Context, orderInfo *order.Order
// Clear cache
l.clearServerCache(ctx, sub)
// Handle referral reward
go l.handleReferralReward(context.Background(), userInfo, orderInfo)
// Handle commission
go l.handleCommission(context.Background(), userInfo, orderInfo)
// Send notifications
l.sendNotifications(ctx, orderInfo, userInfo, sub, userSub, telegram.RenewalNotify)