├── php-api/ # 改造后的PHP接口层 ├── java-ad-service/ # 若依框架微服务(广告+VIP+分账) ├── uniapp-reader/ # UniApp前端项目 │ ├── pages/ # 各端页面 │ └──
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

adManager.js 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import { ref } from 'vue'
  2. import { useUserStore } from '@/stores/user'
  3. import { useVipStore } from '@/stores/vip'
  4. // 广告平台配置
  5. const AD_PLATFORMS = {
  6. WECHAT: {
  7. reward: (adUnitId) => {
  8. const ad = wx.createRewardedVideoAd({
  9. adUnitId,
  10. multiton: true
  11. })
  12. ad.onError(err => console.error('微信激励广告错误:', err))
  13. return ad
  14. },
  15. feed: (adUnitId) => {
  16. return wx.createBannerAd({
  17. adUnitId,
  18. adIntervals: 30,
  19. style: {
  20. left: 0,
  21. top: 0,
  22. width: 320
  23. }
  24. })
  25. },
  26. interstitial: (adUnitId) => {
  27. return wx.createInterstitialAd({ adUnitId })
  28. }
  29. },
  30. DOUYIN: {
  31. reward: (adUnitId) => {
  32. const ad = tt.createRewardedVideoAd({ adUnitId })
  33. ad.onError(err => console.error('抖音激励广告错误:', err))
  34. return ad
  35. },
  36. feed: (adUnitId) => {
  37. return tt.createBannerAd({
  38. adUnitId,
  39. style: {
  40. left: 0,
  41. top: 0,
  42. width: 300
  43. }
  44. })
  45. },
  46. interstitial: (adUnitId) => {
  47. return tt.createInterstitialAd({ adUnitId })
  48. }
  49. },
  50. H5: {
  51. reward: (adUnitId) => {
  52. console.log('H5激励广告:', adUnitId)
  53. return {
  54. show: () => {
  55. console.log('展示激励广告')
  56. return Promise.resolve()
  57. },
  58. onClose: (callback) => {
  59. // 模拟关闭事件
  60. setTimeout(() => callback({ isEnded: true }), 3000)
  61. }
  62. }
  63. },
  64. feed: (adUnitId) => {
  65. console.log('H5信息流广告:', adUnitId)
  66. return {
  67. show: () => console.log('展示信息流广告'),
  68. destroy: () => {}
  69. }
  70. },
  71. interstitial: (adUnitId) => {
  72. console.log('H5插屏广告:', adUnitId)
  73. return {
  74. show: () => console.log('展示插屏广告')
  75. }
  76. }
  77. }
  78. }
  79. // 广告触发控制器
  80. export function useAdManager() {
  81. const userStore = useUserStore()
  82. const chapterCount = ref(0)
  83. const lastAdShownTime = ref(0)
  84. // 获取当前平台适配器
  85. const getPlatformAdapter = () => {
  86. // #ifdef MP-WEIXIN
  87. return AD_PLATFORMS.WECHAT
  88. // #endif
  89. // #ifdef MP-TOUTIAO
  90. return AD_PLATFORMS.DOUYIN
  91. // #endif
  92. return AD_PLATFORMS.H5
  93. }
  94. // 展示激励广告(每5章触发)
  95. const showRewardAd = async (chapterId) => {
  96. if (userStore.isVIP) return
  97. // 广告冷却时间(至少30秒)
  98. const now = Date.now()
  99. if (now - lastAdShownTime.value < 30000) return
  100. chapterCount.value++
  101. if (chapterCount.value % 5 !== 0) return
  102. const platform = getCurrentPlatform()
  103. const adapter = getPlatformAdapter()
  104. const adUnitId = getAdUnitId(platform, 'reward')
  105. if (!adUnitId) {
  106. console.error('未配置广告单元ID')
  107. return
  108. }
  109. const ad = adapter.reward(adUnitId)
  110. try {
  111. // 监听广告关闭事件
  112. return new Promise((resolve) => {
  113. ad.onClose(res => {
  114. if (res && res.isEnded) {
  115. // 完整观看,记录广告
  116. logAdView(chapterId, 'reward', platform)
  117. resolve(true)
  118. } else {
  119. resolve(false)
  120. }
  121. })
  122. // 展示广告
  123. ad.show().catch(err => {
  124. console.error('广告展示失败:', err)
  125. resolve(false)
  126. })
  127. lastAdShownTime.value = Date.now()
  128. })
  129. } catch (err) {
  130. console.error('广告展示失败:', err)
  131. return false
  132. }
  133. }
  134. // 展示底部信息流广告
  135. const showFeedAd = (adUnitId) => {
  136. if (userStore.isVIP) return null
  137. const platform = getCurrentPlatform()
  138. const adapter = getPlatformAdapter()
  139. if (!adUnitId) {
  140. adUnitId = getAdUnitId(platform, 'feed')
  141. }
  142. if (!adUnitId) {
  143. console.error('未配置信息流广告单元ID')
  144. return null
  145. }
  146. const ad = adapter.feed(adUnitId)
  147. try {
  148. ad.show()
  149. return ad
  150. } catch (err) {
  151. console.error('信息流广告展示失败:', err)
  152. return null
  153. }
  154. }
  155. // 展示插屏广告
  156. const showInterstitialAd = () => {
  157. if (userStore.isVIP) return
  158. // 广告冷却时间(至少60秒)
  159. const now = Date.now()
  160. if (now - lastAdShownTime.value < 60000) return
  161. const platform = getCurrentPlatform()
  162. const adapter = getPlatformAdapter()
  163. const adUnitId = getAdUnitId(platform, 'interstitial')
  164. if (!adUnitId) {
  165. console.error('未配置插屏广告单元ID')
  166. return
  167. }
  168. const ad = adapter.interstitial(adUnitId)
  169. try {
  170. ad.show()
  171. lastAdShownTime.value = Date.now()
  172. return true
  173. } catch (err) {
  174. console.error('插屏广告展示失败:', err)
  175. return false
  176. }
  177. }
  178. // 广告日志记录
  179. const logAdView = (chapterId, adType, platform) => {
  180. uni.request({
  181. url: 'https://api.aiyadianzi.ltd/ad/log',
  182. method: 'POST',
  183. data: {
  184. userId: userStore.userId,
  185. chapterId,
  186. adType,
  187. platform
  188. },
  189. header: {
  190. 'Authorization': `Bearer ${uni.getStorageSync('token')}`
  191. }
  192. })
  193. }
  194. // 获取当前平台
  195. const getCurrentPlatform = () => {
  196. // #ifdef MP-WEIXIN
  197. return 'wechat'
  198. // #endif
  199. // #ifdef MP-TOUTIAO
  200. return 'douyin'
  201. // #endif
  202. return 'h5'
  203. }
  204. // 获取广告单元ID
  205. const getAdUnitId = (platform, adType) => {
  206. const config = {
  207. wechat: {
  208. reward: 'wechat_reward_ad_123456',
  209. feed: 'wechat_feed_ad_654321',
  210. interstitial: 'wechat_interstitial_ad_789012'
  211. },
  212. douyin: {
  213. reward: 'douyin_reward_ad_abcdef',
  214. feed: 'douyin_feed_ad_fedcba',
  215. interstitial: 'douyin_interstitial_ad_123abc'
  216. },
  217. h5: {
  218. reward: 'h5_reward_ad_123',
  219. feed: 'h5_feed_ad_456',
  220. interstitial: 'h5_interstitial_ad_789'
  221. }
  222. }
  223. return config[platform]?.[adType]
  224. }
  225. return {
  226. showRewardAd,
  227. showFeedAd,
  228. showInterstitialAd
  229. }
  230. }