diff --git a/api/system.js b/api/system.js index 2099e07..9d4263a 100644 --- a/api/system.js +++ b/api/system.js @@ -112,7 +112,7 @@ export const createTracking = async (data) => { export const watchAdReward = async (token) => { return request({ - url: "/api/blessing/ad/reward", + url: "/api/ad/reward", method: "POST", data: { rewardToken: token, @@ -122,7 +122,7 @@ export const watchAdReward = async (token) => { export const watchAdStart = async () => { return request({ - url: "/api/blessing/ad/start", + url: "/api/ad/start", method: "POST", data: { adPlacementId: "adunit-d7a28e0357d98947", diff --git a/pages/avatar/download.vue b/pages/avatar/download.vue index e7a7a51..873e786 100644 --- a/pages/avatar/download.vue +++ b/pages/avatar/download.vue @@ -314,10 +314,7 @@ const downloadAvatar = async (item) => { return; } - const canProceed = await checkAbilityAndHandle( - "avatar_download", - rewardAdRef, - ); + const canProceed = await checkAbilityAndHandle("avatar_download"); if (!canProceed) return; uni.showLoading({ title: "下载中..." }); diff --git a/pages/avatar/index.vue b/pages/avatar/index.vue index ee913ff..21a58e2 100644 --- a/pages/avatar/index.vue +++ b/pages/avatar/index.vue @@ -213,13 +213,14 @@ import { onReachBottom, } from "@dcloudio/uni-app"; import { useUserStore } from "@/stores/user"; -import { getShareReward, abilityCheck } from "@/api/system.js"; +import { checkAbilityAndHandle } from "@/utils/ability.js"; import { getAvatarSystemList, getAvatarFrameList, getAvatarDecorList, avatarCreateComplete, } from "@/api/avatar.js"; +import { getShareReward } from "@/api/system.js"; import { saveRecordRequest, getShareToken, @@ -685,29 +686,14 @@ const saveAndUse = async () => { return; } - const abilityRes = await abilityCheck("avatar_download"); - if (!abilityRes.canUse) { - if ( - abilityRes?.blockType === "need_share" && - abilityRes?.message === "分享可继续" - ) { - uni.showToast({ - title: "分享给好友可继续使用", - icon: "none", - }); - return; - } - uni.showToast({ - title: "您今日头像下载次数已用完,明日再试", - icon: "none", - }); - return; - } + const canProceed = await checkAbilityAndHandle("avatar_download"); + if (!canProceed) return; const tempPath = await saveByCanvas(true); const id = createAvatarId(); - saveRecordRequest(tempPath, id, "avatar_download"); + await saveRecordRequest(tempPath, id, "avatar_download"); completeCardInfo(id); + userStore.fetchUserAssets(); return; // 调用avatarDownloadRecord API记录下载次数 // await avatarDownloadRecord({ diff --git a/pages/fortune/index.vue b/pages/fortune/index.vue index 28db860..e6135a4 100644 --- a/pages/fortune/index.vue +++ b/pages/fortune/index.vue @@ -125,19 +125,19 @@ import { onShareAppMessage, onShareTimeline, } from "@dcloudio/uni-app"; -import { abilityCheck } from "@/api/system.js"; -import { drawFortune } from "@/api/fortune.js"; -import { getShareReward } from "@/api/system.js"; -import LoginPopup from "@/components/LoginPopup/LoginPopup.vue"; -import { useUserStore } from "@/stores/user"; import { - getShareToken, - saveRemoteImageToLocal, saveRecordRequest, + saveRemoteImageToLocal, + getShareToken, saveViewRequest, - trackRecord, } from "@/utils/common.js"; +import { drawFortune } from "@/api/fortune.js"; +import { abilityCheck } from "@/api/system.js"; +import { checkAbilityAndHandle } from "@/utils/ability.js"; +import { useUserStore } from "@/stores/user"; +import LoginPopup from "@/components/LoginPopup/LoginPopup.vue"; import NavBar from "@/components/NavBar/NavBar.vue"; +import { trackRecord } from "@/utils/common.js"; const userStore = useUserStore(); const loginPopupRef = ref(null); @@ -238,10 +238,8 @@ const startShake = async () => { return; } - if (remainingCount.value <= 0) { - uni.showToast({ title: "今日次数已用完", icon: "none" }); - return; - } + const canProceed = await checkAbilityAndHandle("fortune_draw"); + if (!canProceed) return; status.value = "shaking"; diff --git a/pages/index/index_old.vue b/pages/index/index_old.vue index 8d2b433..8a4d48b 100644 --- a/pages/index/index_old.vue +++ b/pages/index/index_old.vue @@ -246,12 +246,8 @@ import { ref, onMounted, computed } from "vue"; import { getStatusBarHeight } from "@/utils/system"; import { onShareAppMessage, onShareTimeline, onShow } from "@dcloudio/uni-app"; import { useUserStore } from "@/stores/user"; -import { - getRecommendList, - getRankList, - abilityCheck, - watchAdReward, -} from "@/api/system"; +import { checkAbilityAndHandle } from "@/utils/ability.js"; +import { getRecommendList, getRankList, watchAdReward } from "@/api/system"; import { saveRemoteImageToLocal, saveRecordRequest } from "@/utils/common"; import { getUserSignInfo, userSignIn } from "@/api/user"; import { avatarDownloadRecord } from "@/api/avatar"; @@ -481,40 +477,8 @@ const handleDownload = async (item) => { const type = item.type === "wallpaper" ? "wallpaper_download" : "avatar_download"; - const abilityRes = await abilityCheck(type); - - if (!abilityRes.canUse) { - if ( - abilityRes?.blockType === "need_share" && - abilityRes?.message === "分享可继续" - ) { - uni.showToast({ - title: "分享给好友即可下载", - icon: "none", - }); - return; - } - if ( - abilityRes?.blockType === "need_ad" && - abilityRes?.message === "观看广告可继续" - ) { - uni.showModal({ - title: "积分不足", - content: "观看广告可获得50积分,继续下载", - success: (res) => { - if (res.confirm) { - rewardAdRef.value.show(); - } - }, - }); - return; - } - uni.showToast({ - title: "您今日下载次数已用完,明日再试", - icon: "none", - }); - return; - } + const canProceed = await checkAbilityAndHandle(type); + if (!canProceed) return; uni.showLoading({ title: "下载中..." }); try { diff --git a/pages/make/index.vue b/pages/make/index.vue index e4bcf47..877c1ea 100644 --- a/pages/make/index.vue +++ b/pages/make/index.vue @@ -563,12 +563,8 @@ import { getCardTemplateTitleList, getCardMusicList, } from "@/api/make"; -import { - abilityCheck, - getShareReward, - msgCheckApi, - watchAdReward, -} from "@/api/system"; +import { getShareReward, msgCheckApi, watchAdReward } from "@/api/system"; +import { checkAbilityAndHandle } from "@/utils/ability.js"; import { onShareAppMessage, onShareTimeline, @@ -1505,24 +1501,9 @@ const preview = async () => { loginPopupRef.value.open(); return; } - const abilityRes = await abilityCheck("card_generate"); - if (!abilityRes.canUse) { - if ( - abilityRes?.blockType === "need_share" && - abilityRes?.message === "分享可继续" - ) { - uni.showToast({ - title: "分享给好友可继续使用", - icon: "none", - }); - return; - } - uni.showToast({ - title: "您今日祝福卡下载次数已用完,直接分享给好友或者明日再试", - icon: "none", - }); - return; - } + const canProceed = await checkAbilityAndHandle("card_generate"); + if (!canProceed) return; + const tempPath = await saveByCanvas(true); const id = createCard(); shareOrSave(id); diff --git a/pages/wallpaper/detail.vue b/pages/wallpaper/detail.vue index ac2bc19..da464a8 100644 --- a/pages/wallpaper/detail.vue +++ b/pages/wallpaper/detail.vue @@ -288,10 +288,7 @@ const downloadWallpaper = async () => { return; } - const canProceed = await checkAbilityAndHandle( - "wallpaper_download", - rewardAdRef, - ); + const canProceed = await checkAbilityAndHandle("wallpaper_download"); if (!canProceed) return; uni.showLoading({ title: "下载中..." }); diff --git a/pages/wallpaper/index.vue b/pages/wallpaper/index.vue index 0d6edae..d46a8dc 100644 --- a/pages/wallpaper/index.vue +++ b/pages/wallpaper/index.vue @@ -304,10 +304,7 @@ const downloadWallpaper = async (item) => { return; } - const canProceed = await checkAbilityAndHandle( - "wallpaper_download", - rewardAdRef, - ); + const canProceed = await checkAbilityAndHandle("wallpaper_download"); if (!canProceed) return; uni.showLoading({ title: "下载中..." }); diff --git a/utils/ability.js b/utils/ability.js index d88b189..8be67dc 100644 --- a/utils/ability.js +++ b/utils/ability.js @@ -1,14 +1,14 @@ import { abilityCheck } from "@/api/system"; +import adManager from "@/utils/adManager"; /** * Checks if a user has the ability to perform an action (e.g., download). * Handles common blocking scenarios like "need_share" or "need_ad". * * @param {string} scene - The scene identifier for the ability check (e.g., "wallpaper_download"). - * @param {Object} rewardAdRef - The ref to the RewardAd component (must have a .value.show() method or be the instance itself). * @returns {Promise} - Returns true if the action can proceed, false otherwise. */ -export const checkAbilityAndHandle = async (scene, rewardAdRef) => { +export const checkAbilityAndHandle = async (scene) => { try { const abilityRes = await abilityCheck(scene); if (abilityRes.canUse) { @@ -35,19 +35,7 @@ export const checkAbilityAndHandle = async (scene, rewardAdRef) => { content: "观看广告可获得50积分,继续下载", success: (res) => { if (res.confirm) { - // Check if rewardAdRef is a ref (has .value) or the component instance itself - if ( - rewardAdRef && - rewardAdRef.value && - typeof rewardAdRef.value.show === "function" - ) { - rewardAdRef.value.show(); - } else if (rewardAdRef && typeof rewardAdRef.show === "function") { - rewardAdRef.show(); - } else { - console.error("RewardAd component reference is invalid"); - uni.showToast({ title: "广告加载失败", icon: "none" }); - } + adManager.showVideoAd(); } }, }); diff --git a/utils/adManager.js b/utils/adManager.js new file mode 100644 index 0000000..e38f075 --- /dev/null +++ b/utils/adManager.js @@ -0,0 +1,107 @@ +import { watchAdStart, watchAdReward } from "@/api/system.js"; +import { useUserStore } from "@/stores/user"; + +class AdManager { + constructor() { + this.videoAd = null; + this.rewardToken = ""; + this.isLoaded = false; + this.adUnitId = "adunit-d7a28e0357d98947"; // Default ID from RewardAd.vue + this._init(); + } + + _init() { + if (uni.createRewardedVideoAd) { + this.videoAd = uni.createRewardedVideoAd({ + adUnitId: this.adUnitId, + }); + + this.videoAd.onLoad(() => { + console.log("Ad Manager: Ad Loaded"); + this.isLoaded = true; + }); + + this.videoAd.onError((err) => { + console.error("Ad Manager: Ad Load Error", err); + this.isLoaded = false; + }); + } + } + + /** + * Show the rewarded video ad. + * Handles the full flow: start session -> show ad -> verify completion -> claim reward -> refresh assets. + * @returns {Promise} Resolves with true if reward was claimed, false otherwise. + */ + async showVideoAd() { + if (!this.videoAd) { + uni.showToast({ title: "当前环境不支持广告", icon: "none" }); + return false; + } + + try { + // Step 1: Start Ad Session + const startRes = await watchAdStart(); + if (!startRes || !startRes.rewardToken) { + uni.showToast({ title: "广告启动失败,请稍后再试", icon: "none" }); + return false; + } + this.rewardToken = startRes.rewardToken; + + // Step 2: Show Ad + try { + await this.videoAd.show(); + } catch (e) { + // Retry load and show + await this.videoAd.load(); + await this.videoAd.show(); + } + + // Step 3: Wait for Close + return new Promise((resolve) => { + const onCloseHandler = async (res) => { + this.videoAd.offClose(onCloseHandler); // Clean up listener + + if (res && res.isEnded) { + // Step 4: Claim Reward + await this._claimReward(this.rewardToken); + resolve(true); + } else { + uni.showToast({ title: "观看完整广告才能获取奖励哦", icon: "none" }); + resolve(false); + } + }; + + this.videoAd.onClose(onCloseHandler); + }); + + } catch (e) { + console.error("Ad Manager: Show failed", e); + uni.showToast({ title: "广告加载失败,请稍后再试", icon: "none" }); + return false; + } + } + + async _claimReward(token) { + try { + uni.showLoading({ title: "发放奖励中..." }); + const res = await watchAdReward(token); + if (res) { + uni.showToast({ + title: "获得50积分", + icon: "success", + }); + // Refresh user assets + const userStore = useUserStore(); + await userStore.fetchUserAssets(); + } + } catch (e) { + console.error("Ad Manager: Reward claim failed", e); + uni.showToast({ title: "奖励发放失败", icon: "none" }); + } finally { + uni.hideLoading(); + } + } +} + +export default new AdManager();