@@ -0,0 +1,167 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user