@@ -0,0 +1,312 @@
|
||||
package sourcemod
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/cachev2"
|
||||
"91porn-server/common/constant/redisconst"
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/common/log"
|
||||
"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
|
||||
|
||||
const table = models.Source
|
||||
|
||||
const (
|
||||
pingListCacheKey = "source:ping-list:v1"
|
||||
pingListCacheTTL = time.Minute
|
||||
)
|
||||
|
||||
type pingListCache struct {
|
||||
Domains []string `json:"domains"`
|
||||
Sources []*SourceRes `json:"sources"`
|
||||
}
|
||||
|
||||
func coll(t *db.MongoTool) *db.MongoTool {
|
||||
if t == nil {
|
||||
return mdb.Coll(table)
|
||||
}
|
||||
return t.Coll(table)
|
||||
}
|
||||
|
||||
// initIndex 索引设置
|
||||
func initIndex() {
|
||||
many := []mongo.IndexModel{
|
||||
{
|
||||
Keys: bson.D{{Key: "type", Value: 1}},
|
||||
Options: options.Index().SetUnique(true).SetSparse(true),
|
||||
},
|
||||
{
|
||||
Keys: bson.D{{Key: "domain", Value: 1}},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{{Key: "isActive", Value: 1}},
|
||||
},
|
||||
{
|
||||
Keys: bson.D{{Key: "createdAt", Value: -1}},
|
||||
},
|
||||
}
|
||||
if _, err := coll(nil).CreateIndex(many); err != nil {
|
||||
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
|
||||
}
|
||||
}
|
||||
|
||||
// PingList 返回
|
||||
func PingList() (rd []string, rs []*SourceRes) {
|
||||
// 域名配置体积较大且更新频率低,通过共享缓存避免每次启动请求都读取 MongoDB。
|
||||
result := pingListCache{}
|
||||
_, err := cachev2.Classes().
|
||||
CacheTime(pingListCacheTTL).
|
||||
Key(pingListCacheKey).
|
||||
ResBind(&result).
|
||||
Cache(loadPingList)
|
||||
if err != nil {
|
||||
log.Error("PingList cache error", log.E(err))
|
||||
result = loadPingList()
|
||||
}
|
||||
return result.Domains, result.Sources
|
||||
}
|
||||
|
||||
func loadPingList() pingListCache {
|
||||
return buildPingList(List())
|
||||
}
|
||||
|
||||
func buildPingList(data []*SourceRes) pingListCache {
|
||||
if len(data) == 0 {
|
||||
return pingListCache{}
|
||||
}
|
||||
dataLen := len(data)
|
||||
d := make([]string, 0, dataLen)
|
||||
s := make([]*SourceRes, 0, dataLen)
|
||||
for _, v := range data {
|
||||
switch v.Type {
|
||||
case Ping:
|
||||
for _, v := range v.Domain {
|
||||
d = append(d, v.Url)
|
||||
}
|
||||
case Image, Audio, Common, Group, Telegram, Guide, PROXYRULE, FAQ, QRCODE, WELFARE, ACT, PromoteURL, AppStore, BusinessCooperation, ShareURL, PreSaleBgImg:
|
||||
s = append(s, v)
|
||||
case Vid:
|
||||
s = append(s, v)
|
||||
}
|
||||
}
|
||||
d = d[:len(d):len(d)]
|
||||
s = s[:len(s):len(s)]
|
||||
return pingListCache{Domains: d, Sources: s}
|
||||
}
|
||||
|
||||
func clearPingListCache() {
|
||||
if _, err := cachev2.Classes().Delete(pingListCacheKey); err != nil {
|
||||
log.Error("clear PingList cache error", log.E(err))
|
||||
}
|
||||
}
|
||||
|
||||
// GetCdnURL GetCdnURL
|
||||
func GetCdnURL() (rd []Domain) {
|
||||
data := List()
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
var d []Domain
|
||||
for _, v := range data {
|
||||
switch v.Type {
|
||||
case Common, Vid:
|
||||
d = append(d, v.Domain...)
|
||||
}
|
||||
}
|
||||
rd = d
|
||||
return
|
||||
}
|
||||
|
||||
// List 返回
|
||||
func List() (array []*SourceRes) {
|
||||
if err := coll(nil).Find(&array, bson.M{"isActive": true}); err != nil {
|
||||
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "List", table, "Find", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// List 返回
|
||||
func WebList() (array []*SourceRes) {
|
||||
if err := coll(nil).Find(&array, bson.M{}); err != nil {
|
||||
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "WebList", table, "Find", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetCustomerStat 获取客服状态
|
||||
func GetCustomerStat() bool {
|
||||
var s *SourceRes
|
||||
if err := coll(nil).FindOne(&s, bson.M{"type": Customer}); err != nil {
|
||||
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "GetCustomerStat", table, "FindOne", err))
|
||||
return true
|
||||
}
|
||||
if s == nil {
|
||||
return true
|
||||
}
|
||||
return s.IsActive
|
||||
}
|
||||
|
||||
// 随机获取推广页域名
|
||||
func GetRandomPromotionURL() string {
|
||||
if cnt, err := appg.Redis.SCard(redisconst.LandDomainCacheKey); cnt == 0 || err != nil {
|
||||
purls := GetPromoteURLArray()
|
||||
if len(purls) > 0 {
|
||||
_, _ = appg.Redis.SAdd(redisconst.LandDomainCacheKey, purls)
|
||||
}
|
||||
}
|
||||
purl, err := appg.Redis.SRandMember(redisconst.LandDomainCacheKey)
|
||||
if err != nil {
|
||||
return getPromoteURL()
|
||||
}
|
||||
return purl
|
||||
}
|
||||
|
||||
// 获取推广url数组
|
||||
func GetPromoteURLArray() []string {
|
||||
data := GetPromoteURLList()
|
||||
purls := make([]string, 0)
|
||||
if data != nil {
|
||||
purls = make([]string, 0, len(data.Domain))
|
||||
for _, v := range data.Domain {
|
||||
purls = append(purls, v.Url)
|
||||
}
|
||||
}
|
||||
return purls
|
||||
}
|
||||
|
||||
// GetPromoteURLList 返回
|
||||
func GetPromoteURLList() (data *SourceWebRes) {
|
||||
if err := coll(nil).FindOne(&data, bson.M{"type": PromoteURL, "isActive": true}); err != nil {
|
||||
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "GetPromoteURLList", table, "FindOne", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getPromoteURL 返回
|
||||
func getPromoteURL() string {
|
||||
var s *SourceRes
|
||||
if err := coll(nil).FindOne(&s, bson.M{"type": PromoteURL, "isActive": true}); err != nil {
|
||||
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "getPromoteURL", table, "FindOne", err))
|
||||
}
|
||||
if s != nil {
|
||||
return s.Domain[0].Url
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Insert SourceInsert
|
||||
func Insert(s *Source) error {
|
||||
s.UpdatedAt = time.Now()
|
||||
s.CreatedAt = time.Now()
|
||||
if _, err := coll(nil).InsertOne(&s); err != nil {
|
||||
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "Insert", table, "InsertOne", err))
|
||||
return err
|
||||
}
|
||||
clearPingListCache()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update 修改
|
||||
func Update(id primitive.ObjectID, sType SourceType, set *SourceEdit) error {
|
||||
set.UpdatedAt = time.Now()
|
||||
if _, err := coll(nil).UpdateOne(bson.M{"_id": id, "type": sType}, bson.M{"$set": set}); err != nil {
|
||||
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "Update", table, "UpdateOne", err),
|
||||
log.Any("id", id),
|
||||
log.Any("sType", sType),
|
||||
log.Any("set", set),
|
||||
)
|
||||
return err
|
||||
}
|
||||
clearPingListCache()
|
||||
return nil
|
||||
}
|
||||
|
||||
// EditDomain 修改
|
||||
func EditDomain(id primitive.ObjectID, edit *SourceEdit) error {
|
||||
edit.UpdatedAt = time.Now()
|
||||
update, _ := common.ToBsonM(edit)
|
||||
if _, err := coll(nil).UpdateOne(bson.M{"_id": id}, bson.M{"$set": update}); err != nil {
|
||||
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "EditDomain", table, "UpdateOne", err),
|
||||
log.Any("id", id),
|
||||
log.Any("edit", *edit),
|
||||
)
|
||||
return err
|
||||
}
|
||||
clearPingListCache()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove SourceRemove
|
||||
func Remove(ids []primitive.ObjectID) error {
|
||||
if _, err := coll(nil).DeleteMany(bson.M{"_id": bson.M{"$in": ids}}); err != nil {
|
||||
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "Remove", table, "DeleteMany", err),
|
||||
log.Any("ids", ids),
|
||||
)
|
||||
return err
|
||||
}
|
||||
clearPingListCache()
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetWeightCdnURL() string {
|
||||
var cho []common.Choice
|
||||
data, err := appg.Redis.Get(redisconst.CdnCacheKey)
|
||||
//不存在cdn缓存
|
||||
if err != nil || data == nil {
|
||||
var choice []common.Choice
|
||||
cdn := GetCdnURL()
|
||||
for _, v := range cdn {
|
||||
if v.Weight == 0 {
|
||||
continue
|
||||
}
|
||||
choice = append(choice, common.Choice{Weight: int(v.Weight), Item: v.Url})
|
||||
}
|
||||
cbyte, _ := json.Marshal(choice)
|
||||
_ = appg.Redis.Set(redisconst.CdnCacheKey, cbyte, redisconst.CdnCacheExpire)
|
||||
c, err := common.WeightedChoice(choice)
|
||||
if err != nil {
|
||||
if len(cdn) > 0 {
|
||||
return cdn[0].Url
|
||||
}
|
||||
}
|
||||
return c.Item.(string)
|
||||
}
|
||||
_ = json.Unmarshal([]byte(*data), &cho)
|
||||
c, _ := common.WeightedChoice(cho)
|
||||
return c.Item.(string)
|
||||
}
|
||||
|
||||
func GetVideoAndImgCdn() (videoCdn []string, imgCdn []string) {
|
||||
data := List()
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
dataLen := len(data)
|
||||
videoCdn = make([]string, 0, dataLen)
|
||||
imgCdn = make([]string, 0, dataLen)
|
||||
for _, v := range data {
|
||||
if v.Type == Vid {
|
||||
for _, d := range v.Domain {
|
||||
videoCdn = append(videoCdn, d.Url)
|
||||
}
|
||||
}
|
||||
if v.Type == Image {
|
||||
for _, d := range v.Domain {
|
||||
imgCdn = append(imgCdn, d.Url)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package sourcemod
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func TestBuildPingList(t *testing.T) {
|
||||
pingID := primitive.NewObjectID()
|
||||
videoID := primitive.NewObjectID()
|
||||
ignoredID := primitive.NewObjectID()
|
||||
data := []*SourceRes{
|
||||
{ID: pingID, Type: Ping, IsActive: true, Domain: []Domain{{Url: "https://api-1.example"}, {Url: "https://api-2.example"}}},
|
||||
{ID: videoID, Type: Vid, IsActive: true, Domain: []Domain{{Url: "https://video.example"}}},
|
||||
{ID: ignoredID, Type: Customer, IsActive: true, Domain: []Domain{{Url: "https://customer.example"}}},
|
||||
}
|
||||
|
||||
got := buildPingList(data)
|
||||
if len(got.Domains) != 2 || got.Domains[0] != "https://api-1.example" || got.Domains[1] != "https://api-2.example" {
|
||||
t.Fatalf("buildPingList() domains = %#v", got.Domains)
|
||||
}
|
||||
if len(got.Sources) != 1 || got.Sources[0].ID != videoID {
|
||||
t.Fatalf("buildPingList() sources = %#v", got.Sources)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildPingListEmpty(t *testing.T) {
|
||||
got := buildPingList(nil)
|
||||
if len(got.Domains) != 0 || len(got.Sources) != 0 {
|
||||
t.Fatalf("buildPingList(nil) = %#v", got)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package sourcemod
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/db"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type SourceType string
|
||||
|
||||
const (
|
||||
Ping SourceType = "API" //APP 服务域名
|
||||
Common SourceType = "COMMON" //CDN域名 COMMON:视频,图片共用
|
||||
Vid SourceType = "VID" //CDN域名 VID:视频专用
|
||||
Image SourceType = "IMAGE" //CDN域名 IMAGE:图片专用
|
||||
Audio SourceType = "AUDIO" //CDN域名 AUDIO:有声小说专用
|
||||
Group SourceType = "GROUP" //GROUP:官方群号地址(土豆)
|
||||
Telegram SourceType = "TELEGRAM" //官方tg
|
||||
PromoteURL SourceType = "LAND" //LAND: 落地页域名
|
||||
Guide SourceType = "GUIDE" //GUIDE: 导航域名
|
||||
Customer SourceType = "CUST" //CUST:客服
|
||||
Disc SourceType = "DISC" //DISC:商区
|
||||
PROXYRULE SourceType = "PROXYRULE" //PROXYRULE:代理规则
|
||||
FAQ SourceType = "FAQ" //FAQ:问题常见
|
||||
QRCODE SourceType = "QRCODE" //QRCODE:二维码链接
|
||||
WELFARE SourceType = "WELFARE" //WELFARE:福利页面连接
|
||||
ACT SourceType = "ACT" //ACT:活动连接
|
||||
AppStore SourceType = "AppStore" //应用商店地址
|
||||
BusinessCooperation SourceType = "BusinessCooperation" //商务合作
|
||||
ShareURL SourceType = "SHARE" //SHARE 分享页域名
|
||||
PreSaleBgImg SourceType = "preSaleBgImg" //预售卡动态图
|
||||
)
|
||||
|
||||
type Status int64
|
||||
|
||||
const WxBlock Status = 1 //被微信封禁
|
||||
|
||||
// Source 资源域名配置
|
||||
type Source struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
|
||||
Domain []Domain `json:"domain" bson:"domain" binding:"required"`
|
||||
Type SourceType `json:"type" bson:"type"`
|
||||
IsActive bool `json:"isActive" bson:"isActive"`
|
||||
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"`
|
||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
|
||||
}
|
||||
|
||||
// TODO 此处需要后期删除
|
||||
// SourceRes 返回原生版本资源域名配置
|
||||
type SourcePreviousRes struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"_id" swaggerignore:"true"`
|
||||
//域名
|
||||
Domain string `json:"domain" bson:"domain"`
|
||||
//是否激活
|
||||
IsActive bool `json:"isActive" bson:"isActive"`
|
||||
//资源类型 COMMON:视频,图片共用 VID:视频URL IMAGE:图片URL LAND:落地页域名
|
||||
Type SourceType `json:"type" bson:"type" swaggertype:"integer"`
|
||||
}
|
||||
|
||||
// SourceRes 返回资源域名配置
|
||||
type SourceRes struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"_id" swaggerignore:"true"`
|
||||
//域名
|
||||
Domain []Domain `json:"domain" bson:"domain"`
|
||||
//是否激活
|
||||
IsActive bool `json:"isActive" bson:"isActive"`
|
||||
//资源类型 COMMON:视频,图片共用 VID:视频URL IMAGE:图片URL LAND:落地页域名 AppStore:商店地址
|
||||
Type SourceType `json:"type" bson:"type" swaggertype:"integer"`
|
||||
}
|
||||
|
||||
type Domain struct {
|
||||
//权重
|
||||
Weight int64 `json:"weight" bson:"weight,omitempty"`
|
||||
//地址
|
||||
Url string `json:"url" bson:"url"`
|
||||
//备注
|
||||
Desc string `json:"desc" bson:"desc"`
|
||||
//默认正常状态为0 1:表示该url被微信封禁
|
||||
Status int64 `json:"status" bson:"status"`
|
||||
}
|
||||
|
||||
// SourceWebRes 返回资源域名配置
|
||||
type SourceWebRes struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
|
||||
Domain []Domain `json:"domain" bson:"domain"`
|
||||
IsActive bool `json:"isActive" bson:"isActive"`
|
||||
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"`
|
||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
|
||||
}
|
||||
type SourceEdit struct {
|
||||
Domain []Domain `json:"domain" bson:"domain,omitempty"` //网址
|
||||
IsActive bool `json:"isActive" bson:"isActive"`
|
||||
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"`
|
||||
}
|
||||
|
||||
func Init() {
|
||||
mdb = db.Init(table)
|
||||
initIndex()
|
||||
}
|
||||
Reference in New Issue
Block a user