Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
+161
View File
@@ -0,0 +1,161 @@
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)))
}