package moduleconfmod import ( "91porn-server/common/log" "fmt" "time" "91porn-server/common/db" "91porn-server/models" "91porn-server/models/commod" "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 const table = models.ModuleConf var moduleMetadataCache = newModuleSnapshotCache(loadModuleMetadata, time.Now, moduleSnapshotTTL, moduleSnapshotRetryDelay) func loadModuleMetadata() (modules []ModuleConf, err error) { err = coll(nil).Find(&modules, bson.M{}) return } // InitIndex 设置index func initIndex() { coll := coll(nil) many := []mongo.IndexModel{ { Keys: bson.D{{Key: "moduleName", Value: 1}, {Key: "subModuleName", Value: 1}}, Options: options.Index().SetUnique(true), }, } if _, err := coll.CreateIndex(many); err != nil { panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err)) } } func coll(t *db.MongoTool) *db.MongoTool { if t == nil { return mdb.Coll(table) } return t.Coll(table) } // InsertOne 插入一条数据 func InsertOne(p *ModuleConf) error { p.HaiJiaoStyle.NormalizeRefreshConfig() if err := p.HaiJiaoStyle.ValidateRefreshConfig(); err != nil { return err } if err := p.ValidateSchedule(); err != nil { return err } p.CreatedAt = time.Now() _, err := coll(nil).InsertOne(p) if err == nil { moduleMetadataCache.invalidate() } return err } // UpdateOne 更新一条数据 func UpdateOne(set EditSelector) error { current, err := getByIDFromDB(set.ID) if err != nil { return err } if set.HaiJiaoStyle != nil { set.HaiJiaoStyle.NormalizeRefreshConfig() if err = set.HaiJiaoStyle.ValidateRefreshConfig(); err != nil { return err } } if set.ClearOnlineAt && set.OnlineAt != nil { return fmt.Errorf("onlineAt and clearOnlineAt cannot be set together") } if set.ClearOfflineAt && set.OfflineAt != nil { return fmt.Errorf("offlineAt and clearOfflineAt cannot be set together") } if set.OnlineAt != nil { current.OnlineAt = set.OnlineAt } if set.OfflineAt != nil { current.OfflineAt = set.OfflineAt } if set.ClearOnlineAt { current.OnlineAt = nil } if set.ClearOfflineAt { current.OfflineAt = nil } if err = current.ValidateSchedule(); err != nil { return err } set.UpdatedAt = time.Now() update := bson.M{"$set": set} unset := bson.M{} if set.ClearOnlineAt { unset["onlineAt"] = "" } if set.ClearOfflineAt { unset["offlineAt"] = "" } if len(unset) > 0 { update["$unset"] = unset } _, err = coll(nil).UpdateOne(bson.M{"_id": set.ID}, update) if err == nil { moduleMetadataCache.invalidate() } return err } func (p ModuleConf) ValidateSchedule() error { if p.OnlineAt != nil && p.OfflineAt != nil && !p.OfflineAt.After(*p.OnlineAt) { return fmt.Errorf("offlineAt must be later than onlineAt") } return nil } func (p ModuleConf) IsActiveAt(now time.Time) bool { if p.Status != 1 || p.DeletedAt != nil { return false } if p.OnlineAt != nil && p.OnlineAt.After(now) { return false } return p.OfflineAt == nil || p.OfflineAt.After(now) } // DeleteOne 删除一条数据 func DeleteOne(id primitive.ObjectID) (err error) { now := time.Now() status := uint8(0) set := EditSelector{ID: id, DeletedAt: &now, Status: &status} return UpdateOne(set) } // GetByID 根据id获取一条记录 func GetByID(id primitive.ObjectID) (conf ModuleConf, err error) { snapshot, err := moduleMetadataCache.get() if err != nil { return ModuleConf{}, err } conf, ok := snapshot.findByID(id) if !ok { return ModuleConf{}, mongo.ErrNoDocuments } return conf, nil } func getByIDFromDB(id primitive.ObjectID) (conf ModuleConf, err error) { err = coll(nil).FindOne(&conf, bson.M{"_id": id}) return } // GetModuleConf 获取一个模块下的所有配置 func GetModuleConf(moduleName string) (ret []ModuleConf, err error) { snapshot, err := moduleMetadataCache.get() if err != nil { return nil, err } return snapshot.findByModuleName(moduleName), nil } // GetModuleConfByType 根据类型获取配置 func GetModuleConfByType(moduleType int) (ret []ModuleConf, err error) { snapshot, err := moduleMetadataCache.get() if err != nil { log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "GetModuleConfByType", table, "snapshot", err)) return nil, err } return snapshot.findByType(moduleType), nil } // GetAllModule 获取所有开启(status=1)的模块配置 func GetAllModule() (ret []ModuleConf, err error) { snapshot, err := moduleMetadataCache.get() if err != nil { return nil, err } return snapshot.enabled(), nil } // GetAllActiveModule 获取当前处于有效展示时间内的亚模块。 func GetAllActiveModule(now time.Time) (ret []ModuleConf, err error) { snapshot, err := moduleMetadataCache.get() if err != nil { return nil, err } return snapshot.activeAt(now), nil } // ExcludedVideoModuleIDs 返回指定场景需要从聚合列表排除的亚模块 ID。 func ExcludedVideoModuleIDs(now time.Time, recommend bool) ([]string, error) { snapshot, err := moduleMetadataCache.get() if err != nil { return nil, err } return snapshot.excludedVideoModuleIDs(now, recommend), nil } // ExcludedSearchModuleIDs 返回配置为不进入搜索列表的亚模块 ID。 func ExcludedSearchModuleIDs() ([]string, error) { snapshot, err := moduleMetadataCache.get() if err != nil { return nil, err } return snapshot.excludedSearchModuleIDs(), nil } // BlockedOutsideSearchModuleIDs 返回当前仅允许通过搜索入口访问的亚模块 ID 集合。 func BlockedOutsideSearchModuleIDs(moduleIDs []string, now time.Time) (map[string]struct{}, error) { objectIDs := make([]primitive.ObjectID, 0, len(moduleIDs)) seen := make(map[primitive.ObjectID]struct{}, len(moduleIDs)) for _, moduleID := range moduleIDs { id, parseErr := primitive.ObjectIDFromHex(moduleID) if parseErr != nil || id.IsZero() { continue } if _, ok := seen[id]; ok { continue } seen[id] = struct{}{} objectIDs = append(objectIDs, id) } if len(objectIDs) == 0 { return map[string]struct{}{}, nil } snapshot, err := moduleMetadataCache.get() if err != nil { return nil, err } return snapshot.blockedOutsideSearchModuleIDs(objectIDs, now), nil } // CanBrowseModule 判断亚模块是否能从搜索以外的普通入口访问。 func CanBrowseModule(moduleID primitive.ObjectID, now time.Time) (bool, error) { module, err := GetByID(moduleID) if err != nil { return false, err } return module.IsActiveAt(now), nil } // Search 根据搜索条件检索 func Search(q QuerySelector, page commod.Page) (resp ListResp, err error) { skip := int64(page.Skip()) limit := int64(page.Limit() + 1) opts := options.FindOptions{ Skip: &skip, Limit: &limit, Sort: bson.D{{Key: "sortNum", Value: 1}}, } filter := bson.M{} if err != nil { return } if q.ModuleName != nil { filter["moduleName"] = primitive.Regex{ Pattern: *q.ModuleName, } } if q.SubModuleName != nil { filter["subModuleName"] = primitive.Regex{ Pattern: *q.SubModuleName, } } if q.Type != nil { filter["type"] = *q.Type } if q.ShowType != nil { filter["showType"] = q.ShowType } data := []ModuleConf{} if err = coll(nil).Find(&data, filter, &opts); err != nil { return } total := int64(0) if total, err = coll(nil).Count(filter); err != nil { return } hasNext := false if uint64(len(data)) > page.Limit() { hasNext = true data = data[:page.Limit()] } resp.List = data resp.Total = total resp.HasNext = hasNext return } // FindByIDs 根据section id批量获取section详情 func FindByIDs(ids []primitive.ObjectID) (list []ModuleConf, err error) { if len(ids) == 0 { return nil, nil } snapshot, err := moduleMetadataCache.get() if err != nil { return nil, err } return snapshot.findByIDs(ids), nil } func FindOneById(id primitive.ObjectID) (ModuleConf, error) { return GetByID(id) } // QueryAllList 分页查询文档 func QueryAllList(filter primitive.M, opts ...*options.FindOptions) (out []*ModuleConf, err error) { if err = coll(nil).Find(&out, filter, opts...); err != nil { log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "QueryAllList", table, "Find", err), log.Any("filter", filter), log.Any("opts", opts), ) return nil, err } return }