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" v10 "91porn-server/common/v10" "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/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() builId := v10.ExtractPureEngChar(ua.BuildID) //版本、广告、公告 versionResp, err := VersionThreeServer(ua.Ver, ua.SysType, builId, ua.BuildID) if err != nil { return } if versionResp.HasNewVersion { versionBody := []*versionmod.VersionBody{ &versionmod.VersionBody{ VersionName: versionResp.ServerVersion, Platform: ua.SysType, Description: versionResp.Description, ForcedUpdate: versionResp.IsForceUpdate, URL: versionResp.DownloadLink[0], IosUrl: versionResp.DownloadLink[0], }} sysInfo.Ver = versionBody } }) 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) } } log.Info("JtAdvertiseThreeServer", log.Any("ads", adsList)) 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) }