Files
huangguo_server/web/service/vipcardexperimentser/service.go
T
rootandClaude Opus 5 8679200f41 Initial commit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 13:57:10 +08:00

168 lines
5.1 KiB
Go

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
}