Files
huangguo_server/models/v/vipcardexperimentmod/model.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

284 lines
6.9 KiB
Go

package vipcardexperimentmod
import (
"fmt"
"strings"
"time"
"91porn-server/common/db"
"91porn-server/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var mdb *db.MongoDB
func Init() {
mdb = db.Init(models.VIPCardExperiment)
initIndexes()
}
func experimentColl() *db.MongoTool {
return mdb.Coll(models.VIPCardExperiment)
}
func eventColl() *db.MongoTool {
return mdb.Coll(models.VIPCardAnalyticsEvent)
}
func initIndexes() {
if _, err := experimentColl().CreateIndex([]mongo.IndexModel{
{
Keys: bson.D{{Key: "experimentId", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{{Key: "activeSlot", Value: 1}},
Options: options.Index().
SetName("uniq_vip_card_active_slot").
SetUnique(true).
SetPartialFilterExpression(bson.M{"activeSlot": bson.M{"$gt": ""}}),
},
{
Keys: bson.D{
{Key: "status", Value: 1},
{Key: "startAt", Value: 1},
{Key: "endAt", Value: 1},
{Key: "publishedAt", Value: -1},
},
},
}); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", models.VIPCardExperiment, err))
}
if _, err := eventColl().CreateIndex([]mongo.IndexModel{
{
Keys: bson.D{{Key: "eventId", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{
{Key: "experimentId", Value: 1},
{Key: "variant", Value: 1},
{Key: "eventName", Value: 1},
{Key: "occurredAt", Value: 1},
},
},
{
Keys: bson.D{
{Key: "experimentId", Value: 1},
{Key: "productId", Value: 1},
{Key: "eventName", Value: 1},
},
},
}); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", models.VIPCardAnalyticsEvent, err))
}
}
func Publish(experiment *Experiment, operator string) error {
experiment.Normalize()
if err := experiment.Validate(); err != nil {
return err
}
now := time.Now()
experiment.ID = primitive.NewObjectID()
experiment.Status = StatusActive
experiment.PublishedAt = now
experiment.CreatedAt = now
experiment.UpdatedAt = now
experiment.CreatedBy = operator
experiment.DisabledAt = nil
experiment.DisabledBy = ""
experiment.ActiveSlot = activeSlot
return mdb.Trans(func(t *db.MongoTool) error {
coll := t.Coll(models.VIPCardExperiment)
if _, err := coll.UpdateMany(
bson.M{"status": StatusActive},
bson.M{
"$set": bson.M{
"status": StatusDisabled,
"disabledAt": now,
"disabledBy": operator,
"updatedAt": now,
},
"$unset": bson.M{"activeSlot": ""},
},
); err != nil {
return err
}
_, err := coll.InsertOne(experiment)
return err
})
}
func Current(now time.Time) (*Experiment, error) {
filter := bson.M{
"status": StatusActive,
"$and": bson.A{
bson.M{"$or": bson.A{
bson.M{"startAt": bson.M{"$exists": false}},
bson.M{"startAt": nil},
bson.M{"startAt": bson.M{"$lte": now}},
}},
bson.M{"$or": bson.A{
bson.M{"endAt": bson.M{"$exists": false}},
bson.M{"endAt": nil},
bson.M{"endAt": bson.M{"$gt": now}},
}},
},
}
var out Experiment
if err := experimentColl().FindOne(
&out,
filter,
options.FindOne().SetSort(bson.D{{Key: "publishedAt", Value: -1}, {Key: "_id", Value: -1}}),
); err != nil {
return nil, err
}
if out.ID.IsZero() {
return nil, nil
}
return &out, nil
}
func FindByExperimentID(experimentID string) (*Experiment, error) {
var out Experiment
if err := experimentColl().FindOne(&out, bson.M{"experimentId": experimentID}); err != nil {
return nil, err
}
if out.ID.IsZero() {
return nil, nil
}
return &out, nil
}
func Disable(experimentID, operator string) (bool, error) {
now := time.Now()
result, err := experimentColl().UpdateMany(
bson.M{"experimentId": experimentID, "status": StatusActive},
bson.M{
"$set": bson.M{
"status": StatusDisabled,
"disabledAt": now,
"disabledBy": operator,
"updatedAt": now,
},
"$unset": bson.M{"activeSlot": ""},
},
)
if err != nil {
return false, err
}
return result.ModifiedCount > 0, nil
}
func InsertEvent(event *AnalyticsEvent) (bool, error) {
event.EventID = strings.TrimSpace(event.EventID)
event.EventName = strings.ToUpper(strings.TrimSpace(event.EventName))
event.SessionID = strings.TrimSpace(event.SessionID)
event.ExperimentID = strings.TrimSpace(event.ExperimentID)
event.Variant = strings.ToUpper(strings.TrimSpace(event.Variant))
event.ID = primitive.NewObjectID()
event.ReceivedAt = time.Now()
result, err := eventColl().UpsertOne(
bson.M{"eventId": event.EventID},
bson.M{"$setOnInsert": event},
)
if err != nil {
return false, err
}
return result.UpsertedCount > 0, nil
}
func EventStatistics(experimentID string) ([]EventStat, []ProductImpressionStat, error) {
var eventRaw []struct {
ID struct {
Variant string `bson:"variant"`
EventName string `bson:"eventName"`
} `bson:"_id"`
People int `bson:"people"`
Times int `bson:"times"`
}
err := eventColl().Aggregate(&eventRaw, []bson.M{
{"$match": bson.M{"experimentId": experimentID}},
{"$group": bson.M{
"_id": bson.M{
"variant": "$variant",
"eventName": "$eventName",
"uid": "$uid",
},
"userTimes": bson.M{"$sum": 1},
}},
{"$group": bson.M{
"_id": bson.M{
"variant": "$_id.variant",
"eventName": "$_id.eventName",
},
"people": bson.M{"$sum": 1},
"times": bson.M{"$sum": "$userTimes"},
}},
{"$sort": bson.D{{Key: "_id.variant", Value: 1}, {Key: "_id.eventName", Value: 1}}},
})
if err != nil {
return nil, nil, err
}
eventStats := make([]EventStat, 0, len(eventRaw))
for _, item := range eventRaw {
eventStats = append(eventStats, EventStat{
Variant: item.ID.Variant,
EventName: item.ID.EventName,
People: item.People,
Times: item.Times,
})
}
var productRaw []struct {
ID struct {
Variant string `bson:"variant"`
ProductID primitive.ObjectID `bson:"productId"`
} `bson:"_id"`
People int `bson:"people"`
Times int `bson:"times"`
}
err = eventColl().Aggregate(&productRaw, []bson.M{
{"$match": bson.M{
"experimentId": experimentID,
"eventName": EventProductImpression,
}},
{"$group": bson.M{
"_id": bson.M{
"variant": "$variant",
"productId": "$productId",
"uid": "$uid",
},
"userTimes": bson.M{"$sum": 1},
}},
{"$group": bson.M{
"_id": bson.M{
"variant": "$_id.variant",
"productId": "$_id.productId",
},
"people": bson.M{"$sum": 1},
"times": bson.M{"$sum": "$userTimes"},
}},
{"$sort": bson.D{{Key: "_id.variant", Value: 1}, {Key: "times", Value: -1}}},
})
if err != nil {
return nil, nil, err
}
productStats := make([]ProductImpressionStat, 0, len(productRaw))
for _, item := range productRaw {
productStats = append(productStats, ProductImpressionStat{
Variant: item.ID.Variant,
ProductID: item.ID.ProductID,
People: item.People,
Times: item.Times,
})
}
return eventStats, productStats, nil
}