package activityclient import ( "encoding/json" "math/rand" "regexp" "strconv" "strings" "sync" "time" "91porn-server/app/appg" "91porn-server/common" "91porn-server/common/httputil" "91porn-server/common/log" "91porn-server/models/v/usermod" "91porn-server/models/v/walletmod" ) const ( ReplaceDomain = "https://activity.domain.com" ) // DomainItem 活动服域名项(与 activity-public-server 的 /api/app/domains 返回结构一致) type DomainItem struct { Domain string `json:"domain"` // 域名 Remark string `json:"remark"` // 备注 } // domainsResp 活动服域名接口响应 type domainsResp struct { Code int `json:"code"` Data []DomainItem `json:"data"` } var ( cachedDomains []DomainItem domainsMu sync.RWMutex ) // GetActivityDomains 获取缓存的活动服域名列表 func GetActivityDomains() []DomainItem { domainsMu.RLock() defer domainsMu.RUnlock() return cachedDomains } // GetActivityDomain 随机获取一个可用的活动服域名 func GetActivityDomain() string { domainsMu.RLock() defer domainsMu.RUnlock() n := len(cachedDomains) if n > 0 { return cachedDomains[rand.Intn(n)].Domain } return "" } // ReplaceActivityDomain 替换活动服占位域名,并追加授权参数 appId、sign func ReplaceActivityDomain(rawURL string, user *usermod.User, w *walletmod.Wallet) string { if !strings.Contains(rawURL, ReplaceDomain) { return rawURL } domain := GetActivityDomain() if domain == "" { return rawURL } rawURL = strings.ReplaceAll(rawURL, ReplaceDomain, domain) if user == nil { return rawURL } conf := appg.Conf.ActivityServer now := time.Now() rechargeAmount := 0 if w != nil { rechargeAmount = int(w.Consumption) } payload := &SignPayload{ AppId: conf.AppId, UserId: strconv.FormatUint(user.UID, 10), Nickname: user.Name, Avatar: user.Portrait, Ts: now.Unix(), RegisteredAt: user.CreatedAt.Unix(), RechargeAmount: rechargeAmount, } sign, err := EncryptSign(conf.SecretKey, payload) if err != nil { log.Error("活动服-生成签名失败", log.E(err)) return rawURL } if strings.Contains(rawURL, "?") { rawURL += "&inner=1&appId=" + conf.AppId + "&sign=" + sign } else { rawURL += "?inner=1&appId=" + conf.AppId + "&sign=" + sign } return rawURL } var hrefRe = regexp.MustCompile(`href="([^"]*)"`) // ReplaceActivityDomainInHTML 替换富文本 HTML 中所有 href 里的活动服占位域名 func ReplaceActivityDomainInHTML(htmlStr string, user *usermod.User, w *walletmod.Wallet) string { if !strings.Contains(htmlStr, ReplaceDomain) { return htmlStr } return hrefRe.ReplaceAllStringFunc(htmlStr, func(match string) string { url := match[6 : len(match)-1] // 去掉 href=" 前缀和 " 后缀 return `href="` + ReplaceActivityDomain(url, user, w) + `"` }) } // startDomainSync 定时拉取活动服域名(每5分钟) func startDomainSync() { fetchDomains() common.Go(func() { ticker := time.NewTicker(5 * time.Minute) defer ticker.Stop() for range ticker.C { fetchDomains() } }) } // fetchDomains 从活动服拉取可用域名列表 func fetchDomains() { conf := appg.Conf.ActivityServer apiUrl := conf.ApiUrl if apiUrl == "" { return } const path = "/api/app/domains" url := apiUrl + path headers := BuildSvcHeaders(conf.AppId, conf.SecretKey, "GET", path, "", nil) _, body, err := httputil.DefaultClientGetBytes(url, headers) if err != nil { log.Error("活动服-拉取域名列表失败", log.Any("url", url), log.E(err)) return } var resp domainsResp if err := json.Unmarshal(body, &resp); err != nil { log.Error("活动服-解析域名列表失败", log.Any("body", string(body)), log.E(err)) return } if resp.Code != 200 { log.Warn("活动服-域名列表返回非成功状态", log.Any("code", resp.Code)) return } domainsMu.Lock() cachedDomains = resp.Data domainsMu.Unlock() log.Info("活动服-域名列表更新成功", log.Any("count", len(resp.Data))) }