├── php-api/ # 改造后的PHP接口层 ├── java-ad-service/ # 若依框架微服务(广告+VIP+分账) ├── uniapp-reader/ # UniApp前端项目 │ ├── pages/ # 各端页面 │ └──
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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