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
+911
View File
@@ -0,0 +1,911 @@
package pingctrl
import (
"91porn-server/app/service/activityclient"
"91porn-server/app/service/adser"
"91porn-server/app/service/advance_ser"
"91porn-server/app/service/ai_mate_ser"
"91porn-server/app/service/messageser"
"91porn-server/app/service/paymentguideser"
"91porn-server/app/service/sys_config"
"91porn-server/common/constant"
"91porn-server/common/services/message"
"91porn-server/common/store"
"91porn-server/models/cache/bannerjumpdata"
"91porn-server/models/cache/sysconfdata"
"91porn-server/models/v/bannerjumpmod"
"91porn-server/models/v/jingangmod"
"91porn-server/models/v/sysconfmod"
"91porn-server/models/v/walletmod"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"sync"
"time"
"91porn-server/app/appg"
"91porn-server/app/middleware/requestEncrypt"
"91porn-server/app/proto"
"91porn-server/app/service/versionser"
"91porn-server/common"
"91porn-server/common/log"
"91porn-server/common/stderr"
"91porn-server/common/version"
"91porn-server/models/commod"
"91porn-server/models/v/sourcemod"
"91porn-server/models/v/systemmod"
"91porn-server/models/v/usermod"
"91porn-server/models/v/versionmod"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// newUserAdFree 判断当前用户是否处于新人免广告期内。
// 当【新人广告开关】(VCodeNewUserAdFreeSwitch) 开启,且用户已登录、注册时间仍在
// 【新人免广告时限】(VCodeNewUserAdFreeHours) 内时返回 true,此时
// /ping/domain 与 /ping/domain/h5 不返回广告信息。
func newUserAdFree(configure sysconfmod.ConfMap, user *usermod.User) bool {
if !configure.GetBool(sysconfmod.VCodeNewUserAdFreeSwitch) {
return false
}
if user == nil {
return false
}
hours := configure.GetInt(sysconfmod.VCodeNewUserAdFreeHours)
if hours <= 0 {
return false
}
return user.CreatedAt.Add(time.Duration(hours) * time.Hour).After(time.Now())
}
func shortDramaEntryPopupEnabled(configure sysconfmod.ConfMap) bool {
if _, exists := configure[string(sysconfmod.VCodeShortDramaEntryPopup)]; !exists {
return true
}
return configure.GetBool(sysconfmod.VCodeShortDramaEntryPopup)
}
const (
defaultEntryPageHome = "home"
defaultEntryPageDrama = "drama"
defaultEntryAudienceNew = "new_user"
defaultEntryAudienceAll = "all_users"
)
func normalizeDefaultEntryPage(page string) string {
switch strings.TrimSpace(page) {
case defaultEntryPageDrama:
return defaultEntryPageDrama
case defaultEntryPageHome:
return defaultEntryPageHome
default:
return defaultEntryPageHome
}
}
// defaultEntryAudienceMatch 判断当前用户是否属于默认入口配置的生效对象。
// 注册未满24小时视为新用户;已经处理过旧版本的老用户升级后也命中一次。
// 历史用户首次接入版本标记时,以最近登录版本兼容判断是否刚升级。
func defaultEntryAudienceMatch(audience, currentVer string, user *usermod.User, now time.Time) bool {
switch strings.TrimSpace(audience) {
case defaultEntryAudienceAll:
return true
case defaultEntryAudienceNew:
if user == nil || currentVer == "" {
return false
}
if user.IsNewUser(now) {
return true
}
if user.DefaultEntryHandledVer == currentVer {
return false
}
if user.DefaultEntryHandledVer != "" {
return true
}
return user.LastVer != "" && user.LastVer != currentVer
default:
return false
}
}
// defaultEntryClaimAccepted 判断版本标记的原子抢占结果是否允许本次进入配置页。
// 注册未满24小时的用户持续命中新用户规则,不受版本标记及其缓存状态影响;
// 超过24小时的升级老用户仍只允许首次抢占成功的请求命中。
func defaultEntryClaimAccepted(audience string, user *usermod.User, now time.Time, claimed bool, claimErr error) bool {
if strings.TrimSpace(audience) != defaultEntryAudienceNew || user.IsNewUser(now) {
return true
}
return claimErr == nil && claimed
}
// resolveDefaultEntryPage 返回客户端本次应直接进入的最终页面。
// defaultEntryAudience 仅作为后台规则保留,客户端无需再次组合判断。
func resolveDefaultEntryPage(configure sysconfmod.ConfMap, user *usermod.User, currentVer string) string {
page := normalizeDefaultEntryPage(configure.GetString(sysconfmod.VCodeDefaultEntryPage))
audience := strings.TrimSpace(configure.GetString(sysconfmod.VCodeDefaultEntryAudience))
now := time.Now()
matched := defaultEntryAudienceMatch(audience, currentVer, user, now)
if user != nil && currentVer != "" && user.DefaultEntryHandledVer != currentVer {
claimed, err := usermod.ClaimDefaultEntryVersion(user.UID, currentVer)
if !defaultEntryClaimAccepted(audience, user, now, claimed, err) {
return defaultEntryPageHome
}
}
if matched {
return page
}
return defaultEntryPageHome
}
// newUserAdFreePosSet 返回【新人免广告-广告位列表】(VCodeNewUserAdFreePositions) 配置的广告位集合。
// 命中新人免广告的用户,集合内的广告位(pos)不返回广告。
func newUserAdFreePosSet(configure sysconfmod.ConfMap) map[int]struct{} {
codes := configure.GetStrSlice(sysconfmod.VCodeNewUserAdFreePositions)
set := make(map[int]struct{}, len(codes))
for _, c := range codes {
pos, err := strconv.Atoi(c)
if err != nil {
log.Error("新人免广告广告位配置错误,必须为数字", log.Any("code", c))
continue
}
set[pos] = struct{}{}
}
return set
}
// DomainList doc
// @Summary 获取资源信息
// @Description 获取域名/广告/版本等信息
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} proto.SysInfo "{"msg": "操作成功"}"
// @Router /api/app/ping/domain [get]
func DomainList(ctx *gin.Context) {
wg := sync.WaitGroup{}
ua, _ := common.GetUA(ctx)
configure, _ := sysconfdata.GetAllFromCache()
uid, _ := common.GetUID(ctx)
var user *usermod.User
var wallet *walletmod.Wallet
if uid > 0 {
user, _ = usermod.FindUserByUID(uid)
if user != nil {
wallet, _ = walletmod.GetWallet(user.UID)
}
}
adFree := newUserAdFree(configure, user)
wg.Add(8)
sysInfo := new(proto.SysInfo)
var ads proto.AdsRes
common.Go(func() {
defer wg.Done()
advSource := proto.AdvanceSource{
PageBackground: configure.GetString(sysconfmod.VCodeAdvancePageBackground),
PageVidBackground: configure.GetString(sysconfmod.VCodeAdvancePageVidBackground),
ButtonBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonBackground),
ButtonWaitBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonWaitBackground),
ButtonProcBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonProcBackground),
EnterBgWait: configure.GetString(sysconfmod.VCodeAdvanceEnterBgWait),
EnterBgProc: configure.GetString(sysconfmod.VCodeAdvanceEnterBgProc),
PopBgWait: configure.GetString(sysconfmod.VCodeAdvancePopBgWait),
PopBgProc: configure.GetString(sysconfmod.VCodeAdvancePopBgProc),
Banner: configure.GetString(sysconfmod.VCodeAdvanceBanner),
BannerWait: configure.GetString(sysconfmod.VCodeAdvanceBannerWait),
BannerProc: configure.GetString(sysconfmod.VCodeAdvanceBannerProc),
}
sysInfo.AdvancePage = advSource
// 获取banner活动
banner := make(map[int]bannerjumpmod.BannerJumpInfo)
bj, err := bannerjumpdata.GetAllFromCache()
if err != nil {
log.Error("获取banner活动发生错误", log.E(err))
return
}
for _, b := range bj {
item, ok := buildBannerJumpInfo(&b, user, wallet)
if !ok {
continue
}
sysInfo.BannerJumpList = append(sysInfo.BannerJumpList, item)
if _, exists := banner[b.Position]; exists {
continue
}
banner[b.Position] = item
}
sysInfo.BannerJump = banner
})
common.Go(func() {
defer wg.Done()
sysInfo.AiBubble = configure.GetStrSlice(sysconfmod.VCodeAiBubble)
sysInfo.AiCharacterImg = configure.GetString(sysconfmod.VCodeAiCharacterImg)
})
common.Go(func() {
defer wg.Done()
sysInfo.Domain, sysInfo.SourceList = sourcemod.PingList()
sysInfo.JGArea, _ = jingangmod.GetJGListValid(nil)
for _, v := range sysInfo.JGArea {
v.LinkUrl = activityclient.ReplaceActivityDomain(v.LinkUrl, user, wallet)
}
})
common.Go(func() {
defer wg.Done()
if user != nil && !user.ID.IsZero() {
sysInfo.SendMsgPrice = messageser.CheckChatPrice(user)
}
sysInfo.AdvanceStatus = advance_ser.GainAdvanceStatus(uid)
})
common.Go(func() {
defer wg.Done()
//版本、广告、公告
verResp, _, annouResp, _, _, _, err := versionser.AdvVersionAnnounThreeServer(ua.Ver, ua.SysType)
if err != nil {
log.Error("VersionThreeServer", log.E(err))
}
for k, v := range annouResp {
v.Href = activityclient.ReplaceActivityDomain(v.Href, user, wallet)
annouResp[k] = v
}
//版本业务
//安卓不做限制;iOS 端版本 <= 1.11.2 不下发版本信息
skipVer := false
if ua.SysType == constant.SysTypeIOS {
if cur, err := version.New(ua.Ver); err == nil && cur.LTE(version.MustNew("1.11.2")) {
skipVer = true
}
}
if len(verResp.DownloadLink) > 0 && !skipVer {
versionBody := []*versionmod.VersionBody{
&versionmod.VersionBody{
VersionName: verResp.ServerVersion,
Platform: ua.SysType,
Description: verResp.Description,
ForcedUpdate: verResp.IsForceUpdate,
URL: verResp.DownloadLink[0],
IosUrl: verResp.DownloadLink[0],
}}
sysInfo.Ver = versionBody
}
//公告
sysInfo.Ads.AnnounList = annouResp
})
common.Go(func() {
defer wg.Done()
jtAds, err := adser.JtAdvertiseThreeServer()
if err != nil {
log.Error("JtAdvertiseThreeServer error occur", log.E(err))
return
}
// 新人免广告:命中开关时,构建需要屏蔽的广告位集合
freePosSet := make(map[int]struct{})
if adFree {
freePosSet = newUserAdFreePosSet(configure)
}
// 默认空列表,避免序列化为 null
adsList := []proto.AdsInfo{}
for _, loc := range jtAds {
pos, err := strconv.Atoi(loc.AdvertiseLocationCode)
if err != nil {
log.Error("广告位置代码错误,必须为数字", log.Any("code", loc.AdvertiseLocationCode))
continue
}
// 100000 以上保留为娱乐广告
if pos > 100000 {
continue
}
// 新人免广告:命中配置的广告位则跳过,不返回该广告位的广告
if _, ok := freePosSet[pos]; ok {
continue
}
for _, ad := range loc.AdDetailInfoList {
extra := ad.GetExtraData()
adsinfo := proto.AdsInfo{
ID: ad.AdvertiseCode,
Title: ad.AdvertiseName,
Cover: ad.GetCoverLsj(),
Href: ad.GetRealLink(user, wallet),
Position: pos,
PositionName: loc.AdvertiseLocationName,
SortCode: ad.Sort,
CoverImgSize: extra.CoverImgSize,
WatchTime: extra.WatchTime,
}
adsList = append(adsList, adsinfo)
}
}
sysInfo.Ads.AdsList = adsList
})
common.Go(func() {
defer wg.Done()
var e error
sysInfo.PaymentStatusPopupConfig, sysInfo.PaymentStatusPopup, e = sys_config.SysConfUserPaymentStatusPopup(user)
if e != nil {
// 分层弹窗配置失败不阻断整个接口,降级为空配置
log.Error("SysConfUserPaymentStatusPopup error", log.E(e))
}
})
common.Go(func() {
defer wg.Done()
var e error
sysInfo.PaymentGuide, e = paymentguideser.GetPingGuide(user)
if e != nil {
// 新版付费引导失败不阻断 Ping,降级为不展示。
log.Error("GetPingGuide error", log.E(e))
}
})
wg.Wait()
item, _ := json.Marshal(ads)
log.Info(fmt.Sprintf("home:%s", string(item)))
sysInfo.SystemConfigList = []*systemmod.Config{}
sysInfo.TotalWatch = sys_config.GetTotalWatchCount()
sysInfo.RandomBanner = appg.Conf.RandomBanner
//sysInfo.Active2023URL = appg.Conf.URL.Active2023 + "?appId=" + strconv.FormatInt(int64(commod.KFK_APPID), 10)
sysInfo.AdsTimeLongVideo = commod.AdsTimeLongVideo
sysInfo.HlH5URL = appg.Conf.URL.HlH5Url
if configure.GetBool(sysconfmod.VCodeLotteryEnable) {
sysInfo.LuckyDrawIcon = configure.GetString(sysconfmod.VCodeLotteryIcon)
sysInfo.LuckyDrawH5 = appg.Conf.URL.LuckyDrawH5
luckyDrawUrl := configure.GetString(sysconfmod.VCodeLotteryUrl)
if luckyDrawUrl != "" {
sysInfo.LuckyDrawH5 = activityclient.ReplaceActivityDomain(luckyDrawUrl, user, wallet)
}
}
sysInfo.AiUndressPrice = configure.GetInt(sysconfmod.VCodeAiUndressPrice)
sysInfo.AiImageToVideoPrice = configure.GetInt(sysconfmod.VCodeAiImageToVideoPrice)
sysInfo.AiTextToImagePrice = configure.GetInt(sysconfmod.VCodeAiTextToImagePrice)
sysInfo.Broadcast = configure.GetBool(sysconfmod.VCodeBroadcast)
sysInfo.StoreIsOpen = configure.GetBool(sysconfmod.VCodeStoreOpen)
sysInfo.BackgroundTheme = constant.ThemeDefault
sysInfo.HotSearchTerms = configure.GetStrSlice(sysconfmod.VCodeHotSearchTerms)
sysInfo.SearchHintWord = configure.GetStrSlice(sysconfmod.VCodeSearchHintWord)
sysInfo.FestivalUi = configure.GetString(sysconfmod.VCodeFestivalUi)
sysInfo.AiGirlFriend = configure.GetBool(sysconfmod.VCodeAiGirlFriend)
sysInfo.AiUndress = configure.GetBool(sysconfmod.VCodeAiUndress)
sysInfo.AiImageChangeFace = configure.GetBool(sysconfmod.VCodeAiImageChangeFace)
sysInfo.AiVideoChangeFace = configure.GetBool(sysconfmod.VCodeAiVideoChangeFace)
sysInfo.AiTextToNovelPrice = configure.GetInt(sysconfmod.VCodeAiTextToNovelPrice)
sysInfo.QmdlUrl = configure.GetString(sysconfmod.VCodeQMDL)
sysInfo.DarkWebVipName = configure.GetString(sysconfmod.VCodeDarkWebVipName)
sysInfo.DarkWebVipId = configure.GetString(sysconfmod.VCodeDarkWebVipId)
sysInfo.RecommendVipIds = configure.GetStrSlice(sysconfmod.VCodeRecommendVipId)
sysInfo.ShortDramaCardID = configure.GetString(sysconfmod.VCodeShortDramaCardID)
sysInfo.ShortDramaEntryPopupEnabled = shortDramaEntryPopupEnabled(configure)
sysInfo.DefaultEntryPage = resolveDefaultEntryPage(configure, user, ua.Ver)
sysInfo.DefaultEntryAudience = configure.GetString(sysconfmod.VCodeDefaultEntryAudience)
sysInfo.NewbieSaleTime = configure.GetInt(sysconfmod.VCodeNewbieSaleTime)
sysInfo.PrivateZoneVipName = configure.GetString(sysconfmod.VCodePrivateZoneVipName)
sysInfo.PrivateZoneVipId = configure.GetString(sysconfmod.VCodePrivateZoneVipId)
sysInfo.ReturnSaleVipIds = configure.GetStrSlice(sysconfmod.VCodeReturnSaleVipIds)
sysInfo.OldReturnSaleTime = configure.GetInt(sysconfmod.VCodeOldReturnSaleTime)
sysInfo.Video1 = configure.GetString(sysconfmod.VCodeVideo1)
sysInfo.Video2 = configure.GetString(sysconfmod.VCodeVideo2)
sysInfo.PersonalCenterBackground = configure.GetString(sysconfmod.VCodePersonalCenterBackground)
sysInfo.AIMateH5 = ai_mate_ser.GetApiUrl()
sysInfo.ReportUrl = appg.Conf.DataReport.AppUrl
sysInfo.FreeMark = configure.GetBool(sysconfmod.VCodeFreeMark)
sysInfo.VipMark = configure.GetBool(sysconfmod.VCodeVipMark)
sysInfo.CoinMark = configure.GetBool(sysconfmod.VCodeCoinMark)
sysInfo.AiSwitchConf = doAiSwitchConf(configure.GetObject(sysconfmod.VCodeAiSort), configure.GetObject(sysconfmod.VCodeAiSwitch))
sysInfo.SignIcon = configure.GetString(sysconfmod.VCodeSignIcon)
sysInfo.DarkWebEnable = configure.GetBool(sysconfmod.VCodeDarkWebEnable)
sysInfo.DarkWebImg = configure.GetString(sysconfmod.VCodeDarkWebImg)
sysInfo.DarkWebIcon = configure.GetString(sysconfmod.VCodeDarkWebIcon)
sysInfo.DarkWebIconName = configure.GetString(sysconfmod.VCodeDarkWebIconName)
common.ServeJSON(ctx, stderr.Success, sysInfo)
}
// Ping doc
// @Summary 域名测试
// @Description 测试域名是否正常
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} proto.GinH{response=string} "response:返回"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ping/check [get]
func Ping(ctx *gin.Context) {
common.ServeJSON(ctx, stderr.Success, gin.H{"response": "pong"})
}
// Ping doc
// @Summary 域名
// @Description 域名
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Param ver query string true "版本号"
// @Param buildId query string true "安装包ID"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /web/pass [get]
func Pass(ctx *gin.Context) {
ver := ctx.Param("ver")
buildId := ctx.Param("buildId")
if ver == "" || buildId == "" {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
pass, err := versionmod.CheckPass(ver, buildId)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, nil)
return
}
ctx.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "msg": "success", "data": gin.H{"pass": pass}})
}
// GetSysDate doc
// @Summary 获取服务器时间
// @Description 获取服务器时间
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /ping/sysDate [get]
func GetSysDate(ctx *gin.Context) {
common.ServeJSON(ctx, http.StatusOK, gin.H{"sysDate": time.Now()})
}
func M(ctx *gin.Context) {
ctx.String(200, "%d", 0)
}
// Domain doc
// @Summary 获取资源信息(web)
// @Description 获取域名/广告/版本等信息(web)
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} proto.SysInfos "{"msg": "操作成功"}"
// @Router /api/app/ping/domain/h5 [get]
func Domain(ctx *gin.Context) {
wg := sync.WaitGroup{}
ua, _ := common.GetUA(ctx)
configure, _ := sysconfdata.GetAllFromCache()
uid, _ := common.GetUID(ctx)
var user *usermod.User
var wallet *walletmod.Wallet
if uid > 0 {
user, _ = usermod.FindUserByUID(uid)
if user != nil {
wallet, _ = walletmod.GetWallet(user.UID)
}
}
adFree := newUserAdFree(configure, user)
wg.Add(8)
sysInfo := new(proto.SysInfos)
common.Go(func() {
defer wg.Done()
advSource := proto.AdvanceSource{
PageBackground: configure.GetString(sysconfmod.VCodeAdvancePageBackground),
PageVidBackground: configure.GetString(sysconfmod.VCodeAdvancePageVidBackground),
ButtonBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonBackground),
ButtonWaitBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonWaitBackground),
ButtonProcBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonProcBackground),
EnterBgWait: configure.GetString(sysconfmod.VCodeAdvanceEnterBgWait),
EnterBgProc: configure.GetString(sysconfmod.VCodeAdvanceEnterBgProc),
PopBgWait: configure.GetString(sysconfmod.VCodeAdvancePopBgWait),
PopBgProc: configure.GetString(sysconfmod.VCodeAdvancePopBgProc),
Banner: configure.GetString(sysconfmod.VCodeAdvanceBanner),
BannerWait: configure.GetString(sysconfmod.VCodeAdvanceBannerWait),
BannerProc: configure.GetString(sysconfmod.VCodeAdvanceBannerProc),
}
sysInfo.AdvancePage = advSource
// 获取banner活动
banner := make(map[int]bannerjumpmod.BannerJumpInfo)
bj, err := bannerjumpdata.GetAllFromCache()
if err != nil {
log.Error("获取banner活动发生错误", log.E(err))
return
}
for _, b := range bj {
item, ok := buildBannerJumpInfo(&b, user, wallet)
if !ok {
continue
}
sysInfo.BannerJumpList = append(sysInfo.BannerJumpList, item)
if _, exists := banner[b.Position]; exists {
continue
}
banner[b.Position] = item
}
sysInfo.BannerJump = banner
})
common.Go(func() {
defer wg.Done()
sysInfo.AiBubble = configure.GetStrSlice(sysconfmod.VCodeAiBubble)
sysInfo.AiCharacterImg = configure.GetString(sysconfmod.VCodeAiCharacterImg)
})
common.Go(func() {
defer wg.Done()
sysInfo.Domain, sysInfo.SourceList = sourcemod.PingList()
sysInfo.JGArea, _ = jingangmod.GetJGListValid(nil)
for _, v := range sysInfo.JGArea {
v.LinkUrl = activityclient.ReplaceActivityDomain(v.LinkUrl, user, wallet)
}
})
common.Go(func() {
defer wg.Done()
if user != nil && !user.ID.IsZero() {
sysInfo.SendMsgPrice = messageser.CheckChatPrice(user)
}
sysInfo.AdvanceStatus = advance_ser.GainAdvanceStatus(uid)
})
common.Go(func() {
defer wg.Done()
//版本、广告、公告
verResp, _, annouResp, iosUrl, androidUrl, shopIosLink, err := versionser.AdvVersionAnnounThreeServer(ua.Ver, "ios")
if err != nil {
log.Error("VersionThreeServer", log.E(err))
return
}
//版本业务
if len(verResp.DownloadLink) > 0 {
versionBody := []*versionmod.VersionBody{
&versionmod.VersionBody{
VersionName: verResp.ServerVersion,
Platform: ua.SysType,
Description: verResp.Description,
ForcedUpdate: verResp.IsForceUpdate,
URL: verResp.DownloadLink[0],
}}
sysInfo.Ver = versionBody
}
for k, v := range annouResp {
v.Href = activityclient.ReplaceActivityDomain(v.Href, user, wallet)
annouResp[k] = v
}
//公告
sysInfo.AnnounList = annouResp
sysInfo.IosLink = iosUrl
sysInfo.AndLink = androidUrl
sysInfo.ShopIosLink = shopIosLink
})
common.Go(func() {
defer wg.Done()
jtAds, err := adser.JtAdvertiseThreeServer()
if err != nil {
log.Error("JtAdvertiseThreeServer error occur", log.E(err))
return
}
// 新人免广告:命中开关时,构建需要屏蔽的广告位集合
freePosSet := make(map[int]struct{})
if adFree {
freePosSet = newUserAdFreePosSet(configure)
}
// 默认空列表,避免序列化为 null
adsList := []proto.AdsInfo{}
for _, loc := range jtAds {
pos, err := strconv.Atoi(loc.AdvertiseLocationCode)
if err != nil {
log.Error("广告位置代码错误,必须为数字", log.Any("code", loc.AdvertiseLocationCode))
continue
}
// 100000 以上保留为娱乐广告
if pos > 100000 {
continue
}
// 新人免广告:命中配置的广告位则跳过,不返回该广告位的广告
if _, ok := freePosSet[pos]; ok {
continue
}
for _, ad := range loc.AdDetailInfoList {
extra := ad.GetExtraData()
adsinfo := proto.AdsInfo{
ID: ad.AdvertiseCode,
Title: ad.AdvertiseName,
Cover: ad.GetCoverLsj(),
Href: ad.GetRealLink(user, wallet),
Position: pos,
PositionName: loc.AdvertiseLocationName,
SortCode: ad.Sort,
CoverImgSize: extra.CoverImgSize,
WatchTime: extra.WatchTime,
}
adsList = append(adsList, adsinfo)
}
}
sysInfo.AdsList = adsList
})
common.Go(func() {
defer wg.Done()
var e error
sysInfo.PaymentStatusPopupConfig, sysInfo.PaymentStatusPopup, e = sys_config.SysConfUserPaymentStatusPopup(user)
if e != nil {
// 分层弹窗配置失败不阻断整个接口,降级为空配置
log.Error("SysConfUserPaymentStatusPopup error", log.E(e))
}
})
common.Go(func() {
defer wg.Done()
var e error
sysInfo.PaymentGuide, e = paymentguideser.GetPingGuide(user)
if e != nil {
// 新版付费引导失败不阻断 Ping,降级为不展示。
log.Error("GetPingGuide error", log.E(e))
}
})
wg.Wait()
sysInfo.SystemConfigList = []*systemmod.Config{}
sysInfo.TotalWatch = sys_config.GetTotalWatchCount()
sysInfo.EKey = requestEncrypt.PubKey
sysInfo.RandomBanner = appg.Conf.RandomBanner
//sysInfo.Active2023URL = appg.Conf.URL.Active2023 + "?appId=" + strconv.FormatInt(int64(commod.KFK_APPID), 10)
sysInfo.AdsTimeLongVideo = commod.AdsTimeLongVideo
sysInfo.HlH5URL = appg.Conf.URL.HlH5Url
if configure.GetBool(sysconfmod.VCodeLotteryEnable) {
sysInfo.LuckyDrawIcon = configure.GetString(sysconfmod.VCodeLotteryIcon)
sysInfo.LuckyDrawH5 = appg.Conf.URL.LuckyDrawH5
luckyDrawUrl := configure.GetString(sysconfmod.VCodeLotteryUrl)
if luckyDrawUrl != "" {
sysInfo.LuckyDrawH5 = activityclient.ReplaceActivityDomain(luckyDrawUrl, user, wallet)
}
}
sysInfo.AiUndressPrice = configure.GetInt(sysconfmod.VCodeAiUndressPrice)
sysInfo.AiImageToVideoPrice = configure.GetInt(sysconfmod.VCodeAiImageToVideoPrice)
sysInfo.AiTextToImagePrice = configure.GetInt(sysconfmod.VCodeAiTextToImagePrice)
sysInfo.Broadcast = configure.GetBool(sysconfmod.VCodeBroadcast)
sysInfo.StoreIsOpen = configure.GetBool(sysconfmod.VCodeStoreOpen)
sysInfo.BackgroundTheme = constant.ThemeDefault
sysInfo.HotSearchTerms = configure.GetStrSlice(sysconfmod.VCodeHotSearchTerms)
sysInfo.SearchHintWord = configure.GetStrSlice(sysconfmod.VCodeSearchHintWord)
sysInfo.FestivalUi = configure.GetString(sysconfmod.VCodeFestivalUi)
sysInfo.AiGirlFriend = configure.GetBool(sysconfmod.VCodeAiGirlFriend)
sysInfo.AiUndress = configure.GetBool(sysconfmod.VCodeAiUndress)
sysInfo.AiImageChangeFace = configure.GetBool(sysconfmod.VCodeAiImageChangeFace)
sysInfo.AiVideoChangeFace = configure.GetBool(sysconfmod.VCodeAiVideoChangeFace)
sysInfo.AiTextToNovelPrice = configure.GetInt(sysconfmod.VCodeAiTextToNovelPrice)
sysInfo.QmdlUrl = configure.GetString(sysconfmod.VCodeQMDL)
sysInfo.DarkWebVipName = configure.GetString(sysconfmod.VCodeDarkWebVipName)
sysInfo.DarkWebVipId = configure.GetString(sysconfmod.VCodeDarkWebVipId)
sysInfo.RecommendVipIds = configure.GetStrSlice(sysconfmod.VCodeRecommendVipId)
sysInfo.ShortDramaCardID = configure.GetString(sysconfmod.VCodeShortDramaCardID)
sysInfo.ShortDramaEntryPopupEnabled = shortDramaEntryPopupEnabled(configure)
sysInfo.DefaultEntryPage = resolveDefaultEntryPage(configure, user, ua.Ver)
sysInfo.DefaultEntryAudience = configure.GetString(sysconfmod.VCodeDefaultEntryAudience)
sysInfo.PrivateZoneVipName = configure.GetString(sysconfmod.VCodePrivateZoneVipName)
sysInfo.PrivateZoneVipId = configure.GetString(sysconfmod.VCodePrivateZoneVipId)
sysInfo.ReturnSaleVipIds = configure.GetStrSlice(sysconfmod.VCodeReturnSaleVipIds)
sysInfo.OldReturnSaleTime = configure.GetInt(sysconfmod.VCodeOldReturnSaleTime)
sysInfo.NewbieSaleTime = configure.GetInt(sysconfmod.VCodeNewbieSaleTime)
sysInfo.AIMateH5 = ai_mate_ser.GetApiUrl()
sysInfo.Video1 = configure.GetString(sysconfmod.VCodeVideo1)
sysInfo.Video2 = configure.GetString(sysconfmod.VCodeVideo2)
sysInfo.PersonalCenterBackground = configure.GetString(sysconfmod.VCodePersonalCenterBackground)
sysInfo.ReportUrl = appg.Conf.DataReport.AppUrl
sysInfo.FreeMark = configure.GetBool(sysconfmod.VCodeFreeMark)
sysInfo.VipMark = configure.GetBool(sysconfmod.VCodeVipMark)
sysInfo.CoinMark = configure.GetBool(sysconfmod.VCodeCoinMark)
sysInfo.AiSwitchConf = doAiSwitchConf(configure.GetObject(sysconfmod.VCodeAiSort), configure.GetObject(sysconfmod.VCodeAiSwitch))
sysInfo.SignIcon = configure.GetString(sysconfmod.VCodeSignIcon)
sysInfo.DarkWebEnable = configure.GetBool(sysconfmod.VCodeDarkWebEnable)
sysInfo.DarkWebImg = configure.GetString(sysconfmod.VCodeDarkWebImg)
sysInfo.DarkWebIcon = configure.GetString(sysconfmod.VCodeDarkWebIcon)
sysInfo.DarkWebIconName = configure.GetString(sysconfmod.VCodeDarkWebIconName)
common.ServeJSON(ctx, stderr.Success, sysInfo)
}
// CheckMessageTip doc
// @Summary 检查消息小红点
// @Description 检查消息小红点
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /ping/checkMessageTip [get]
func CheckMessageTip(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
tip := message.CheckTip(uid)
common.ServeJSON(ctx, http.StatusOK, gin.H{"newsTip": tip})
return
}
func StoreUrl(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil || uid == 0 {
common.ServeJSON(ctx, stderr.Success, nil)
return
}
user, err := usermod.FindUserByUID(uid)
if err != nil {
return
}
if user == nil {
return
}
var balance int64
w, _ := walletmod.GetWallet(uid)
if w != nil {
balance = w.Income + w.Amount
}
shopUrl := store.GetStoreLink(&store.UserData{
AppUid: user.UID,
AppId: int(commod.KFK_APPID),
Name: user.Name,
Portrait: user.Portrait,
ExpireTime: time.Now().Add(time.Hour * 24 * 2).Unix(),
Balance: balance,
})
common.ServeJSON(ctx, stderr.Success, shopUrl)
}
// buildBannerJumpInfo 根据 banner DB 数据构造下发 DTO。
// 倒计时类型按 Url 中的 type 参数推导(与 task/list 保持一致的判断方式)。
// 返回 ok=false 表示该 banner 当前不应下发:
// - Url 含 type=hongbaoRain 但活动服无可用红包雨场次
//
// countdownType=1 时,StartAt/EndAt 用场次起止时间覆盖;否则保留 banner 自身时间。
func buildBannerJumpInfo(b *bannerjumpmod.BannerJump, user *usermod.User, wallet *walletmod.Wallet) (bannerjumpmod.BannerJumpInfo, bool) {
cdStart, cdEnd, cdType, ok := activityclient.ResolveCountdownByLink(b.Url)
if !ok {
return bannerjumpmod.BannerJumpInfo{}, false
}
startAt, endAt := b.StartAt, b.EndAt
if cdType == 1 {
startAt, endAt = cdStart, cdEnd
}
return bannerjumpmod.BannerJumpInfo{
ID: b.ID,
Position: b.Position,
Banner: b.Banner,
Title: b.Title,
Url: activityclient.ReplaceActivityDomain(b.Url, user, wallet),
StartAt: startAt,
EndAt: endAt,
CountdownType: cdType,
}, true
}
// GetBannerJump doc
// @Summary 通过浮窗ID获取浮窗信息
// @Description 按浮窗ID返回单个浮窗的最新信息,等同于 /ping/domain 中对应 banner 的状态。countdownType=1 但当前无可用红包雨场次时返回空数据
// @Tags PING
// @Accept json
// @Produce json
// @Param id path string true "浮窗ID"
// @Success 200 {object} bannerjumpmod.BannerJumpInfo "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "参数错误"}"
// @Router /api/app/ping/banner/{id} [get]
func GetBannerJump(ctx *gin.Context) {
id, err := primitive.ObjectIDFromHex(ctx.Param("id"))
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
uid, _ := common.GetUID(ctx)
var user *usermod.User
var wallet *walletmod.Wallet
if uid != 0 {
user, _ = usermod.FindUserByUID(uid)
wallet, _ = walletmod.GetWallet(uid)
}
bj, err := bannerjumpdata.GetAllFromCache()
if err != nil {
log.Error("获取banner活动发生错误", log.E(err))
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, nil)
return
}
for i := range bj {
if bj[i].ID != id {
continue
}
info, ok := buildBannerJumpInfo(&bj[i], user, wallet)
if !ok {
common.ServeJSON(ctx, stderr.CodeEmptyData, nil)
return
}
common.ServeJSON(ctx, stderr.Success, info)
return
}
common.ServeJSON(ctx, stderr.CodeEmptyData, nil)
}
func doAiSwitchConf(aiSort map[string]string, aiSwitch map[string]string) []proto.AISwitchConf {
list := make([]proto.AISwitchConf, 0)
for i := 0; i < 7; i++ {
conf := proto.AISwitchConf{
Type: i + 1, // 类型从1(脱衣)开始
Sort: i + 7, // 默认排后面
IsOpen: true, // 默认开启状态
}
key := strconv.Itoa(conf.Type)
if _, ok := aiSort[key]; ok {
sortInt, _ := strconv.Atoi(aiSort[key])
conf.Sort = sortInt
}
if _, ok := aiSwitch[key]; ok {
isOpen, _ := strconv.Atoi(aiSwitch[key])
conf.IsOpen = isOpen == 1
}
list = append(list, conf)
}
return list
}
// DomainRefresh doc
// @Summary 按需刷新资源信息
// @Description 按 keys 增量刷新部分资源(当前支持 paymentPopup/paymentGuide)
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Param keys query []string true "刷新项,如 paymentPopup/paymentGuide"
// @Success 200 {object} proto.SysInfoRefresh "{"msg": "操作成功"}"
// @Router /api/app/ping/domain/refresh [get]
func DomainRefresh(ctx *gin.Context) {
var p = &struct {
Keys []string `json:"keys" form:"keys" binding:"required"`
}{}
if err := ctx.ShouldBind(p); err != nil {
log.Error(fmt.Sprintf("DomainRefresh param err:%v", err))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
var user *usermod.User
if uid, err := common.GetUID(ctx); err == nil && uid > 0 {
user, _ = usermod.FindUserByUID(uid)
}
var resp = proto.SysInfoRefresh{}
for _, key := range p.Keys {
switch key {
case "paymentPopup":
if user == nil {
continue
}
paymentStatusPopupConfig, paymentStatusPopup, err := sys_config.SysConfUserPaymentStatusPopup(user)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, "SysConfUserPaymentStatusPopup Error: "+err.Error())
return
}
resp.PaymentPopup = proto.SysInfoRefreshPaymentPopup{
PaymentStatusPopup: paymentStatusPopup,
Homepage: paymentStatusPopupConfig.Homepage,
HomepageFlot: paymentStatusPopupConfig.HomepageFlot,
PlayPage: paymentStatusPopupConfig.PlayPage,
MeTab: paymentStatusPopupConfig.MeTab,
VipCard: paymentStatusPopupConfig.VipCard,
LastDiscountTime: paymentStatusPopupConfig.LastDiscountTime,
}
case "paymentGuide":
paymentGuide, err := paymentguideser.GetPingGuide(user)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, "GetPingGuide Error: "+err.Error())
return
}
resp.PaymentGuide = paymentGuide
default:
log.Error(fmt.Sprintf("DomainRefresh unknown key:%v", key))
}
}
common.ServeJSON(ctx, stderr.Success, resp)
}