package vipcardexperimentser import ( "fmt" "strings" "time" "91porn-server/models/v/prdcthsomod" "91porn-server/models/v/productmod" "91porn-server/models/v/rchgordmod" "91porn-server/models/v/vipcardexperimentmod" "go.mongodb.org/mongo-driver/bson/primitive" ) type DisableRequest struct { ExperimentID string `json:"experimentId" binding:"required"` } type StatisticsResponse struct { Experiment *vipcardexperimentmod.Experiment `json:"experiment"` Events []vipcardexperimentmod.EventStat `json:"events"` ProductImpressions []vipcardexperimentmod.ProductImpressionStat `json:"productImpressions"` Orders []rchgordmod.VIPExperimentOrderStat `json:"orders"` } func Current(now time.Time) (*vipcardexperimentmod.Experiment, error) { return vipcardexperimentmod.Current(now) } func Publish(experiment *vipcardexperimentmod.Experiment, operator string) error { if experiment == nil { return fmt.Errorf("experiment is required") } experiment.Normalize() if err := experiment.Validate(); err != nil { return err } now := time.Now() if err := validatePublicationWindow(experiment, now); err != nil { return err } productIDs := uniqueProductIDs(experiment.VariantA.ProductIDs, experiment.VariantB.ProductIDs) products, err := productmod.FindByProductIDs(productIDs) if err != nil { return err } enabled := make(map[primitive.ObjectID]struct{}, len(products)) for _, product := range products { if product.Status { enabled[product.ID] = struct{}{} } } for _, productID := range productIDs { if _, ok := enabled[productID]; !ok { return fmt.Errorf("product is missing or disabled: %s", productID.Hex()) } } return vipcardexperimentmod.Publish(experiment, operator) } func validatePublicationWindow(experiment *vipcardexperimentmod.Experiment, now time.Time) error { if experiment.StartAt != nil && experiment.StartAt.After(now.Add(time.Minute)) { return fmt.Errorf("startAt cannot be in the future; publish starts the experiment immediately") } if experiment.EndAt != nil && !experiment.EndAt.After(now) { return fmt.Errorf("endAt must be later than the current time") } return nil } func Disable(experimentID, operator string) error { experimentID = strings.TrimSpace(experimentID) if experimentID == "" { return fmt.Errorf("experimentId is required") } updated, err := vipcardexperimentmod.Disable(experimentID, operator) if err != nil { return err } if !updated { return fmt.Errorf("active experiment not found") } return nil } func Statistics(experimentID string) (StatisticsResponse, error) { response := StatisticsResponse{ Events: make([]vipcardexperimentmod.EventStat, 0), ProductImpressions: make([]vipcardexperimentmod.ProductImpressionStat, 0), Orders: make([]rchgordmod.VIPExperimentOrderStat, 0), } experimentID = strings.TrimSpace(experimentID) if experimentID == "" { return response, fmt.Errorf("experimentId is required") } experiment, err := vipcardexperimentmod.FindByExperimentID(experimentID) if err != nil { return response, err } if experiment == nil { return response, fmt.Errorf("experiment not found") } response.Experiment = experiment response.Events, response.ProductImpressions, err = vipcardexperimentmod.EventStatistics(experimentID) if err != nil { return response, err } response.Orders, err = rchgordmod.VIPExperimentOrderStatistics(experimentID) if err != nil { return response, err } goldOrders, err := prdcthsomod.VIPExperimentGoldOrderStatistics(experimentID) if err != nil { return response, err } response.Orders = mergeOrderStatistics(response.Orders, goldOrders) return response, nil } func mergeOrderStatistics( cashOrders []rchgordmod.VIPExperimentOrderStat, goldOrders []prdcthsomod.VIPExperimentGoldOrderStat, ) []rchgordmod.VIPExperimentOrderStat { result := append(make([]rchgordmod.VIPExperimentOrderStat, 0, len(cashOrders)+len(goldOrders)), cashOrders...) type orderKey struct { variant string productID primitive.ObjectID } positions := make(map[orderKey]int, len(result)+len(goldOrders)) for i := range result { positions[orderKey{variant: result[i].Variant, productID: result[i].ProductID}] = i } for _, goldOrder := range goldOrders { key := orderKey{variant: goldOrder.Variant, productID: goldOrder.ProductID} position, exists := positions[key] if !exists { position = len(result) positions[key] = position result = append(result, rchgordmod.VIPExperimentOrderStat{ Variant: goldOrder.Variant, ProductID: goldOrder.ProductID, }) } result[position].GoldPaidOrders += goldOrder.GoldPaidOrders result[position].GoldPaidAmount += goldOrder.GoldPaidAmount } return result } func uniqueProductIDs(groups ...[]primitive.ObjectID) []primitive.ObjectID { seen := make(map[primitive.ObjectID]struct{}) result := make([]primitive.ObjectID, 0) for _, ids := range groups { for _, id := range ids { if id.IsZero() { continue } if _, ok := seen[id]; ok { continue } seen[id] = struct{}{} result = append(result, id) } } return result }