package invite import ( "context" modellog "github.com/perfect-panel/server/internal/model/log" "github.com/perfect-panel/server/pkg/xerr" "github.com/pkg/errors" "gorm.io/gorm" ) type Benefits struct { OrderCount int64 HasPurchased bool InviterCommission int64 InviterGiftDays int64 InviteeGiftDays int64 } type InviteRelation struct { InviteeId int64 InviterId int64 } type paidOrderRow struct { UserId int64 `gorm:"column:user_id"` OrderNo string `gorm:"column:order_no"` } type systemLogRow struct { ObjectID int64 `gorm:"column:object_id"` Content string `gorm:"column:content"` } func NormalizePage(page, size int) (int, int) { if page < 1 { page = 1 } if size < 1 { size = 10 } if size > 100 { size = 100 } return page, size } func QueryBenefits(ctx context.Context, db *gorm.DB, relations []InviteRelation) (map[int64]Benefits, error) { result := make(map[int64]Benefits, len(relations)) if len(relations) == 0 { return result, nil } inviteeIds := make([]int64, 0, len(relations)) inviteeToInviter := make(map[int64]int64, len(relations)) for _, relation := range relations { inviteeIds = append(inviteeIds, relation.InviteeId) inviteeToInviter[relation.InviteeId] = relation.InviterId result[relation.InviteeId] = Benefits{} } var orderCounts []struct { UserId int64 `gorm:"column:user_id"` Cnt int64 `gorm:"column:cnt"` } if err := db.WithContext(ctx). Table("`order`"). Select("user_id, COUNT(*) as cnt"). Where("user_id IN ? AND status IN ?", inviteeIds, []int{2, 5}). Group("user_id"). Scan(&orderCounts).Error; err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count invitee paid orders failed: %v", err) } for _, row := range orderCounts { benefit := result[row.UserId] benefit.OrderCount = row.Cnt benefit.HasPurchased = row.Cnt > 0 result[row.UserId] = benefit } var paidOrders []paidOrderRow if err := db.WithContext(ctx). Table("`order`"). Select("user_id, order_no"). Where("user_id IN ? AND status IN ?", inviteeIds, []int{2, 5}). Scan(&paidOrders).Error; err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query invitee paid orders failed: %v", err) } if len(paidOrders) == 0 { return result, nil } orderNos := make([]string, 0, len(paidOrders)) orderToInvitee := make(map[string]int64, len(paidOrders)) inviterIds := make([]int64, 0, len(relations)) inviteeAndInviterSet := make(map[int64]struct{}, len(relations)*2) for _, order := range paidOrders { orderNos = append(orderNos, order.OrderNo) orderToInvitee[order.OrderNo] = order.UserId } for _, relation := range relations { inviterIds = append(inviterIds, relation.InviterId) inviteeAndInviterSet[relation.InviteeId] = struct{}{} inviteeAndInviterSet[relation.InviterId] = struct{}{} } inviteeAndInviterIds := make([]int64, 0, len(inviteeAndInviterSet)) for userId := range inviteeAndInviterSet { inviteeAndInviterIds = append(inviteeAndInviterIds, userId) } if err := fillCommissionBenefits(ctx, db, result, orderToInvitee, inviteeToInviter, orderNos, inviterIds); err != nil { return nil, err } if err := fillGiftBenefits(ctx, db, result, orderToInvitee, inviteeToInviter, orderNos, inviteeAndInviterIds); err != nil { return nil, err } return result, nil } func fillCommissionBenefits(ctx context.Context, db *gorm.DB, benefits map[int64]Benefits, orderToInvitee map[string]int64, inviteeToInviter map[int64]int64, orderNos []string, inviterIds []int64) error { var rows []systemLogRow if err := db.WithContext(ctx). Table("system_logs"). Select("object_id, content"). Where("type = ? AND object_id IN ? AND JSON_UNQUOTE(JSON_EXTRACT(content, '$.order_no')) IN ? AND JSON_EXTRACT(content, '$.type') IN ?", modellog.TypeCommission.Uint8(), inviterIds, orderNos, []int{int(modellog.CommissionTypePurchase), int(modellog.CommissionTypeRenewal)}). Scan(&rows).Error; err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query invite commission logs failed: %v", err) } for _, row := range rows { content := modellog.Commission{} if err := content.Unmarshal([]byte(row.Content)); err != nil { continue } inviteeId, ok := orderToInvitee[content.OrderNo] if !ok || inviteeToInviter[inviteeId] != row.ObjectID { continue } benefit := benefits[inviteeId] benefit.InviterCommission += content.Amount benefits[inviteeId] = benefit } return nil } func fillGiftBenefits(ctx context.Context, db *gorm.DB, benefits map[int64]Benefits, orderToInvitee map[string]int64, inviteeToInviter map[int64]int64, orderNos []string, userIds []int64) error { var rows []systemLogRow if err := db.WithContext(ctx). Table("system_logs"). Select("object_id, content"). Where("type = ? AND object_id IN ? AND JSON_UNQUOTE(JSON_EXTRACT(content, '$.order_no')) IN ?", modellog.TypeGift.Uint8(), userIds, orderNos). Scan(&rows).Error; err != nil { return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query invite gift logs failed: %v", err) } for _, row := range rows { content := modellog.Gift{} if err := content.Unmarshal([]byte(row.Content)); err != nil { continue } if content.Type != modellog.GiftTypeIncrease { continue } inviteeId, ok := orderToInvitee[content.OrderNo] if !ok { continue } benefit := benefits[inviteeId] switch row.ObjectID { case inviteeToInviter[inviteeId]: benefit.InviterGiftDays += content.Amount case inviteeId: benefit.InviteeGiftDays += content.Amount default: continue } benefits[inviteeId] = benefit } return nil } func QueryIdentifiers(ctx context.Context, db *gorm.DB, userIds []int64) (map[int64]string, error) { identifiers := make(map[int64]string, len(userIds)) if len(userIds) == 0 { return identifiers, nil } type identifierRow struct { UserId int64 `gorm:"column:user_id"` Identifier string `gorm:"column:identifier"` } var rows []identifierRow if err := db.WithContext(ctx). Table("user_auth_methods uam"). Select("uam.user_id, uam.auth_identifier as identifier"). Joins("JOIN (SELECT user_id, MIN(id) AS id FROM user_auth_methods WHERE user_id IN ? GROUP BY user_id) first_uam ON first_uam.id = uam.id", userIds). Scan(&rows).Error; err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query user identifiers failed: %v", err) } for _, row := range rows { identifiers[row.UserId] = row.Identifier } return identifiers, nil }