From ff2edf1169e2156e4374c337c4e93b07308d6a72 Mon Sep 17 00:00:00 2001 From: 宋庆平 Date: Mon, 1 Nov 2021 14:56:21 +0800 Subject: [PATCH] X --- .gitignore | 36 ++++++++++++++++++++++++++++++++++++ sdk/Analytics.ts | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sdk/SDKTools.ts | 329 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sdk/shareTools.ts | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/WXSDK.ts | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/base/BaseIStorage.ts | 11 +++++++++++ wxsdk/base/SDKBaseData.ts | 3 +++ wxsdk/base/SDKConst.ts | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/base/SDKDefault.ts | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/base/SDKEnum.ts | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/base/SDKEventCenter.ts | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/base/SDKEventEnum.ts | 14 ++++++++++++++ wxsdk/base/wx.d.ts | 3 +++ wxsdk/http/SDKApi.ts | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/http/SDKHttp.ts | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/lib/md5.ts | 378 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/lib/sdk.d.ts | 723 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/platform/Storage.ts | 30 ++++++++++++++++++++++++++++++ wxsdk/platform/wx/WxStorage.ts | 28 ++++++++++++++++++++++++++++ wxsdk/platform/wx/WxStorage.ts.meta | 9 +++++++++ wxsdk/service/AdService.ts | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/service/DataService.ts | 458 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/service/GameService.ts | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/service/LogService.ts | 461 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/service/OnlineService.ts | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/service/ShareVideoService.ts | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/service/entity/SdkData.ts | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/service/entity/SdkData.ts.meta | 9 +++++++++ wxsdk/service/entity/ShareData.ts | 21 +++++++++++++++++++++ wxsdk/service/entity/ShareData.ts.meta | 9 +++++++++ wxsdk/share/SDKShare.ts | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/share/SDKVideo.ts | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/share/SimulateShare.ts | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/utils/Base64Utils.ts | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/utils/DateUtils.ts | 24 ++++++++++++++++++++++++ wxsdk/utils/RandomUtils.ts | 33 +++++++++++++++++++++++++++++++++ wxsdk/utils/SDKUtils.ts | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/utils/Sha1Utils.ts | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/utils/SignUtils.ts | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/utils/StorageUtils.ts | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/wx/Version.ts | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/wx/WxApi.ts | 415 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/wx/WxBanner.ts | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/wx/WxCustom.ts | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/wx/WxGrid.ts | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/wx/WxInit.ts | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/wx/WxInterstitial.ts | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/wx/WxLaunch.ts | 32 ++++++++++++++++++++++++++++++++ wxsdk/wx/WxLogin.ts | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/wx/WxPay.ts | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ wxsdk/wx/WxSystem.ts | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 51 files changed, 7553 insertions(+), 0 deletions(-) create mode 100755 .gitignore create mode 100644 sdk/Analytics.ts create mode 100644 sdk/SDKTools.ts create mode 100644 sdk/shareTools.ts create mode 100644 wxsdk/WXSDK.ts create mode 100644 wxsdk/base/BaseIStorage.ts create mode 100644 wxsdk/base/SDKBaseData.ts create mode 100644 wxsdk/base/SDKConst.ts create mode 100644 wxsdk/base/SDKDefault.ts create mode 100644 wxsdk/base/SDKEnum.ts create mode 100644 wxsdk/base/SDKEventCenter.ts create mode 100644 wxsdk/base/SDKEventEnum.ts create mode 100644 wxsdk/base/wx.d.ts create mode 100644 wxsdk/http/SDKApi.ts create mode 100644 wxsdk/http/SDKHttp.ts create mode 100644 wxsdk/lib/md5.ts create mode 100644 wxsdk/lib/sdk.d.ts create mode 100644 wxsdk/platform/Storage.ts create mode 100644 wxsdk/platform/wx/WxStorage.ts create mode 100644 wxsdk/platform/wx/WxStorage.ts.meta create mode 100644 wxsdk/service/AdService.ts create mode 100644 wxsdk/service/DataService.ts create mode 100644 wxsdk/service/GameService.ts create mode 100644 wxsdk/service/LogService.ts create mode 100644 wxsdk/service/OnlineService.ts create mode 100644 wxsdk/service/ShareVideoService.ts create mode 100644 wxsdk/service/entity/SdkData.ts create mode 100644 wxsdk/service/entity/SdkData.ts.meta create mode 100644 wxsdk/service/entity/ShareData.ts create mode 100644 wxsdk/service/entity/ShareData.ts.meta create mode 100644 wxsdk/share/SDKShare.ts create mode 100644 wxsdk/share/SDKVideo.ts create mode 100644 wxsdk/share/SimulateShare.ts create mode 100644 wxsdk/utils/Base64Utils.ts create mode 100644 wxsdk/utils/DateUtils.ts create mode 100644 wxsdk/utils/RandomUtils.ts create mode 100644 wxsdk/utils/SDKUtils.ts create mode 100644 wxsdk/utils/Sha1Utils.ts create mode 100644 wxsdk/utils/SignUtils.ts create mode 100644 wxsdk/utils/StorageUtils.ts create mode 100644 wxsdk/wx/Version.ts create mode 100644 wxsdk/wx/WxApi.ts create mode 100644 wxsdk/wx/WxBanner.ts create mode 100644 wxsdk/wx/WxCustom.ts create mode 100644 wxsdk/wx/WxGrid.ts create mode 100644 wxsdk/wx/WxInit.ts create mode 100644 wxsdk/wx/WxInterstitial.ts create mode 100644 wxsdk/wx/WxLaunch.ts create mode 100644 wxsdk/wx/WxLogin.ts create mode 100644 wxsdk/wx/WxPay.ts create mode 100644 wxsdk/wx/WxSystem.ts diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..f509e3f --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +bin-debug +bin-release +exml.e.d.ts +.idea/**/ +.idea +libs/modules +node_modules/ +bin/res +bin/res/* +bin/ui +bin/ui/* +bin/js +bin/js/* +bin/code.js +bin/texture.png +bin/unpack.json +.idea/**/* +.idea +.laya/pubset.json +.laya/settings.json +.laya/chrome +.laya/chrome/* +release/wxgame/code.js +release/wxgame/local + +wxsdk/*.meta +sdk/*.meta +wxsdk/*/*.meta + + +.DS_Store +wxgame/.DS_Store + +Thumbs.db +code/Thumbs.db +/tool/clear.sh diff --git a/sdk/Analytics.ts b/sdk/Analytics.ts new file mode 100644 index 0000000..1df252d --- /dev/null +++ b/sdk/Analytics.ts @@ -0,0 +1,87 @@ +import WXSDK from "../wxsdk/WXSDK"; + + +export class Analytics { + private static _instance: Analytics; + private systemType: number + static get I(): Analytics { + return this._instance || (this._instance = new Analytics); + } + + private static IGNORE_LOGGER = false; + + constructor() { + } + + private get isSupported() { + return typeof WXSDK === 'object'; + } + + // 游戏打点 + dot(dot_type: string, data: any = {}) { + if (typeof wx == 'undefined') return + // wx.aldSendEvent(dot_type, data); //阿拉丁打点,没需求注释即可 + return WXSDK.stat.dot(dot_type, data); + } + + // loading完成 + loadingFinish() { + return WXSDK.stat.loadingFinish(); + } + // 用户停留30s打点 + stay(time) { + return WXSDK.stat.stay(time) + } + // 关键行为 + behaviors() { + return WXSDK.stat.behaviors() + } + // 导出打点 + jumps(id, type) { + return WXSDK.stat.jumps(id, type) + } + + /** + * 关卡开始 + * @param stageid 关卡ID, 必须1.1、12.2 “.”前面代表模式 后面代表关卡 + * @param stagename 关卡名称,格式:"xx模式-第x关" + * @param pattern 模式名称,格式:"xx模式" + */ + levelStart(stageid, stagename, pattern) { + return WXSDK.stat.levelStart(stageid, stagename, pattern) + } + /** + * 关卡进行中 + * @param stageid 关卡ID, 必须1.1、12.2 “.”前面代表模式 后面代表关卡 + * @param stagename 关卡名称,格式:"xx模式-第x关" + * @param pattern 模式名称,格式:"xx模式" + * @param event 事件 tools:使用道具 award:奖励 + * @param params_id 道具ID + * @param params_name 道具名称 + * @param params_count 道具数量 + * @param params_desc 道具描述 + */ + levelRunning(stageid, stagename, pattern, event, params_id, params_name, params_count, params_desc?) { + return WXSDK.stat.levelRunning(stageid, stagename, pattern, event, params_id, params_name, params_count, params_desc) + } + /** + * 关卡结束 + * @param stageid 关卡ID, 必须1.1、12.2 “.”前面代表模式 后面代表关卡 + * @param stagename 关卡名称,格式:"xx模式-第x关" + * @param pattern 模式名称,格式:"xx模式" + * @param event complete:成功 fail:失败 + * @param times 时间 + * @param perc 失败时的完成进度 (浮点数) + */ + async levelEnd(stageid, stagename, pattern, event, times, perc?) { + return WXSDK.stat.levelEnd(stageid, stagename, pattern, event, times, perc) + } + +} +export enum EventKey { + toggleScene = 'toggleScene', + recoverGame = 'recoverGame', + replayGame = 'replayGame', + settingClose = 'settingClose', + updateLvUI = 'updateLvUI', +} \ No newline at end of file diff --git a/sdk/SDKTools.ts b/sdk/SDKTools.ts new file mode 100644 index 0000000..8820aef --- /dev/null +++ b/sdk/SDKTools.ts @@ -0,0 +1,329 @@ +import { GAMEDATA } from "../wxsdk/base/SDKConst"; +import DateUtils from "../wxsdk/utils/DateUtils"; +import WXSDK from "../wxsdk/WXSDK"; + +/* +* SDK工具类库; +*/ +export class SDKTools { + static get isWx() { + return typeof wx !== 'undefined'; + } + static get isSupported() { + return typeof WXSDK === 'object'; + } + + static get envNum() { + return WXSDK.data.envEnum; + } + + static get scene() { + return WXSDK.data.scene; + } + static get uid() { + return WXSDK.data.userId; + } + static get channel() { + return WXSDK.data.channelId; + } + static get openId() { + return WXSDK.data.openId; + } + + // 是否当天注册用户 + static get isnew() { + try { + let t = DateUtils.today; + return WXSDK.data.regTime === t; + } catch (error) { + return false; + } + } + // 是否首次登录 + static get isFirstLogin() { + try { + return WXSDK.data.isnew === 1; + } catch (error) { + return false; + } + } + // 是否是某个时间点后注册的 + static isAfterTime(t) { + try { + let t1 = new Date(t).getTime(); + let t2 = new Date(`${WXSDK.data.regTime} 00:00:00`).getTime(); + // console.log("t1t2", t1, t2) + return t2 >= t1; + } catch (error) { + return false; + } + } + + + /** + * banner + * @param adUnitId bannerID + * @param opts { //非必填 默认满屏居低 + * type:默认1 banner + * bannerWidth: + * offsetY: 距离底部多远 + * isOff:是否关闭默认显示banner + * } + */ + static createBanner(adUnitId: string = GAMEDATA.bannerId, opts?: { type?: number; bannerWidth?: number, offsetY?: number; adIntervals?: number, isOff?: boolean }) { + if (!this.isWx) return + return WXSDK.ad.createBanner(adUnitId, opts); + } + /** + * banner 显示 ps:创建默认显示 + */ + static showBanner() { + if (!this.isWx) return + WXSDK.ad.showBanner() + } + /** + * banner 隐藏 + */ + static hideBanner() { + if (!this.isWx) return + WXSDK.ad.hideBanner(); + } + /** + * banner 销毁 + */ + static destoryBanner() { + if (!this.isWx) return + WXSDK.ad.destoryBanner(); + } + + + /** + * 插屏 + */ + static createInterstitialAd(adUnitId: string = GAMEDATA.interstitialAdId) { + if (!this.isWx) return + WXSDK.ad.createInterstitialAd(adUnitId); + } + + + /** + * 格子广告 + * @param adUnitId 格子广告ID + * @param opts { //非必填 默认满屏居低 + * bannerWidth: + * offsetY: 距离底部多远 + * isOff:是否默认不显示 + * } + */ + static createGrid(key: string, adUnitId: string = GAMEDATA.gridId, opts?: { gridCount?: number; bannerWidth?: number, offsetY: number; adIntervals?: number, isOff?: boolean }) { + if (!this.isWx) return + return WXSDK.ad.createGrid(key, adUnitId, opts); + } + /** + * 格子广告 显示 ps:创建默认显示 + */ + static showGrid(key: string) { + if (!this.isWx) return + WXSDK.ad.showGrid(key) + } + /** + * 格子广告 隐藏 + */ + static hideGrid(key: string) { + if (!this.isWx) return + WXSDK.ad.hideGrid(key) + } + /** + * 格子广告 销毁 + */ + static destoryGrid(key: string) { + if (!this.isWx) return + WXSDK.ad.destoryGrid(key) + } + + + /** + * 原生模板广告 全局只能存在一个 并且创建的位置最好是同一个位置 通过show hide控制 如果要改变位置,调用destory再调用创建(不建议频繁销毁创建,会导致广告拉取不到) + * @param adUnitId 格子广告ID + * @param opts { //非必填 默认满屏居低 + * bannerWidth: + * offsetY: 距离底部多远 + * } + */ + static createCustom(adUnitId: string = GAMEDATA.customId, opts: { top: number; left: number; adIntervals?: number }) { + if (!this.isWx) return + return WXSDK.ad.createCustom(adUnitId, opts); + } + /** + * 原生广告 显示 ps:创建默认显示 + */ + static showCustom() { + if (!this.isWx) return + WXSDK.ad.showCustom() + } + /** + * 原生广告 隐藏 + */ + static hideCustom() { + if (!this.isWx) return + WXSDK.ad.hideCustom() + } + /** + * 原生广告 销毁 + */ + static destoryCustom() { + if (!this.isWx) return + WXSDK.ad.destoryCustom() + } + + /** + * + * @param data 游戏数据 + * @param type 游戏类型 + * @param opts 扩展参数 + */ + static navigateToMiniProgram(data: IGameList, type, opts?) { + if (!this.isWx) return + return WXSDK.ad.navigateToMiniProgram(data, type, opts) + } + + + /** + * 订阅 + * @param template_ids 模板id eg['aaaaaaa','bbbbbbb'] + * @param ids 对应后台的id eg:['1','2'] + */ + static subScribe(template_ids: Array, ids: Array) { + if (!this.isWx) return + return WXSDK.game.subScribe(template_ids, ids) + } + + /** + * 登录,sdk已实现弱登录和强登录的判断 + */ + static login(isAuthorize?: boolean): Promise { + return WXSDK.game.login(isAuthorize); + } + /** + * 保存数据 + * @param key + * @param value + */ + static saveData(key: string, value: string) { + return WXSDK.game.saveData(key, value) + } + /** + * 获取数据 + * @param key + */ + static getData(key: string) { + return WXSDK.game.getData(key) + } + + /** + * 添加排行榜 2021-5-17 废弃新游戏勿用 + * + * @param key 获取排行时掉对应key即可获取对应的排行 + * @param value 值 + */ + static rankAdd(key: string, value: number) { + return WXSDK.game.rankAdd(key, value) + } + /** + * 添加排行榜扩展数据 + * @param value 对象 扩展字段 段位 等级等等... + */ + static updateRankData(value: Object = {}) { + let val = JSON.stringify(value) + return WXSDK.game.saveData('rankData', val) + } + /** + * 排行榜数据 废弃新游戏勿用 + * type:1 日榜 2周榜 3月榜 + * isRankData: 是否有扩展参数 + */ + static rankList(key: string, type: number = 1, isRankData: boolean = false): Promise> { + if (!this.isWx) return Promise.resolve({ code: -1 }) + return WXSDK.game.rankList(key, type, isRankData); + } + /** + * 添加排行榜 2021-5-17 新增 + * + * @param key 获取排行时掉对应key即可获取对应的排行 + * @param value 值 + * @param type 0永久排行1日排行2周排行3月排行,默认为0 + * @param sort 1 大到小 2小到大 + */ + static totalrankAdd(key: string, value: number, type: number = 0, sort: number = 1) { + return WXSDK.game.totalrankAdd(key, value, type, sort); + } + /** + * 排行榜数据 + * type:0永久榜单 1 日榜 2周榜 3月榜 + * isRankData: 是否有扩展参数 + * @param sort 1 大到小 2小到大 + */ + static totalrankList(key: string, type: number = 0, isRankData: boolean = false, sort: number = 1): Promise> { + if (!this.isWx) return Promise.resolve({ code: -1 }); + return WXSDK.game.totalrankList(key, type, isRankData, sort); + } + + + + + /** + * 互导数据 + * 1抽屉广告2猜你喜欢3格子广告4试玩 + */ + static adList(key: number): Promise> { + if (!this.isWx) return Promise.resolve({ code: -1 }) + return WXSDK.game.adList(key); + } + /** + * 添加曝光 + * @param type 1,抽屉 2 banner(猜你喜欢)3格子4猜你喜欢 + * @param arr + */ + static addExposure(type, arr: Array) { + if (!this.isWx) return Promise.resolve({ code: -1 }) + return WXSDK.stat.addExposure(type, arr) + } + + /** + * 初始化在线参数,初始化游戏调用 + */ + static updateOnlineParams() { + return WXSDK.online.updateOnlineConfig(); + } + + static getParamsInt(key: OnlineKeys | string, defaultVal: number = 0) { + return WXSDK.online.getParamsInt(key.toString(), defaultVal) + } + + static getParamsObj(key: OnlineKeys | string, defaultVal: any = {}) { + return WXSDK.online.getParamsObj(key.toString(), defaultVal) + } + + static getParamsString(key: OnlineKeys | string, defaultVal: string = '') { + return WXSDK.online.getParamsString(key.toString(), defaultVal) + } + + /** + * 支付 + * @param params + * @param opts + * @returns + */ + static pay(params: { money: number; source:string }, opts: any = {}) { + return WXSDK.game.pay(params, opts) + } + +} + +/** + * 中台自定义配置表 + */ +export enum OnlineKeys { + isOpenInterstitialAdId = 'isOpenInterstitialAdId', + shareRandom = 'shareRandom', +} \ No newline at end of file diff --git a/sdk/shareTools.ts b/sdk/shareTools.ts new file mode 100644 index 0000000..4720bd7 --- /dev/null +++ b/sdk/shareTools.ts @@ -0,0 +1,94 @@ +import { utils } from "../Base/utils"; +import { WxHelper } from "../Base/WxHelper"; +import WXSDK from "../wxsdk/WXSDK"; +import { OnlineKeys, SDKTools } from "./SDKTools"; + +/* +* 分享与视频工具类; +*/ +export class ShareTools { + private static isFail: boolean = false; + /** + * 是否进入分享 onShow有插屏的话用这个判断 + */ + public static onShowAd: boolean = false; + + public static isTest: boolean = false; + /** + * 验证分享:可处理成功、失败 + * @param shareKey + * @param params params.fail 有就不处理,没有自动处理 + * @param opts 目前支持4个key 1,title自定义分享标题 2,img_url自定义分享图片 3,share_type(不走后台配置写死走视频or分享。1分享2视频3无视频则分享)4,closeSimulate是否关闭模拟分享 + */ + static share(shareKey: string, params?: { success?: Function, fail?: Function, context?: any }, opts?: any) { + if (typeof wx === 'undefined' || this.isTest) { + params && params.success && params.success(); + return + } + this.onShowAd = true; + WXSDK.share.share(shareKey.toString(), params, opts).then(async res => { + this.onShowAd = false; + params && params.success && params.success(res); + }).catch(async err => { + console.log("err", err) + if (err && err.code && err.code === 1006) { + if (Math.random() > SDKTools.getParamsInt(OnlineKeys.shareRandom, 1) && !this.isFail) { + this.isFail = true; + let modalRes = await WxHelper.showModal({ + title: '分享失败', + content: `${Math.random() > 0.5 ? '分享失败,请重新分享!' : '请分享其它群试试!'}`, + showCancel: true, + confirmText: '去分享' + }) + if (modalRes) { + ShareTools.share(shareKey, params, opts); + return + } + } else { + this.isFail = false; + } + } + this.onShowAd = false; + if (!params || !params.fail) { + utils.tips(err.msg) + } else { + params && params.fail && params.fail(err); + } + })//this.buildParams(params) + } + /** + * 纯净分享 不处理回调 + * @param shareKey + */ + static pureShare(shareKey: string, opts = {}) { + this.share(shareKey, {}, { closeSimulate: true, ...opts }); + } + + /** + * 设置右上角分享key + * @param key + */ + static setForwardKey(key) { + WXSDK.share.setForwardKey(key) + } + + /** + * 获取分享还是视频 + * @param key + */ + static getShareType(key) { + try { + return WXSDK.share.getType(key) + } catch (err) { + console.log("1") + return 2 + } + } + +} + + + +export enum ShareKey { + +} diff --git a/wxsdk/WXSDK.ts b/wxsdk/WXSDK.ts new file mode 100644 index 0000000..cd78228 --- /dev/null +++ b/wxsdk/WXSDK.ts @@ -0,0 +1,55 @@ +import { __LOG__ } from "./base/SDKConst"; +import DataService from "./service/DataService"; +import SdkData from "./service/entity/SdkData"; +import ShareVideoService from "./service/ShareVideoService"; +import WxInit from "./wx/WxInit"; +import GameService from "./service/GameService"; +import LogService from "./service/LogService"; +import AdService from "./service/AdService"; +import OnlineService from "./service/OnlineService"; +import WxInterstitial from "./wx/WxInterstitial"; +export default class WXSDK { + public static get isWx(): boolean { + return typeof (wx) != "undefined" + } + public static get data(): SdkData { + return DataService.I.Data; + } + public static get share(): ShareVideoService { + return ShareVideoService.I; + } + + public static get game(): GameService { + return GameService.I; + } + + public static get ad(): AdService { + return AdService.I; + } + + public static get stat(): LogService { + return LogService.I; + } + + public static get online(): OnlineService { + return OnlineService.I; + } + + + public static async init() { + if (this.isWx) { + WxInit.I.init(); + //视频预加载 启动预加载视频会闪屏 + setTimeout(() => { + WxInterstitial.initInterstitialAd();//插屏预加载 + ShareVideoService.I.preloadVideo(); + }, 2000); + } + await this.game.env(); + if (this.isWx) { + ShareVideoService.I.init(); + } + return Promise.resolve(); + } + +} \ No newline at end of file diff --git a/wxsdk/base/BaseIStorage.ts b/wxsdk/base/BaseIStorage.ts new file mode 100644 index 0000000..961b4ba --- /dev/null +++ b/wxsdk/base/BaseIStorage.ts @@ -0,0 +1,11 @@ +export default interface BaseIStorage { + getItem(key: string): string; + + setItem(key: string, value: string): void; + + removeItem(key: string): void; + + hasKey(key: string): boolean; + + clear(): void; +} \ No newline at end of file diff --git a/wxsdk/base/SDKBaseData.ts b/wxsdk/base/SDKBaseData.ts new file mode 100644 index 0000000..f40a00b --- /dev/null +++ b/wxsdk/base/SDKBaseData.ts @@ -0,0 +1,3 @@ +export default class SDKBaseData{ + public static SimulateShareTime:number = 3000; +} \ No newline at end of file diff --git a/wxsdk/base/SDKConst.ts b/wxsdk/base/SDKConst.ts new file mode 100644 index 0000000..30b785a --- /dev/null +++ b/wxsdk/base/SDKConst.ts @@ -0,0 +1,172 @@ +// 游戏配置数据 只需要改动这里的配置信息 +// 游戏配置数据 只需要改动这里的配置信息 +export const GAMEDATA = { + game_id: '10060', + channel_id: '10060', + version: '1.0.0', + appkey: 'dd2bd347b23befef947c6d95de0d6cc9', + interstitialAdId: 'adunit-e8f80e01fb6e1b8e',//插屏ID + bannerId: 'adunit-cc8fff6dee9188d4',//banner + gridId: '',//格子 + customId: 'adunit-cf69738dc3970fa7',//原生模板单个 //adunit-fb925feb16d2c13b + videoAd: 'adunit-4f1adc59ecc219bb',//初始化视频广告id + shareMessageToFriend: { // 暂时只支持一个场景值 + scene: 10, //定向分享场景值1-50 配>0的会初始化 + sharekey: 'shareMessageToFriendScene',//定向分享对应后台的分享key + share_id: 26,//定向分享对应后台的分享key + }, + default_share: { + content: '2021最新的数独题库来啦!', + icon: 'https://cdn-wxsdk.miso-lab.com/0a/1c4c9d24237b33a4c6fd81d35e7ade.png?attname=share.png', + id: '9999', + key: 'default', + title: '默认', + typ: 1, + videoid: '' + }, + MidasPay: { // 米大师虚拟支付配置 + OfferId: "1450031480", // 在米大师申请的应用id + ZoneId: "1", // 分区ID,默认:1 + Mode: "game", // 默认:game + CurrencyType: "CNY", // 默认:CNY + Platform: '' // 申请接入时的平台 + } +} + + +// sdk版本 +export const SDKVersion = 'v1.0'; +// 是否打印 +export const __LOG__ = true; + +//游戏基础信息 +export const VersionHost = 'https://wxsdk-ver.d3games.com/version'; + +// 登录服务器url +export const LoginHost = { + Prod: 'https://login-wxsdk.d3games.com/', + Pre: 'https://login-wxsdk-pre.d3games.com/' +}; +// 打点服务器URL +export const DotHost = { + Prod: 'https://wxsdk-api.cn-beijing.log.aliyuncs.com/', + Pre: 'https://wxsdk-api.cn-beijing.log.aliyuncs.com/' +}; +// 业务接口 +export const GameHost = { + Prod: 'https://wxsdk-data.d3games.com/', + Pre: 'https://wxsdk-data-pre.d3games.com/', +}; +// +export const OrderHost = { + Prod: 'https://wxsdk-order.d3games.com/', + Pre: 'https://wxsdk-order-pre.d3games.com/', +}; + +export const HostKeys = { + //打点服务器 + Active: 'logstores/login/track', //活跃用户 + AdStat: 'logstores/adlog/track', // 上报广告行为 + logOut: 'logstores/times/track', // 上报时长 + Share: 'logstores/share/track', // 上报分享 + loadingFinish: 'logstores/firstsecren/track', //结束加载 + stay: 'logstores/stay/track', //用户停留 + behaviors: 'logstores/behaviors/track', //关键行为 + dot: 'logstores/events/track', //自定义打点 + jumps: 'logstores/jumps/track', //游戏跳转 + level: 'logstores/level/track', //关卡打点 + + //登录 + Login: 'api/login', //登录 + weakLogin: 'api/login', //登录 + Reftoken: 'api/reftoken', //刷新登录令牌 + + //业务接口 + ShareList: 'api/share/lst', //分享配置列表 + getConfig: 'api/game/config', //在线参数 + subscribe: 'api/subscribe/add', //订阅 + saveData: 'api/member/savedata', //存数据 + getData: 'api/member/getdata', //取数据 + rankAdd: 'api/rank/add', //排行榜添加分数 517废弃 + rankList: 'api/rank/list', //世界排行榜 517废弃 + totalrankAdd: 'game/totalrank/add', //排行榜添加分数 + totalrankList: 'game/totalrank/list', //世界排行榜 + adList: 'api/adplan/list', //广告计划列表 + + //订单 + orderReport: 'api/order/report' //订单信息上报 +}; + +// 本地存储keys +export const StorageKeys = { + SDKAdVtKey: '__pcsdk_advt_key__', + SDKAdMvKey: '__pcsdk_admv_key__', + SDKAdMvNewKey: '__pcsdk_admv_new_key__', + SDKTokenKey: '__pcsdk_token_key__', + SDKLaunchKey: '__pcsdk_launch_key__', + SDKEventLaunchKey: '__pcsdk_event_launch', + SDKOnlienKey: '__pcsdk_online_env_key__', + SDKStartUpKey: '__pcsdk_startup_key__', + SDKDayAllShareNumKey: '__pcsdk_day_allsharenum_key__', + SDKDayAllVideoNumKey: '__pcsdk_day_allvideonum_key__', + SDKIntegralShareNumKey: '__pcsdk_integral_sharenum_key__', + SDKVideoOverShareNumKey: '__pcsdk_video_over_sharenum_$0_key__', + SDKShareRatioKey: '__pcsdk_shareratio_key__', + SDKUserShieldKey: '__pcsdk_usershield_key__' +}; + +// 互导广告类型 +export const BannerType = { + +}; + +export const ErrorCode = { + UnKnow: { code: 1001, msg: '未知错误' }, + InvalidLogin: { code: 10000, msg: '登陆失效' } +}; + +export const InterstitalAdError = { + AdQuit: { code: 1000, msg: '要看完广告哦!' }, + AdFail: { code: 1001, msg: '加载广告失败!' }, + AdInvalid: { code: 999, msg: '广告UID不存在!' }, + AdNotOpen: { code: 1002, msg: '微信版本过低,暂不支持!' }, + AdPlaying: { code: 1003, msg: '正在加载中...' } +}; + +export const ShareVideoError = { + VideoQuit: { code: 1000, msg: '要看完视频哦!' }, + VideoFail: { code: 1001, msg: '视频广告加载失败!' }, + VideoInvalid: { code: 999, msg: '视频UID不存在!' }, + VideoNotOpen: { code: 1002, msg: '视频组件未开放!' }, + VideoPlaying: { code: 1003, msg: '正在观看视频中...' }, + ShareFail: { code: 1004, msg: '分享失败,请尝试发送至不同群!' }, + ShareSame: { code: 1005, msg: '别总骚扰这个群,换个群分享吧!' }, + ShareNotGroup: { code: 1006, msg: '请分享到群哦!' }, + NotNet: { code: 1007, msg: '网络错误~' }, + ShareRuleFail: { code: 1008, msg: '分享失败,请尝试发送至不同群!' }, + ShareOverLimit: { code: 1009, msg: '今日已达分享上限次数,请明日再来' }, +}; + +export const BannerError = { + BannerInvalid: { code: 1000, msg: '广告 uid不能为空!' }, + BannerFail: { code: 1001, msg: '加载广告失败!' }, + BannerNotOpen: { code: 1002, msg: '加载广告失败!' } +}; + +export const Method = { + Get: 'GET', + Post: 'POST' +}; + +export const SceneCode = { + WX_STORE: 1104, // 我的小程序, + WX_SHARE_FRIEND: 1007, // 好友分享 + WX_SHARE_GROUP: 1008, // 群分享 + WX_SHARE_TICKET: 1044, + WX_INTEGRAL: 1139 // 积分投放 +}; + +// sdk系统默认分享id +export const SDKDotType = { + Share: 1001 // 会话进入,无渠道ID +}; \ No newline at end of file diff --git a/wxsdk/base/SDKDefault.ts b/wxsdk/base/SDKDefault.ts new file mode 100644 index 0000000..6920d2d --- /dev/null +++ b/wxsdk/base/SDKDefault.ts @@ -0,0 +1,228 @@ + +/** 返回参数类型 */ +interface IResult { + /** 错误码 */ + code?: number; + /** 错误信息 */ + msg?: string; + /** 返回数据 */ + data?: T; +} + +/** 微信api 返回用户信息 */ +interface WxUserInfo { + /** 用户昵称 */ + nickName: string; + /** 用户头像图片的 URL。URL 最后一个数值代表正方形头像大小 + * (有 0、46、64、96、132 数值可选,0 代表 640x640 的正方形头像,46 表示 46x46 的正方形头像, + * 剩余数值以此类推。默认132),用户没有头像时该项为空。若用户更换头像,原有头像 URL 将失效。 */ + avatarUrl: string; + /** 用户性别 gender的合法值(0未知,1男性,2女性) */ + gender: number; + /** 用户所在国家 */ + country: string; + /** 用户所在省份 */ + province: string; + /** 用户所在城市 */ + city: string; + /** 显示 country,province,city 所用的语言(en英文,zh_CN简体中文,zh_TW繁体中文) */ + language: string; +} + +/** 显示模态对话框参数类型 */ +interface ShowModalType { + /** 提示的标题 */ + title: string; + /** 提示的内容 */ + content: string; + /** 是否显示取消按钮 */ + showCancel?: boolean; + /** 取消按钮的文字,最多 4 个字符 */ + cancelText?: string; + /** 取消按钮的文字颜色,必须是 16 进制格式的颜色字符串 */ + cancelColor?: string; + /** 确认按钮的文字,最多 4 个字符 */ + confirmText?: string; + /** 确认按钮的文字颜色,必须是 16 进制格式的颜色字符串 */ + confirmColor?: string; + /** 接口调用成功的回调函数 */ + success?: (res: { confirm: boolean, cancel: boolean }) => void; + /** 接口调用失败的回调函数 */ + fail?: () => void; + /** 接口调用结束的回调函数(调用成功、失败都会执行) */ + complete?: () => void; +} +/** + * 排行榜数据 + */ +interface IRankData { + fraction: number, // 对应type传入的分数、数量或者其他 + headurl: string, //头像 + nickname: string,//昵称 + uid: number//用户id + rankData: any //扩展参数 +} + +interface _NetworkTypeSuccessObject { + networkType: string; // wifi/2g/3g/4g/unknown(Android 下不常见的网络类型)/none(无网络) +} +interface _StyleObject { + left?: number; + + top?: number; + + width?: number; + + height?: number; +} +interface _StyleGameObject { + left?: number; + top?: number; +} + +interface _BannerAdObject { + adUnitId: string; + adIntervals?: number; + style: _StyleObject; +} +interface _FeedbackButtonObject { + /** + * 按钮的类型 + */ + type: string; + + /** + * 按钮上的文本,仅当 type 为 text 时有效 + */ + text?: string; + + /** + * 按钮的背景图片,仅当 type 为 image 时有效 + */ + image?: string; + + /** + * 按钮的样式 + */ + style: _ButtonStyle; +} + +interface _UserInfoButton { + type: string; + + text: string; + + image: string; + + style: _ButtonStyle; + + show: Function; + + hide: Function; + + destroy: Function; + + onTap: (callback: Function) => void; + + offTap: (callback: Function) => void; +} + +interface _ShareAppMessageObject { + /** + * 是否使用带 shareTicket 的转发详情 + */ + withShareTicket?: boolean; + /** + * 发标题,不传则默认使用当前小游戏的昵称。 + */ + title?: string; + + /** + * 转发显示图片的链接,可以是网络图片路径或本地图片文件路径或相对代码包根目录的图片文件路径。显示图片长宽比是 5: 4 + */ + imageUrl?: string; + + /** + * 查询字符串,必须是 key1 = val1 & key2=val2 的格式。从这条转发消息进入后,可通过 wx.getLaunchOptionSync() 或 wx.onShow() 获取启动参数中的 query。 + */ + query?: string; + + /** + * 审核通过的图片 ID,详见 使用审核通过的转发图片 + */ + imageUrlId?: string; + + /** + * 10.10后废弃 + */ + success?: (ret?: any) => void; + + /** + * 接口调用失败的回调函数 + */ + fail?: (err?: any) => void; + + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: () => void; + + /** + * 接口取消的回调 + */ + cancel?: () => void; +} +interface _UpdateShareMenuObject { + /** + * 是否使用带 shareTicket 的转发 + */ + withShareTicket?: boolean; + + /** + * 接口调用成功的回调函数 + */ + success?: () => void; + + /** + * 接口调用失败的回调函数 + */ + fail?: () => void; + + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: () => void; + + /** + * 接口取消的回调 + */ + cancel?: () => void; +} +interface _getShareInfoSuccessObject { + /** + * 错误信息 + */ + errMsg: string; + + /** + * 包括敏感数据在内的完整转发信息的加密数据,详细见[加密数据解密算法](./signature.md#加密数据解密算法) + */ + encryptedData: string; + + /** + * 加密算法的初始向量,详细见[加密数据解密算法](./signature.md#加密数据解密算法) + */ + iv: string; +} + + +interface IGameList { + id: number,//唯一id + appid: string,//微信appid + path: string, //携带参数 + icon: string, //游戏icon + game: string //游戏名称 + adtyp: string,//类型 + animation: string,//骨骼 + highlight: string,//是否高亮 +} diff --git a/wxsdk/base/SDKEnum.ts b/wxsdk/base/SDKEnum.ts new file mode 100644 index 0000000..fe4343b --- /dev/null +++ b/wxsdk/base/SDKEnum.ts @@ -0,0 +1,97 @@ +export enum EnvCode { + Prod = 2, // 正式环境 + Pre = 1 // 测试环境 +} + +// 性别 +export enum Gender { + Invalid = -1, // 无效 + Unknown = 0, // 未知 + Male = 1, // 男 + Female = 2 // 女 +} + +export enum BannerStat { + Click = 0, // 0 点击广告icon + Cancel = -1 // 1 取消(监听弹框【取消】事件) +} + +// 分享视频类型 +export enum ShareVideoType { + None = -1, // -1无分享无视频 + Share = 1, // 0分享 + Video = 2, // 2看视频 + VideoToShare = 3, // 3无视频则分享 +} + +// 分享视频来源 +export enum ShareVideoFrom { + None, // 无分享 + Share, // 同步分享 + ShareAysnc, // 异步分享 + Video // 看视频 +} + +// 用户策略log统计,枚举类型 +export enum TacticType { + ShareLaunch = 10, // 分享调起 + ShareInterrupt = 11, // 分享中断 + ShareSuccess = 12, // 分享完成 + ShareEnterLaunch = 13, // 分享被点入-用户启动游戏, + ShareEnterGame = 14, // 分享被点入-用户进入游戏成功 + + VideoFetchSuccess = 20, // 视频拉取播放 + VideoPlayInterrupt = 21,// 视频播放中断 + VideoPlayComplete = 22, // 视频播放完成 + VideoFetchFail = 23, // 视频拉取失败 + + InterstitialAdSuccess = 30, // 插屏广告拉取播放 + InterstitialAdFail = 31 // 插屏广告拉取失败 +} + +//分享打点类型 +export enum DOT_SHARE_TYPE { + default,//占位 + share, //点击分享 + click, //从别人的分享点入 +} +//广告类型 +export enum DOT_AD_TYPE { + default,//占位 + banner,//banner + video,//视频 + interstitial,//插屏 + grid,//格子广告 + custom,//原生 + +} +//广告状态 +export enum DOT_AD_STATUS { + default,//占位 + request,//请求 + rt,//返回 + show,//展示 + click,//点击 + complete,//完成 + interrupt,//中断 + fail,//失败 +} + +// 网络类型 +export enum NetworkType { + 'Wifi' = 'wifi', + '2g' = '2g', + '3g' = '3g', + '4g' = '4g', + 'Unknown' = 'unknown', // Android 下不常见的网络类型 + 'None' = 'none' // 无网络 +} + + +export enum AdType{ + default, + draw, + guessLike, + grid, + play +} \ No newline at end of file diff --git a/wxsdk/base/SDKEventCenter.ts b/wxsdk/base/SDKEventCenter.ts new file mode 100644 index 0000000..2eba0aa --- /dev/null +++ b/wxsdk/base/SDKEventCenter.ts @@ -0,0 +1,186 @@ +/** + * 事件管理器 + * eg: + * 注册一个事件:EventCenter.I.add( EventEnum.SCROLL_MAIL_VIEW , this.handleEvent , this ) + * 移除一个事件:EventCenter.I.remove( EventData.TYPES.SCROLL_MAIL_VIEW , this.handleEvent ) + * 触发一个事件:EventCenter.I.emit( EventData.TYPES.SCROLL_MAIL_VIEW, ...args ) + */ +// tslint:disable-next-line: jsdoc-format +export default class SDKEventCenter { + private _events: any; + private _onceReturnValue: any; + + private static instance: SDKEventCenter; + static get I(): SDKEventCenter { + return this.instance || (this.instance = new SDKEventCenter); + } + + // 防止外部初始化 + private constructor() { + this._events = {}; + } + + private getEvents(): any { + return this._events || (this._events = {}); + } + + private getListeners(evt: string): any { + let events = this.getEvents(); + return events[evt] || (events[evt] = []); + } + + private getListenersAsObject(evt: string) { + let listeners = this.getListeners(evt); + let response: any; + if (listeners instanceof Array) { + response = {}; + response[evt] = listeners; + } + + return response || listeners; + } + + private isValidListener(listener: any): boolean { + if (typeof listener === 'function' || listener instanceof RegExp) + return true; + + else if (listener && typeof listener === 'object') + return this.isValidListener(listener.listener); + + else + return false; + } + + private getOnceReturnValue() { + if (this.hasOwnProperty('_onceReturnValue')) { + return this._onceReturnValue; + } else { + return true; + } + } + + public set onceReturnValue(value: any) { + this._onceReturnValue = value; + } + + indexOf(listeners: any[], listener: Function, context: any) { + let i = listeners.length; + while (i--) { + if (listeners[i].listener === listener && (context ? listeners[i].context === context : true)) { + return i; + } + } + return -1; + } + + /** + * @param evt {String} evt + * @param listener {Function|Object} listener + * @param context {Object} context listener执行的上下文:当listener为function时候执行的上下文,当listener为Object时,可以不传,context可作为listener的一个context属性 + */ + add(evt: string, listener: any, context?: any): SDKEventCenter { + if (!evt || !evt.constructor) { + throw new TypeError('evt must be a string'); + } + + if (!this.isValidListener(listener)) { + throw new TypeError('listener must be a function'); + } + + let listeners = this.getListenersAsObject(evt); + let listenerIsWrapped = typeof listener === 'object'; + let key: string; + for (key in listeners) { + if (listeners.hasOwnProperty(key) && this.indexOf(listeners[key], listener, context) === -1) { + listeners[key].push(listenerIsWrapped ? listener : { + listener, + context, + once: false + }); + } + } + + return this; + } + + once(evt: string, listener: Function, context?: any): SDKEventCenter { + return this.add(evt, { + listener, + context, + once: true + }); + } + + remove(evt: string, listener: Function, context?: any): SDKEventCenter { + let listeners = this.getListenersAsObject(evt); + let index: number; + let key: string; + for (key in listeners) { + if (listeners.hasOwnProperty(key)) { + index = this.indexOf(listeners[key], listener, context); + if (index !== -1) { + listeners[key].splice(index, 1); + } + } + } + return this; + } + + removeAll(evt: string): SDKEventCenter { + let type = typeof evt; + let events = this.getEvents(); + if (type === 'string') { + delete events[evt]; + } else { + delete this._events; + } + return this; + } + + trigger(evt: string, ...args: any[]): SDKEventCenter { + let listenersMap = this.getListenersAsObject(evt); + let listeners: any; + let listener: any; + let i: number; + let key: string; + let response: any; + + for (key in listenersMap) { + if (listenersMap.hasOwnProperty(key)) { + listeners = listenersMap[key].slice(0); + + for (i = 0; i < listeners.length; i++) { + listener = listeners[i]; + + if (listener.once === true) { + this.remove(evt, listener.listener, listener.context); + } + + response = listener.listener.apply(listener.context || this, args || []); + + if (response === this.getOnceReturnValue()) { + this.remove(evt, listener.listener, listener.context); + } + } + } + } + return this; + } + + emit(evt: string, ...args: any[]) { + // let args = Array.prototype.slice.call(arguments, 1); + return this.trigger(evt, ...args); + } + + defineEvent(evt: string): SDKEventCenter { + this.getListeners(evt); + return this; + } + + defineEvents(evts: string[]): SDKEventCenter { + for (let i = 0, len = evts.length; i < len; i++) { + this.defineEvent(evts[i]); + } + return this; + } +} \ No newline at end of file diff --git a/wxsdk/base/SDKEventEnum.ts b/wxsdk/base/SDKEventEnum.ts new file mode 100644 index 0000000..b1f18a4 --- /dev/null +++ b/wxsdk/base/SDKEventEnum.ts @@ -0,0 +1,14 @@ +/** + * 事件枚举名称 + */ +export enum SDKEventEnum { + APP_SHOW = 'app.show', + APP_HIDE = 'app.hide', + TACTIC_UPDATE = 'tactic.update', + BANNER_HIDE = 'banner.hide', + BANNER_SHOW = 'banner.show', + BANNER_ERROR = 'banner.error', + BANNER_DESTORY = 'banner.destory', + BANNER_SUCCESS = 'banner.success', + ONLINE_SUCCESS = 'online.success' +} \ No newline at end of file diff --git a/wxsdk/base/wx.d.ts b/wxsdk/base/wx.d.ts new file mode 100644 index 0000000..4202dbd --- /dev/null +++ b/wxsdk/base/wx.d.ts @@ -0,0 +1,3 @@ + +declare const wx; +declare const qq; \ No newline at end of file diff --git a/wxsdk/http/SDKApi.ts b/wxsdk/http/SDKApi.ts new file mode 100644 index 0000000..95c0ec3 --- /dev/null +++ b/wxsdk/http/SDKApi.ts @@ -0,0 +1,63 @@ +import SDKHttp from "./SDKHttp"; +import { VersionHost, HostKeys } from "../base/SDKConst"; +import DataService from "../service/DataService"; + + +export class SDKApi { + + public static Version = (...args) => SDKHttp.httpPost(`${VersionHost}`, ...args); + + //GameApi + public static ShareList = (...args) => SDKHttp.httpPost(`${DataService.I.GameApi}${HostKeys.ShareList}`, ...args); + + public static getConfig = (...args) => SDKHttp.httpPost(`${DataService.I.GameApi}${HostKeys.getConfig}`, ...args); + //订阅 + public static subscribe = (...args) => SDKHttp.httpPost(`${DataService.I.GameApi}${HostKeys.subscribe}`, ...args); + // + public static saveData = (...args) => SDKHttp.httpPost(`${DataService.I.GameApi}${HostKeys.saveData}`, ...args); + public static getData = (...args) => SDKHttp.httpPost(`${DataService.I.GameApi}${HostKeys.getData}`, ...args); + //排行榜添加分数 废弃 + public static rankAdd = (...args) => SDKHttp.httpPost(`${DataService.I.GameApi}${HostKeys.rankAdd}`, ...args); + //排行榜添加分数 + public static totalrankAdd = (...args) => SDKHttp.httpPost(`${DataService.I.GameApi}${HostKeys.totalrankAdd}`, ...args); + //排行榜 废弃 + public static rankList = (...args) => SDKHttp.httpPost(`${DataService.I.GameApi}${HostKeys.rankList}`, ...args); + //排行榜 + public static totalrankList = (...args) => SDKHttp.httpPost(`${DataService.I.GameApi}${HostKeys.totalrankList}`, ...args); + //广告计划列表 + public static adList = (...args) => SDKHttp.httpPost(`${DataService.I.GameApi}${HostKeys.adList}`, ...args); + + + //LoginApi + public static Login = (...args) => SDKHttp.httpPost(`${DataService.I.LoginApi}${HostKeys.Login}`, ...args); + + public static reftoken = (...args) => SDKHttp.httpPost(`${DataService.I.LoginApi}${HostKeys.Reftoken}`, ...args); + + public static weakLogin = (...args) => SDKHttp.httpPost(`${DataService.I.LoginApi}${HostKeys.weakLogin}`, ...args); + + + //DotApi + // public static dot = (...args) => SDKHttp.httpGet(`${DataService.I.DotApi}${HostKeys.Dot}`, ...args); + + public static logOut = (...args) => SDKHttp.httpGet(`${DataService.I.DotApi}${HostKeys.logOut}`, ...args); + + public static loadingFinish = (...args) => SDKHttp.httpGet(`${DataService.I.DotApi}${HostKeys.loadingFinish}`, ...args); + + public static active = (...args) => SDKHttp.httpGet(`${DataService.I.DotApi}${HostKeys.Active}`, ...args); + + public static share = (...args) => SDKHttp.httpGet(`${DataService.I.DotApi}${HostKeys.Share}`, ...args); + + public static adStat = (...args) => SDKHttp.httpGet(`${DataService.I.DotApi}${HostKeys.AdStat}`, ...args); + + public static stay = (...args) => SDKHttp.httpGet(`${DataService.I.DotApi}${HostKeys.stay}`, ...args); + + public static behaviors = (...args) => SDKHttp.httpGet(`${DataService.I.DotApi}${HostKeys.behaviors}`, ...args); + + public static dot = (...args) => SDKHttp.httpGet(`${DataService.I.DotApi}${HostKeys.dot}`, ...args); + + public static jumps = (...args) => SDKHttp.httpGet(`${DataService.I.DotApi}${HostKeys.jumps}`, ...args); + + public static level = (...args) => SDKHttp.httpGet(`${DataService.I.DotApi}${HostKeys.level}`, ...args); + + public static pay = (...args) => SDKHttp.httpPost(`${DataService.I.OrderApi}${HostKeys.orderReport}`, ...args); +} diff --git a/wxsdk/http/SDKHttp.ts b/wxsdk/http/SDKHttp.ts new file mode 100644 index 0000000..a317446 --- /dev/null +++ b/wxsdk/http/SDKHttp.ts @@ -0,0 +1,107 @@ +import { GAMEDATA } from "../base/SDKConst"; +import SignUtils from "../utils/SignUtils"; + +export default class SDKHttp { + public static async httpRequest(url: string, method: string, data?: any, dataType: "json" | "string" = "json") { + return new Promise>((resolve, reject) => { + data = { + ...data, + ver: GAMEDATA.version, + gameid: GAMEDATA.game_id, + sign_type: 'md5', + time_stamp: (Math.floor(Date.now() / 1000)) + '', + } + + // if (url.indexOf('totalrank')>-1) { + // // console.log("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + // url = 'https://wxsdk-game-pre.d3games.com/game/totalrank/list'; + // } + + //生成sign + data = { + ...data, + sign: SignUtils.I.createSign(data) + } + if (data && typeof data === "object") { + data = JSON.stringify(data); + } + //console.error("sign", url, JSON.stringify(data)) + data = data || ""; + if (method == "GET" && data != "") { + data = JSON.parse(data); + let str = '' + for (let key in data) { + str = str + `${key}` + '=' + `${data[key]}&` + } + url += "?" + str; + data = ""; + } + let info = "[url:" + url + ", data:" + data + "]"; + let xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + if (xhr.status >= 200 && xhr.status < 400) { + let responseText: any = xhr.responseText; + // cc.log("responseText", responseText) + try { + responseText = JSON.parse(responseText); + // cc.log("responseText22", responseText) + if (url.indexOf('.json') > -1) { + resolve({ code: 0, data: responseText, msg: responseText.msg }); + } else { + resolve({ code: +responseText.code, data: responseText.data, msg: responseText.msg }); + } + return + } catch (ex) { + // console.error("httpRequest[parseError]:responseText=" + xhr.responseText); + resolve({ msg: "JSON parse error:" + ex.message, code: -1 }); + return; + } + } else { + console.error(xhr.status, '网络请求失败!'); + resolve({ code: -1 }); + } + } + }; + + xhr.ontimeout = function (info): void { + // cc.error("info1", info) + resolve({ msg: `请求超时!`, code: -1 }); + } + xhr.onerror = function (info): void { + // cc.error("info2", info) + resolve({ msg: `请求失败!`, code: -1 }); + } + xhr.onabort = function (info): void { + // cc.error("info3", info) + resolve({ msg: `请求失败!`, code: -1 }); + } + + xhr.open(method, url, true); + + if (method == "POST") { + xhr.setRequestHeader("Content-Type", "application/json;charset=utf-8")//application/x-www-form-urlencoded + // if (cc.sys.os === 'Android') { + // cc.error("http__uid", AppSdkData.I.uid); + // xhr.setRequestHeader('Uuid', `${AppSdkData.I.uid}`); + // } else { + // xhr.setRequestHeader('Uuid', `909`); + // } + + } + xhr.timeout = 3000; + xhr.send(data); + + + + }); + } + + public static async httpGet(url: string, data?: any, dataType: "json" | "string" = "json") { + return this.httpRequest(url, "GET", data, dataType); + } + + public static httpPost(url: string, data?: any, dataType: "json" | "string" = "json") { + return this.httpRequest(url, "POST", data, dataType); + } +} \ No newline at end of file diff --git a/wxsdk/lib/md5.ts b/wxsdk/lib/md5.ts new file mode 100644 index 0000000..fef3d16 --- /dev/null +++ b/wxsdk/lib/md5.ts @@ -0,0 +1,378 @@ + +export class Md5 { + // One time hashing functions + public static hashStr(str: string, raw?: false): string + public static hashStr(str: string, raw?: true): Int32Array + public static hashStr(str: string, raw: boolean = false) { + return this.onePassHasher + .start() + .appendStr(str) + .end(raw); + } + + public static hashAsciiStr(str: string, raw?: false): string + public static hashAsciiStr(str: string, raw?: true): Int32Array + public static hashAsciiStr(str: string, raw: boolean = false) { + return this.onePassHasher + .start() + .appendAsciiStr(str) + .end(raw); + } + // Private Static Variables + private static stateIdentity = new Int32Array([1732584193, -271733879, -1732584194, 271733878]); + private static buffer32Identity = new Int32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + private static hexChars = '0123456789abcdef'; + private static hexOut: string[] = []; + + // Permanent instance is to use for one-call hashing + private static onePassHasher = new Md5(); + + private static _hex(x: any): string { + const hc = Md5.hexChars; + const ho = Md5.hexOut; + let n; + let offset; + let j; + let i; + + for (i = 0; i < 4; i += 1) { + offset = i * 8; + n = x[i]; + for (j = 0; j < 8; j += 2) { + ho[offset + 1 + j] = hc.charAt(n & 0x0F); + n >>>= 4; + ho[offset + 0 + j] = hc.charAt(n & 0x0F); + n >>>= 4; + } + } + return ho.join(''); + } + + private static _md5cycle(x: Int32Array|Uint32Array, k: Int32Array|Uint32Array) { + let a = x[0]; + let b = x[1]; + let c = x[2]; + let d = x[3]; + // ff() + a += (b & c | ~b & d) + k[0] - 680876936 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[1] - 389564586 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[2] + 606105819 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[3] - 1044525330 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & c | ~b & d) + k[4] - 176418897 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[5] + 1200080426 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[6] - 1473231341 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[7] - 45705983 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & c | ~b & d) + k[8] + 1770035416 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[9] - 1958414417 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[10] - 42063 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[11] - 1990404162 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & c | ~b & d) + k[12] + 1804603682 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[13] - 40341101 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[14] - 1502002290 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[15] + 1236535329 | 0; + b = (b << 22 | b >>> 10) + c | 0; + // gg() + a += (b & d | c & ~d) + k[1] - 165796510 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[6] - 1069501632 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[11] + 643717713 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[0] - 373897302 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b & d | c & ~d) + k[5] - 701558691 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[10] + 38016083 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[15] - 660478335 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[4] - 405537848 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b & d | c & ~d) + k[9] + 568446438 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[14] - 1019803690 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[3] - 187363961 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[8] + 1163531501 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b & d | c & ~d) + k[13] - 1444681467 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[2] - 51403784 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[7] + 1735328473 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[12] - 1926607734 | 0; + b = (b << 20 | b >>> 12) + c | 0; + // hh() + a += (b ^ c ^ d) + k[5] - 378558 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[8] - 2022574463 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[11] + 1839030562 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[14] - 35309556 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (b ^ c ^ d) + k[1] - 1530992060 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[4] + 1272893353 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[7] - 155497632 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[10] - 1094730640 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (b ^ c ^ d) + k[13] + 681279174 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[0] - 358537222 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[3] - 722521979 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[6] + 76029189 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (b ^ c ^ d) + k[9] - 640364487 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[12] - 421815835 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[15] + 530742520 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[2] - 995338651 | 0; + b = (b << 23 | b >>> 9) + c | 0; + // ii() + a += (c ^ (b | ~d)) + k[0] - 198630844 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[5] - 57434055 | 0; + b = (b << 21 | b >>> 11) + c | 0; + a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[10] - 1051523 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0; + b = (b << 21 | b >>> 11) + c | 0; + a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[15] - 30611744 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0; + b = (b << 21 | b >>> 11) + c | 0; + a += (c ^ (b | ~d)) + k[4] - 145523070 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[2] + 718787259 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[9] - 343485551 | 0; + b = (b << 21 | b >>> 11) + c | 0; + + x[0] = a + x[0] | 0; + x[1] = b + x[1] | 0; + x[2] = c + x[2] | 0; + x[3] = d + x[3] | 0; + } + + private _dataLength: number; + private _bufferLength: number; + + private _state: Int32Array = new Int32Array(4); + private _buffer: ArrayBuffer = new ArrayBuffer(68); + private _buffer8: Uint8Array; + private _buffer32: Uint32Array; + + constructor() { + this._buffer8 = new Uint8Array(this._buffer, 0, 68); + this._buffer32 = new Uint32Array(this._buffer, 0, 17); + this.start(); + } + + public start() { + this._dataLength = 0; + this._bufferLength = 0; + this._state.set(Md5.stateIdentity); + return this; + } + + // Char to code point to to array conversion: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt + // #Example.3A_Fixing_charCodeAt_to_handle_non-Basic-Multilingual-Plane_characters_if_their_presence_earlier_in_the_string_is_unknown + public appendStr(str: string) { + const buf8 = this._buffer8; + const buf32 = this._buffer32; + let bufLen = this._bufferLength; + let code; + let i; + + for (i = 0; i < str.length; i += 1) { + code = str.charCodeAt(i); + if (code < 128) { + buf8[bufLen++] = code; + } else if (code < 0x800) { + buf8[bufLen++] = (code >>> 6) + 0xC0; + buf8[bufLen++] = code & 0x3F | 0x80; + } else if (code < 0xD800 || code > 0xDBFF) { + buf8[bufLen++] = (code >>> 12) + 0xE0; + buf8[bufLen++] = (code >>> 6 & 0x3F) | 0x80; + buf8[bufLen++] = (code & 0x3F) | 0x80; + } else { + code = ((code - 0xD800) * 0x400) + (str.charCodeAt(++i) - 0xDC00) + 0x10000; + if (code > 0x10FFFF) { + throw new Error('Unicode standard supports code points up to U+10FFFF'); + } + buf8[bufLen++] = (code >>> 18) + 0xF0; + buf8[bufLen++] = (code >>> 12 & 0x3F) | 0x80; + buf8[bufLen++] = (code >>> 6 & 0x3F) | 0x80; + buf8[bufLen++] = (code & 0x3F) | 0x80; + } + if (bufLen >= 64) { + this._dataLength += 64; + Md5._md5cycle(this._state, buf32); + bufLen -= 64; + buf32[0] = buf32[16]; + } + } + this._bufferLength = bufLen; + return this; + } + + public appendAsciiStr(str: string) { + const buf8 = this._buffer8; + const buf32 = this._buffer32; + let bufLen = this._bufferLength; + let i; + let j = 0; + + for (; ;) { + i = Math.min(str.length - j, 64 - bufLen); + while (i--) { + buf8[bufLen++] = str.charCodeAt(j++); + } + if (bufLen < 64) { + break; + } + this._dataLength += 64; + Md5._md5cycle(this._state, buf32); + bufLen = 0; + } + this._bufferLength = bufLen; + return this; + } + + public appendByteArray(input: Uint8Array) { + const buf8 = this._buffer8; + const buf32 = this._buffer32; + let bufLen = this._bufferLength; + let i; + let j = 0; + + for (; ;) { + i = Math.min(input.length - j, 64 - bufLen); + while (i--) { + buf8[bufLen++] = input[j++]; + } + if (bufLen < 64) { + break; + } + this._dataLength += 64; + Md5._md5cycle(this._state, buf32); + bufLen = 0; + } + this._bufferLength = bufLen; + return this; + } + + public getState() { + const self = this; + const s = self._state; + + return { + buffer: String.fromCharCode.apply(null, self._buffer8), + buflen: self._bufferLength, + length: self._dataLength, + state: [s[0], s[1], s[2], s[3]] + }; + } + + public setState(state: any) { + const buf = state.buffer; + const x = state.state; + const s = this._state; + let i; + + this._dataLength = state.length; + this._bufferLength = state.buflen; + s[0] = x[0]; + s[1] = x[1]; + s[2] = x[2]; + s[3] = x[3]; + + for (i = 0; i < buf.length; i += 1) { + this._buffer8[i] = buf.charCodeAt(i); + } + } + + public end(raw: boolean = false) { + const bufLen = this._bufferLength; + const buf8 = this._buffer8; + const buf32 = this._buffer32; + const i = (bufLen >> 2) + 1; + let dataBitsLen; + + this._dataLength += bufLen; + + buf8[bufLen] = 0x80; + buf8[bufLen + 1] = buf8[bufLen + 2] = buf8[bufLen + 3] = 0; + buf32.set(Md5.buffer32Identity.subarray(i), i); + + if (bufLen > 55) { + Md5._md5cycle(this._state, buf32); + buf32.set(Md5.buffer32Identity); + } + + // Do the final computation based on the tail and length + // Beware that the final length may not fit in 32 bits so we take care of that + dataBitsLen = this._dataLength * 8; + if (dataBitsLen <= 0xFFFFFFFF) { + buf32[14] = dataBitsLen; + } else { + const matches = dataBitsLen.toString(16).match(/(.*?)(.{0,8})$/); + if (matches === null) { + return; + } + + const lo = parseInt(matches[2], 16); + const hi = parseInt(matches[1], 16) || 0; + + buf32[14] = lo; + buf32[15] = hi; + } + + Md5._md5cycle(this._state, buf32); + + return raw ? this._state : Md5._hex(this._state); + } +} + +if (Md5.hashStr('hello') !== '5d41402abc4b2a76b9719d911017c592') { + console.error('Md5 self test failed.'); +} \ No newline at end of file diff --git a/wxsdk/lib/sdk.d.ts b/wxsdk/lib/sdk.d.ts new file mode 100644 index 0000000..0a7f870 --- /dev/null +++ b/wxsdk/lib/sdk.d.ts @@ -0,0 +1,723 @@ + +interface _OnlineInterface { + /** + * 请求在线参数 + * 在游戏启动时候请求在线参数,最早的获取得的在线参数。 + */ + updateOnlineConfig(): Promise; + + /** + * 获取在线参数 + * @param key 在线参数key + */ + getParams(key: string): any; + + /** + * 获取对象数值在线配置参数 + * @param key 在线参数key + * @param defaultVal 获取不到使用默认值 + */ + getParamsObj(key: string, defaultVal?: any); + + /** + * 获取数字在线配置参数 + * @param key 在线参数key + * @param defaultVal 获取不到使用默认值 + */ + getParamsInt(key: string, defaultVal?: number): number; + + /** + * 获取字符串在线配置参数 + * @param key 在线参数key + * @param defaultVal 获取不到使用默认值 + */ + getParamsString(key: string, defaultVal?: string): string; +} + +interface _ConfigInterface { + /** + * 合并覆盖配置:合并config.js配置信息 + * @param conf Object 配置信息 + */ + merge(conf: any): void; +} + +interface _StorageInterface { + /** + * 设置本地存储 + * @param expiration 过期时间(秒) + */ + set(key: string, value: Object, expiration?: number): void; + + /** + * 获取缓存 + * @param key + */ + get(key: string): any; + + /** + * 移除缓存 + * @param key + */ + remove(key: string): void; + + /** + * 清除缓存 + */ + clear(): void; + + /** + * 判断缓存是否存在 + * @param key + */ + isExist(key: string): boolean; +} + +interface _PlatformInterface { + /** + * 创建授权按钮 + * @param type 页面类型 + * @param params 按钮样式,参照微信授权按钮参数 + * @param opts {Object} + * { + * isDebug:boolean 是否是调试模式 + * callback:(status: number, ret: _GetUserInfoSuccessObject | null ) => void + * 回调函数 + * 参数status: 1(授权成功) 0(授权失败) -1(取消授权)取消授权会弹出是否调整到设置界面设置 + * 参数ret:当status状态为1的时候返回wx.getUserInfo返回的数据信息_GetUserInfoSuccessObject,status为0或者-1返回null + * context:any 函数执行上下文 + * } + */ + createUserBtn( + type: string, + params: Array<_UserInfoButtonObject> | _UserInfoButtonObject, + opts: { isDebug?: boolean; callback: (status: number, ret: _GetUserInfoSuccessObject | null) => void; context: any } + ): void; + + /** + * 根据类型显示授权按钮 + * @param type + */ + showUserBtn(type: string): void; + + /** + * 根据类型隐藏授权按钮 + * @param type + */ + hideUserBtn(type: string): void; + + /** + * 根据类型销毁授权按钮 + * @param type + */ + destoryUserBtn(type: string): void; + + /** + * 销毁所有授权按钮 + */ + destoryAllUserBtn(): void; + + /** + * 打开客服消息 + */ + openCustomerServiceConversation(params?: _CustomerServiceConversationObject): number; + + /** + * 检测更新 + * @param data 参数与wx.showModal一模一样,可不传递,不传递更新状态下弹出默认的弹出框:title=>更新提示 content=>新版本已经准备好,是否重启应用? + */ + checkUpdate(data?: _ShowModalObject): void; + + /** + * 长震动 + */ + vibrateLong(): Promise; + + /** + * 短震动 + */ + vibrateShort(): Promise; + + /** + * 开启关闭群排行 + * @param val + */ + updateShareMenu(val: boolean): void; + + /** + * 显示模态弹出框 + * @param data + */ + showModal(data: _ShowModalObject): void; + + /** + * 复制文本 + * @param str + */ + copy(str: string): Promise; + + /** + * 多平台发起支付 + * @param params {Object} + * { + * money: 支付花费钱,单位元 + * desc: 支付道具的描述 + * } + */ + pay(params: { money: number; desc: string }); + + + logPay(params: { type: number; source: string; amount: number; buy_id: string | number; buy_name: string; item_info: string }); + + /** + * 订阅消息api + * @param tmplIds Array 需要订阅的消息模板的id的集合,格式为:["a","b"] + */ + subScribe(tmplIds: Array): Promise; + + /** + * 获取视频是否正在播放中 + */ + isVideoPlaying(): boolean; + + /** + * 获取小游戏推荐弹窗组件GamePortal是否正在加载中 + */ + isGamePortalPlaying(): boolean; + + /** + * 获取插屏广告 or 推荐弹窗组件 是否正在加载中 + */ + isInterstitialPlaying(): boolean; + + /** + * 显示插屏广告 + * @param adKey 广告adUnitId + * @param opts 扩展参数 + */ + interstitialShow(adUnitId: string, opts?: any): Promise; + + /** + * 显示插屏广告 or 小游戏推荐弹窗组件 + * @param adKey 广告adUnitId + * @param opts 扩展参数 + */ + gamePortalShow(adUnitId: string, opts?: any): Promise; + + /** + * 创建并显示小游戏推荐icon组件 + * @param adKey 广告adUnitId + * @param opts 扩展参数,创建数量和样式 + */ + gameIconShow(adUnitId: string, opts: { count: number, style: Array<_GameIconStyleItem> }): Promise; + + gameIconDestroy(): void; + + /** + * banner广告组件api + */ + banner: _BannerInterface; + + /** + * 获取系统信息 + */ + getSystemData(): SystemInfoSyncReturnValue; + + /** + * 获取启动信息 + */ + getLaunchData(): LaunchInfoSyncReturnValue; +} + +interface SystemInfoSyncReturnValue { + /** + * 手机品牌 + */ + brand: any; + + /** + * 手机型号 + */ + model: any; + + /** + * 设备像素比 + */ + pixelRatio: any; + + /** + * 屏幕宽度 + */ + screenWidth: any; + + /** + * 屏幕高度 + */ + screenHeight: any; + + /** + * 可使用窗口宽度 + */ + windowWidth: any; + + /** + * 可使用窗口高度 + */ + windowHeight: any; + + /** + * 状态栏的高度 + */ + statusBarHeight: any; + + /** + * 微信设置的语言 + */ + language: any; + + /** + * 微信版本号 + */ + version: any; + + /** + * 操作系统版本 + */ + system: any; + + /** + * 客户端平台 + */ + platform: any; + + /** + * 用户字体大小设置。以“我-设置-通用-字体大小”中的设置为准,单位:px + */ + fontSizeSetting: any; + + /** + * 客户端基础库版本 + */ + SDKVersion: any; +} + +interface LaunchInfoSyncReturnValue { + scene: number; + + query: any; + + shareTicket: string; + + referrerInfo: _LaunchInfoReferrerInfo; +} + +interface _LaunchInfoReferrerInfo { + /** + * 来源小程序、公众号或 App 的 appId + */ + appId: string; + + /** + * 来源小程序传过来的数据,scene = 1037或1038时支持 + */ + extraData: any; +} + +interface _BannerInterface { + /** + * 初始化设置宽度 建议初始化调用 + * @param designWidth 游戏设计宽度,默认宽度:750 + * @param bannerWidth banner显示宽度,默认宽度:750 + */ + setWidth(designWidth: number, bannerWidth: number); + + /** + * 加载banner广告 + * @param adUnitId 广告的unitId,微信后台申请提供 + * @param opts {Object} + * { + * designWidth: 游戏设计宽度,默认宽度:750 同setWidth api的designWidth参数 + * bannerWidth banner显示宽度,默认宽度:750 同setWidth api的bannerWidth参数 + * } + */ + load(adUnitId: string, opts?: { type?: number; designWidth?: number; bannerWidth?: number }): void; + + /** + * 显示banner广告 + */ + show(adUnitId: string): void; + + /** + * 隐藏banner广告 + */ + hide(adUnitId: string): void; + + /** + * 切换显示和隐藏banner广告 + * @param isshow 是否显示/隐藏 true为显示 false为隐藏 + */ + toggle(adUnitId: string, isshow: boolean): void; + + /** + * 销毁banner广告 + */ + destory(adUnitId: string): void; + + /** + * 清除所有banner定时器 + */ + clear(): void; +} + +/** + * 内部游戏选传传信息 + */ +interface _LoginInnerData { + channel?: number | string; + userId?: number | string; + regTime?: number | string; + openId?: string; + token?:string; + refToken?:string; + expire?:number; + isnew?:number +} + +/** + * 外部游戏必传openId和regTime + */ +interface _LoginOuterData { + openId: string; + regTime: number | string; +} + +interface _LogLevelData { + /** + * 关卡ID(或者合成项目的等级) + */ + id: string | number; + + /** + * 玩法类型(-1老版本闯关,0新版闯关,1合成) + */ + ptype: number; + + /** + * 战斗属性(例如枪的等级) + */ + attr?: number; + + /** + * 操作次数 + */ + anum?: number; + + /** + * 是否使用复活,rnum传递true,表示使用复活,复活次数+1;传递false或者不传递没有使用复活,复活次数不变 + */ + rnum?: boolean; + + /** + * 耗时,单位秒 + */ + ctime?: number; + + /** + * 是否第一次体验关卡,1是,0否 + */ + first?: number; + + /** + * 结束类型,1、通关;2、失败;3、主动退出-onhide + */ + type?: number; + + /** + * 扩展参数:{ key1:val1, key2:val2 }的格式 + */ + ext?: any; +} + +interface _LogResData { + /** + * 资源ID,客户端自定义配置 + */ + id: string | number; + + /** + * 资源名称,客户端自定义配置 + */ + name: string; + + /** + * 类型,0减少,1增加 + */ + type: number; + + /** + * 变化数量 + */ + cnum: number; + + /** + * 变化后剩余数量 + */ + onum: number; + + /** + * 关卡id + */ + levelid: number; + + /** + * 变化原因,客户端自定义配置 + */ + reason: string; +} + +interface _ExposureData { + banner_id: string | number; +} + +interface _RefererInfoData { + /** + * 来源小程序、公众号或 App 的 appId + */ + appId: string; + + /** + * 来源小程序传过来的数据,scene=1037或1038时支持 + */ + extData: any; +} + +interface _UserInfo { + /** + * 用户昵称 + */ + nickName: string; + + /** + * 用户头像图片的 URL。URL 最后一个数值代表正方形头像大小(有 0、46、64、96、132 数值可选,0 代表 640x640 的正方形头像, + * 46 表示 46x46 的正方形头像,剩余数值以此类推。默认132),用户没有头像时该项为空。若用户更换头像,原有头像 URL 将失效。 + */ + avatarUrl: string; + + /** + * 用户性别,gender 的合法值 0:未知 1:男性 2:女性 + */ + gender: number; + + /** + * 用户所在国家 + */ + country: string; + + /** + * 用户所在身份 + */ + province: string; + + /** + * 用户所在城市 + */ + city: string; + + /** + * 显示 country,province,city 所用的语言 + * language 的合法值 en:英文 zh_CN:简体中文 zh_TW:繁体中文 + */ + language: string; +} + +interface _ShowModalObject { + title?: string; + + content?: string; + + showCancel?: boolean; + + cancelText?: string; + + cancelColor?: string; + + confirmText?: string; + + confirmColor?: string; + + success?: (result: _ShowModalSuccessObject) => void; + + fail?: (err) => void; + + complete?: () => void; +} + +interface _ShowModalSuccessObject { + /** + * 为 true 时,表示用户点击了确定按钮 + */ + confirm: boolean; + + /** + * 为 true 时,表示用户点击了取消(用于 Android 系统区分点击蒙层关闭还是点击取消按钮关闭) + */ + cancel: boolean; +} + + +interface _GetUserInfoSuccessObject { + /** + * 用户信息对象,不包含 openid 等敏感信息 + */ + userInfo: _UserInfo; + + /** + * 不包括敏感信息的原始数据字符串,用于计算签名。 + */ + rawData: string; + + /** + * 使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,参考文档 [signature](./signature.md)。 + */ + signature: string; + + /** + * 包括敏感数据在内的完整用户信息的加密数据,详细见[加密数据解密算法](./signature.md#加密数据解密算法) + */ + encryptedData: string; + + /** + * 加密算法的初始向量,详细见[加密数据解密算法](./signature.md#加密数据解密算法) + */ + iv: string; +} + +interface _UserInfoButtonObject { + /** + * 按钮的类型 + */ + type: string; + + /** + * 按钮上的文本,仅当 type 为 text 时有效 + */ + text?: string; + + /** + * 按钮的背景图片,仅当 type 为 image 时有效 + */ + image?: string; + + /** + * 按钮的样式 + */ + style: _ButtonStyle; + + /** + * 是否带上登录态信息。当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期, + * 此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态, + * 返回的数据不包含 encryptedData, iv 等敏感信息。 + */ + withCredentials?: boolean; +} + +interface _ButtonStyle { + left: number; + + top: number; + + width: number; + + height: number; + + backgroundColor?: string; + + borderColor?: string; + + textAlign?: string; + + borderWidth?: number; + + borderRadius?: number; + + fontSize?: number; + + lineHeight?: number; +} + +interface _CustomerServiceConversationObject { + /** + * 充值档位id + */ + itemId?: string | number; + + /** + * 会话来源 + */ + sessionFrom?: string; + + /** + * false 否 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息 + */ + showMessageCard?: boolean; + + /** + * 会话内消息卡片标题 + */ + sendMessageTitle?: string; + + /** + * 会话内消息卡片路径 + */ + sendMessagePath?: string; + + /** + * 会话内消息卡片图片路径 + */ + sendMessageImg?: string; + + /** + * 接口调用成功的回调函数 + */ + success?: (ret?: any) => void; + + /** + * 接口调用失败的回调函数 + */ + fail?: (err: any) => void; + + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + complete?: () => void; +} + +interface _GameIconStyleItem { + // 游戏名称是否隐藏 + appNameHidden: boolean; + // 游戏名称的颜色色值 + color: string; + // 游戏icon的宽高值 + size: number; + // 游戏icon的border尺寸 + borderWidth: number; + // 游戏icon的border颜色色值 + borderColor: string; + // 游戏icon的X轴坐标 + left: number; + // 游戏icon的Y轴坐标 + top: number; +} + +interface _adStat { + // 游戏id + game_id: number, + // openid + openid: string, + // 渠道id + channel_id: number, + // 注册时间 + reg_time: number, + // 类型 默认0 + type: number, +} \ No newline at end of file diff --git a/wxsdk/platform/Storage.ts b/wxsdk/platform/Storage.ts new file mode 100644 index 0000000..f47997b --- /dev/null +++ b/wxsdk/platform/Storage.ts @@ -0,0 +1,30 @@ +import BaseIStorage from "../base/BaseIStorage"; + +export default class Storage implements BaseIStorage { + getItem(key: string) { + return localStorage.getItem(key) || ''; + } + + setItem(key: string, value: string) { + localStorage.setItem(key, value); + } + + removeItem(key: string) { + localStorage.removeItem(key); + } + + clear() { + localStorage.clear(); + } + + hasKey(key: string): boolean { + let len = localStorage.length; + if (len) { + for (let i = 0; i < len; i++) { + let find = localStorage.key(i); + if (key === find) return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/wxsdk/platform/wx/WxStorage.ts b/wxsdk/platform/wx/WxStorage.ts new file mode 100644 index 0000000..2aa8fb5 --- /dev/null +++ b/wxsdk/platform/wx/WxStorage.ts @@ -0,0 +1,28 @@ +import BaseIStorage from "../../base/BaseIStorage"; + +export default class WxStorage implements BaseIStorage { + getItem(key: string) { + return wx.getStorageSync(key); + } + + setItem(key: string, value: string) { + wx.setStorageSync(key, value); + } + + removeItem(key: string) { + wx.removeStorageSync(key); + } + + clear() { + wx.clearStorageSync(); + } + + hasKey(key: string): boolean { + let keys = wx.getStorageInfoSync().keys; + let finds: string[] = []; + if (keys && keys.length) + finds = keys.filter((k: string) => k === key); + + return finds.length !== 0; + } +} \ No newline at end of file diff --git a/wxsdk/platform/wx/WxStorage.ts.meta b/wxsdk/platform/wx/WxStorage.ts.meta new file mode 100644 index 0000000..e76a04b --- /dev/null +++ b/wxsdk/platform/wx/WxStorage.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "6013014c-7aa3-4eb4-b818-583d937a9f05", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/wxsdk/service/AdService.ts b/wxsdk/service/AdService.ts new file mode 100644 index 0000000..6425397 --- /dev/null +++ b/wxsdk/service/AdService.ts @@ -0,0 +1,126 @@ +import WxApi from "../wx/WxApi"; +import WxBanner from "../wx/WxBanner"; +import WxGrid from "../wx/WxGrid"; +import WxCustom from "../wx/WxCustom"; +import WxInterstitial from "../wx/WxInterstitial"; +import { GAMEDATA } from "../base/SDKConst"; +import LogService from "./LogService"; + + + +export default class AdService { + private constructor() { + } + /** + * 创建banner + * @param adUnitId + * @param opts + */ + createBanner(adUnitId: string, opts?: { type?: number; bannerWidth?: number, offsetY?: number; adIntervals?: number, isOff?: boolean }) { + return WxBanner.I.create(adUnitId, opts); + } + /** + * banner 显示 ps:创建默认显示 + */ + showBanner() { + WxBanner.I.show() + } + /** + * banner 隐藏 + */ + hideBanner() { + WxBanner.I.hide(); + } + /** + * banner 销毁 + */ + destoryBanner() { + WxBanner.I.destory(); + } + + + /** + * 插屏 + * @param adUnitId + */ + createInterstitialAd(adUnitId: string) { + WxInterstitial.showInterstitialAd(adUnitId) + // let interstitialAd = WxApi.I.createInterstitialAd(adUnitId); + // interstitialAd.show(); + } + + /** + * 创建格子广告 + * @param adUnitId + * @param opts + */ + createGrid(key: string, adUnitId: string, opts?: { gridCount?: number; bannerWidth?: number, offsetY: number; adIntervals?: number }) { + return WxGrid.I.create(key, adUnitId, opts); + } + /** + * 格子广告 显示 ps:创建默认显示 + */ + showGrid(key: string) { + WxGrid.I.show(key) + } + /** + * 格子广告 隐藏 + */ + hideGrid(key: string) { + WxGrid.I.hide(key); + } + /** + * 格子广告 销毁 + */ + destoryGrid(key: string) { + WxGrid.I.destory(key); + } + + /** + * 创建原生广告 + * @param adUnitId + * @param opts + */ + createCustom(adUnitId: string, opts: { top: number; left: number; adIntervals?: number }) { + return WxCustom.I.create(adUnitId, opts); + } + /** + * 原生广告 显示 ps:创建默认显示 + */ + showCustom() { + WxCustom.I.show() + } + /** + * 格子广告 隐藏 + */ + hideCustom() { + WxCustom.I.hide(); + } + /** + * 格子广告 销毁 + */ + destoryCustom() { + WxCustom.I.destory(); + } + + + + // 导出 + navigateToMiniProgram(data, type, opts?) { + let { id, appid, path, game } = data; + LogService.I.jumps(id, type); + if (!path) path = `?channel_id=${GAMEDATA.channel_id}`; + return WxApi.I.navigateToMiniProgram(appid, path, opts) + } + + + + + + + + private static instance: AdService; + static get I(): AdService { + return this.instance || (this.instance = new AdService()); + } +} \ No newline at end of file diff --git a/wxsdk/service/DataService.ts b/wxsdk/service/DataService.ts new file mode 100644 index 0000000..6091f8b --- /dev/null +++ b/wxsdk/service/DataService.ts @@ -0,0 +1,458 @@ +import SdkData from "./entity/SdkData"; +import SDKUtils from "../utils/SDKUtils"; +import StorageUtils from "../utils/StorageUtils"; +import { StorageKeys, GAMEDATA, LoginHost, GameHost, DotHost, OrderHost } from "../base/SDKConst"; +import { Gender, NetworkType } from "../base/SDKEnum"; +import WxLaunch from "../wx/WxLaunch"; + +/** + * SDK数据管理类 + */ +export default class DataService { + private _data: SdkData; + + private constructor() { + this._data = new SdkData; + } + + private setValue(key: string, val: any) { + // console.error(key,val) + if (SDKUtils.isUndefined(val)) + return; + + this._data = { + ...this._data, + [key]: val + }; + } + + get Data(): SdkData { + return this._data; + } + + setLoginData(data: _LoginInnerData) { + if (!data || Object.keys(data).length === 0) + return; + + if (data.hasOwnProperty('openId')) + this.setOpenId(data.openId); + + if (data.hasOwnProperty('channel')) + this.setChannelId(data.channel); + + if (data.hasOwnProperty('userId')) + this.setUserId(data.userId); + + if (data.hasOwnProperty('regTime')) + this.setRegTime(data.regTime); + + if (data.hasOwnProperty('token')) + this.setToken(data.token); + + if (data.hasOwnProperty('refToken')) + this.setRefToken(data.refToken); + + if (data.hasOwnProperty('expire')) + this.setExpice(data.expire); + + if (data.hasOwnProperty('isnew')) + this.setIsnew(data.isnew); + + } + + get expice() { + return this._data.expice + } + + setExpice(val: any) { + this.setValue('expire', val); + return this; + } + + get isnew() { + return this._data.isnew + } + + setIsnew(val: any) { + this.setValue('isnew', val); + return this; + } + + + get Scene() { + return this._data.scene || 0; + } + + setScene(val: any) { + this.setValue('scene', val); + return this; + } + + get ReferrerInfo() { + return this._data.referrerInfo || {}; + } + + setReferrerInfo(val: any) { + this.setValue('referrerInfo', val); + return this; + } + + get Token() { + return this._data.token || '';//|| StorageUtils.I.get(StorageKeys.SDKTokenKey) || + } + + setToken(val: any) { + this.setValue('token', val); + // StorageUtils.I.set(StorageKeys.SDKTokenKey, val); + return this; + } + + get refToken() { + return this._data.refToken + } + setRefToken(val: any) { + this.setValue('refToken', val); + return this; + } + + get OpenId() { + return this._data.openId; + } + + private setOpenId(val: any) { + this.setValue('openId', val); + // this.fixLaunchKey(val); + return this; + } + + get UserId() { + if (this._data.userId) + return this._data.userId; + else + return this.OpenId; + } + + setUserId(val: any) { + this.setValue('userId', val); + return this; + } + + get RegTime() { + return this._data.regTime; + } + + setRegTime(val: any) { + this.setValue('regTime', val); + return this; + } + + get LaunchTime() { + return this._data.launchTime; + } + + setLaunchTime(val: any) { + this.setValue('launchTime', val); + return this; + } + + get ShareId() { + return this._data.shareId; + } + + setShareId(val: number) { + this.setValue('shareId', val); + return this; + } + + get ShareKey() { + return this._data.shareKey; + } + + setShareKey(val: string) { + this.setValue('shareKey', val); + return this; + } + + get Platform(): string { + return this._data.platform; + } + + setPlatform(val: string) { + this.setValue('platform', val); + return this; + } + + // get NetworkType(): NetworkType { + // return this._data.networkType; + // } + + setNetworkType(networkType: NetworkType) { + this.setValue('networkType', networkType); + return this; + } + + get Gender(): Gender { + return this._data.gender || Gender.Unknown; + } + + setGender(val: Gender) { + this.setValue('gender', val); + return this; + } + + get ChannelId(): any { + return this._data.channelId; + } + + setChannelId(val: any) { + this.setValue('channelId', val); + return this; + } + + get QueryChannelId(): any { + return this._data.queryChannelId || 0; + } + + setQueryChannelId(val: any) { + this.setValue('queryChannelId', val); + return this; + } + + get InviteType(): any { + return this._data.inviteType || 0; + } + + setInviteType(val: any) { + this.setValue('inviteType', val); + return this; + } + + get QueryUserInviteUid(): any { + return this._data.queryUserInviteUid || 0; + } + + setQueryUserInviteUid(val: any) { + //console.warn('====> PCSDK.data.setQueryUserInviteUid', val); + this.setValue('queryUserInviteUid', val); + return this; + } + + get QueryExtData(): any { + return this._data.queryExtData || {}; + } + + setQueryExtData(val: any) { + this.setValue('queryExtData', val); + return this; + } + + get UserInviteUid(): any { + return this._data.inviteUid || 0; + } + + setUserInviteUid(val: any) { + this.setValue('inviteUid', val); + return this; + } + + // get UserState(): UserState { + // return this._data.userState || UserState.Default; + // } + + setUserState(val: any) { + this.setValue('userState', val); + return this; + } + + get Shield(): any { + return this._data.shield || StorageUtils.I.get(StorageKeys.SDKUserShieldKey) || 0; + } + + setShield(val: any) { + this.setValue('shield', val); + StorageUtils.I.set(StorageKeys.SDKUserShieldKey, val); + return this; + } + + get ShareTicket(): any { + return this._data.shareTicket; + } + + setShareTicket(val: any) { + this.setValue('shareTicket', val); + return this; + } + + get SystemId(): number { + return this._data.systemId; + } + + setSystemId() { + let systemId = this.Platform === 'android' ? 1 : 0; + this.setValue('systemId', systemId); + return this; + } + + get Authorize(): boolean { + return this._data.authorize; + } + + setAuthorize(val: boolean) { + // DebugUtils.I.dynamic('====> PCSDK data setAuthorize: ' + val); + this.setValue('authorize', val); + return this; + } + + get Version(): string { + return this._data.version; + } + + setVersion(val: string) { + this.setValue('version', val); + return this; + } + + get CdnUrl(): string { + return this._data.cdnUrl; + } + + setCdnUrl(val: string) { + this.setValue('cdnUrl', val); + return this; + } + + get EnvEnum() { + return this._data.envEnum; + } + + setEnvEnum(val: number) { + this.setValue('envEnum', val); + return this; + } + + get LoginApi() { + let path = LoginHost.Prod; + if (this.EnvEnum === 1) { + path = LoginHost.Pre + } + return path; + } + get GameApi() { + let path = GameHost.Prod; + if (this.EnvEnum === 1) { + path = GameHost.Pre + } + return path; + } + + get DotApi() { + let path = DotHost.Prod; + if (this.EnvEnum === 1) { + path = DotHost.Pre + } + return path; + } + + get OrderApi() { + let path = OrderHost.Prod; + if (this.EnvEnum === 1) { + path = OrderHost.Pre + } + return path; + } + + + get GameId() { + return GAMEDATA.game_id; + } + + get LaunchKey() { + return this._data.launchKey || StorageUtils.I.get(StorageKeys.SDKLaunchKey) || ''; + } + + fixLaunchKey(openId: string) { + if (openId) { + StorageUtils.I.set(StorageKeys.SDKLaunchKey, openId); + this.setValue('launchKey', openId); + } + return this; + } + + // getEventLaunchKey(): string { + // let key = StorageKeys.SDKEventLaunchKey; + // let launchKey = StorageUtils.I.get(key); + // if (!launchKey) { + // launchKey = StringUtils.createCode(); + // StorageUtils.I.set(key, launchKey); + // } + // return launchKey; + // } + + // get LaunchSource() { + // return this._data.launchSource; + // } + + setLaunchSource() { + let source = 0; + let { query } = WxLaunch.I.LaunchData; + if (query) { + if (query.scene) { + let arr = (decodeURIComponent(query.scene) as string).split('&'); + arr[1] && (source = 1); + } else if (query.user_invite_id || query.user_invite_uid || query.invite_unionid) + source = 1; + } else + source = 0; + + this.setValue('launchSource', source); + return this; + } + + get IsCross(): boolean { + return this._data.isCross; + } + + setIsCross(val: boolean) { + this.setValue('isCross', val); + return this; + } + + get IsDrawer(): boolean { + return this._data.isDrawer; + } + + setIsDrawer(val: boolean) { + this.setValue('isDrawer', val); + return this; + } + + get IsGuessLike(): boolean { + return this._data.isGuessLike; + } + + setIsGuessLike(val: boolean) { + this.setValue('isGuessLike', val); + return this; + } + + // get ShareTotalNum(): number { + // return IntegralService.I.getFinishShareNum(); + // } + + // get VideoTotalNum(): number { + // return IntegralService.I.getFinishVideoNum(); + // } + + // get ShareDayNum(): number { + // return LocalService.I.getDayAllShareNum(); + // } + + // get VideoDayNum(): number { + // return LocalService.I.getDayAllVideoNum(); + // } + + private static instance: DataService; + static get I(): DataService { + return this.instance || (this.instance = new DataService()); + } +} \ No newline at end of file diff --git a/wxsdk/service/GameService.ts b/wxsdk/service/GameService.ts new file mode 100644 index 0000000..dddce51 --- /dev/null +++ b/wxsdk/service/GameService.ts @@ -0,0 +1,114 @@ +import DataService from "./DataService"; +import { SDKApi } from "../http/SDKApi"; +import { __LOG__ } from "../base/SDKConst"; +import WxLogin from "../wx/WxLogin"; +import WxApi from "../wx/WxApi"; +import WxPay from "../wx/WxPay"; + + +export default class GameService { + private constructor() { + + } + + env() { + return new Promise((resolve, reject) => { + SDKApi.Version().then((data: any) => { + if (data) { + __LOG__ && console.log("版本信息", data); + let num = data.data ? data.data.env ? data.data.env : 2 : 2; + DataService.I.setEnvEnum(+num) + // DataService.I.setCdnUrl(data.res_url || ''); + } + // 设置在线参数请求 + // let isAutoOnlineUse = CfgManager.I.config.IsOnlineAutoUse; + // isAutoOnlineUse && OnlineService.I.updateOnlineConfig().then(() => EventCenter.I.emit(EventEnum.ONLINE_SUCCESS)); + resolve(data); + }).catch((err: any) => { + reject(err); + }); + }); + } + + login(isAuthorize = false) { + return WxLogin.I.login(isAuthorize); + } + + + saveData(key: string, content: string) { + return SDKApi.saveData({ key, content, uid: DataService.I.UserId, token: DataService.I.Token }); + } + + getData(key: string) { + return SDKApi.getData({ key, uid: DataService.I.UserId, token: DataService.I.Token }); + } + + rankAdd(key, value) { + return SDKApi.rankAdd({ typ: key, fraction: value, uid: DataService.I.UserId, token: DataService.I.Token }) + } + + rankList(typ, t, isRankData) { + return SDKApi.rankList({ typ, t, rankdata: isRankData ? 1 : 0, uid: DataService.I.UserId, token: DataService.I.Token }) + } + + totalrankAdd(key, value, type, sort) { + return SDKApi.totalrankAdd({ typ: key, sort, t: type, fraction: value, uid: DataService.I.UserId, token: DataService.I.Token }) + } + + totalrankList(typ, t, isRankData, sort) { + return SDKApi.totalrankList({ typ, t, sort, rankdata: isRankData ? 1 : 0, uid: DataService.I.UserId, token: DataService.I.Token }) + } + adList(adtyp) { + return SDKApi.adList({ adtyp, uid: DataService.I.UserId, token: DataService.I.Token }) + } + + + subScribe(tmplIds: Array, ids: Array): Promise { + return new Promise((resolve, reject) => { + WxApi.I.subscribeMessage(tmplIds) + .then((ret: any) => { + ret = ret || {}; + let acceptKeys = tmplIds.filter((idKey: string) => ret[idKey] && ret[idKey] === 'accept'); + if (!acceptKeys.length) { + reject({ errCode: 0, errMsg: '点击取消订阅' }); + } else { + let uid = +DataService.I.UserId; + let token = DataService.I.Token + ''; + let openid = DataService.I.OpenId + ''; + let id = ids//JSON.stringify() + SDKApi.subscribe({ + uid, + token, + openid, + status: 1, + id, + }) + } + // GameService.I.subScribe(acceptKeys) + // .then(() => { + // resolve(ret); + // DebugUtils.I.dynamic('====> PCSDK subScribe订阅消息成功', ret); + // }) + // .catch((err) => reject(err || { errCode: 0, errMsg: 'GameApi subScribe请求错误' })); + + }) + .catch(err => reject(err)); + }); + // return WxApi.I.subscribeMessage(template_ids); + } + + /** + * 支付 + * @param params + * @param opts + * @returns + */ + pay(params: { money: number; source:string}, opts: any = {}) { + return WxPay.I.pay(params, opts); + } + + private static instance: GameService; + static get I(): GameService { + return this.instance || (this.instance = new GameService()); + } +} \ No newline at end of file diff --git a/wxsdk/service/LogService.ts b/wxsdk/service/LogService.ts new file mode 100644 index 0000000..f51960c --- /dev/null +++ b/wxsdk/service/LogService.ts @@ -0,0 +1,461 @@ +import DataService from "./DataService"; +import { __LOG__, GAMEDATA } from "../base/SDKConst"; +import { SDKApi } from "../http/SDKApi"; +import WxSystem from "../wx/WxSystem"; +import StorageUtils from "../utils/StorageUtils"; +import { DOT_SHARE_TYPE, DOT_AD_TYPE, DOT_AD_STATUS, AdType } from "../base/SDKEnum"; +import DateUtils from "../utils/DateUtils"; + +export default class LogService { + // 曝光数据 + private exposureArr: any; + // 资源变更统计 + private logResArr: _LogResData[]; + // 关卡统计 + private logLevelArr: _LogLevelData[]; + // 是否已经处理请求了shareList接口 + private isHandledShare: boolean; + private timeoutShareId: any; + // reg接口是否已经返回数据状态 + private regFinishState: number; + + private lastReqBannerTime: any; + private lastReqBannerData: any; + + private queue: Array = []; + + private constructor() { + this.exposureArr = {}; + this.logResArr = []; + this.logLevelArr = []; + this.regFinishState = 0; + this.isHandledShare = false; + this.timeoutShareId = 0; + this.lastReqBannerTime = {}; + this.lastReqBannerData = {}; + } + + setRegFinishState(state: number) { + this.regFinishState = state; + } + + private get SystemId() { + return DataService.I.SystemId; + } + + private get LaunchTime() { + return DataService.I.LaunchTime; + } + + private get LaunchKey() { + return DataService.I.LaunchKey; + } + + // private get EventLaunchKey() { + // return DataService.I.getEventLaunchKey(); + // } + + // private get LaunchSource() { + // return DataService.I.LaunchSource; + // } + + private get Scene() { + return DataService.I.Scene; + } + + private get RegTime() { + return DataService.I.RegTime; + } + + private get UserId() { + return DataService.I.UserId; + } + + private get OpenId() { + return DataService.I.OpenId; + } + + private get UserInviteUid() { + return DataService.I.UserInviteUid; + } + + private get QueryUserInviteUid() { + return DataService.I.QueryUserInviteUid; + } + + private get QueryExtData() { + return DataService.I.QueryExtData; + } + + private get Shield() { + return DataService.I.Shield; + } + + + private get shareKey() { + return DataService.I.ShareKey; + } + private get shareId() { + return DataService.I.ShareId; + } + + fixLaunchKey(launch_key: string) { + DataService.I.fixLaunchKey(launch_key); + } + + //是否登录 + isLogin: boolean = false; + /** + * 内部游戏调用设置:是否新老用户 + * @param data + */ + setLogind(data: _LoginInnerData) { + DataService.I.setLoginData(data); + this.isLogin = true; + if (this.queue.length > 0) { + for (let i = 0; i < this.queue.length; i++) { + let fun = this.queue[i]; + fun(); + } + } + this.queue = []; + } + + async dot(eventkey: string, options = {}) { + //2020-12-31 改二级key + let itemkey = ''; + let itemvalue = ''; + if (JSON.stringify(options) != '{}' && typeof options === 'object') { + for (let k in options) { + if (itemkey === '') { + itemkey = k; + itemvalue = options[k]; + } + } + } + // !this.isLogin && await PromiseSame.get(SDKTools.login, 'login') + let fun = () => SDKApi.dot({ + ...this.buildParams(), + eventkey, + value: JSON.stringify(options), + itemkey, + itemvalue + }); + this.checkLogin(fun); + } + + /** + * 关卡开始 + * @param stageid 关卡ID, 必须1.1、12.2 “.”前面代表模式 后面代表关卡 + * @param stagename 关卡名称,格式:"xx模式-第x关" + * @param pattern 模式名称,格式:"xx模式" + */ + async levelStart(stageid, stagename, pattern) { + let fun = () => SDKApi.level({ + ...this.buildParams(), + stageid, + stagename, + pattern, + typ: 1 + }) + this.checkLogin(fun); + } + + /** + * 关卡进行中 + * @param stageid 关卡ID, 必须1.1、12.2 “.”前面代表模式 后面代表关卡 + * @param stagename 关卡名称,格式:"xx模式-第x关" + * @param pattern 模式名称,格式:"xx模式" + * @param event 事件 tools:使用道具 award:奖励 + * @param params_id 道具ID + * @param params_name 道具名称 + * @param params_count 道具数量 + * @param params_desc 道具描述 + */ + async levelRunning(stageid, stagename, pattern, event, params_id, params_name, params_count, params_desc?) { + let fun = () => SDKApi.level({ + ...this.buildParams(), + stageid, + stagename, + pattern, + event, + params_id, + params_name, + params_count, + params_desc, + typ: 2 + }) + this.checkLogin(fun); + } + + /** + * 关卡结束 + * @param stageid 关卡ID, 必须1.1、12.2 “.”前面代表模式 后面代表关卡 + * @param stagename 关卡名称,格式:"xx模式-第x关" + * @param pattern 模式名称,格式:"xx模式" + * @param event complete:成功 fail:失败 + * @param times 时间 + * @param perc 失败时的完成进度 (浮点数) + */ + async levelEnd(stageid, stagename, pattern, event, times,perc?) { + let fun = () => SDKApi.level({ + ...this.buildParams(), + stageid, + stagename, + pattern, + event, + times, + perc, + typ: 3 + }) + this.checkLogin(fun); + } + + /** + * loading资源完成,新版加载游戏资源完成时调用 + */ + async loadingFinish() { + let fun = () => SDKApi.loadingFinish({ + ...this.buildParams() + }) + this.checkLogin(fun); + // 30秒及时 + this.startStay() + } + + async active() { + let fun = () => SDKApi.active({ + ...this.buildParams(), + isnew: 0, + sharekey: this.shareKey, + shareid: this.shareId + }) + this.checkLogin(fun); + } + + + /** + * 用户log - 退出登录 + * @param time_len 用户在线时长,单位毫秒 + */ + logOut(time_len: number) { + let fun = () => SDKApi.logOut({ + ...this.buildParams(), + times: time_len + }) + this.checkLogin(fun); + } + + /** + * + * @param sharekey + * @param type 1分享 2点击 + */ + share(sharekey: string, shareid: number, type: DOT_SHARE_TYPE) { + let fun = () => SDKApi.share({ + ...this.buildParams(), + typ: type, + sharekey, + shareid, + shareuid: type === DOT_SHARE_TYPE.click ? this.QueryUserInviteUid : '', //点击才要分享id + }) + this.checkLogin(fun); + } + /** + * adtyp:类型 1banner2激励视频3插屏4格子5原生模板 + adstatus: 1请求 2返回 3展示 4点击 5完成 6中断 7失败 + banner:1,3,6,7 + video:1,5,6,7 + interstitial:1 + grid:1,3,6,7 + custom:1,3,6,7 + * @param sharekey + * @param adid + * @param adtyp + * @param adstatus + */ + adStat(videokey: string, adid: string, adtyp: DOT_AD_TYPE, adstatus: DOT_AD_STATUS) { + let fun = () => SDKApi.adStat({ + ...this.buildParams(), + videokey, + adplat: "1", + adid, + adtyp, + adstatus + }) + this.checkLogin(fun); + } + + private stayInterval + private startStay() { + this.stayInterval = setInterval(this.stayFun.bind(this), 1000); + } + private stayFun() { + let newUserTime = StorageUtils.I.get("newUserTime") || 0; + newUserTime++; + StorageUtils.I.set("newUserTime", newUserTime) + let newUserDot = StorageUtils.I.get("newUserDot") || 0; + if (newUserDot === 1) clearInterval(this.stayInterval); + // console.log("newUserTime", newUserTime, newUserDot) + if (newUserTime >= 30) { + StorageUtils.I.set("newUserDot", 1); + this.loadingFinishStay(30); + clearInterval(this.stayInterval); + } + } + async loadingFinishStay(times) { + let fun = () => { + if (DataService.I.RegTime === DateUtils.today) { + SDKApi.stay({ + ...this.buildParams(), + times, + }) + } + } + this.checkLogin(fun); + } + + /** + * 用户停留30s打点 2021-1-6 废弃 + */ + stay(times) { + // let fun = () => SDKApi.stay({ + // ...this.buildParams(), + // times, + // }) + // this.checkLogin(fun); + } + + /** + * 关键行为打点 + */ + behaviors() { + let fun = () => SDKApi.behaviors({ + ...this.buildParams(), + }) + this.checkLogin(fun); + } + + checkLogin(fun) { + if (this.isLogin) { + fun(); + } else { + this.queue.push(fun) + } + } + + /** + * 跳转打点 + */ + jumps(id, adtyp) { + let fun = () => SDKApi.jumps({ + ...this.buildParams(), + adid: 0, + adplat: 1, + adtyp, + typ: 1, + times: 1, + id + }) + this.checkLogin(fun); + } + /** + * 曝光打点 + */ + bannerExposure2(id, adtyp, times) { + let fun = () => SDKApi.jumps({ + ...this.buildParams(), + adid: 0, + adplat: 1, + adtyp, + typ: 2, + times, + id + }) + this.checkLogin(fun); + } + + /** + * 曝光累计 + */ + addExposure(type: AdType | number, data: Array) { + if (!data) return + data.forEach(item => { + let { id } = item; + if (this.exposureArr.hasOwnProperty(type)) { + let d = this.exposureArr[type]; + if (d.hasOwnProperty(id)) { + this.exposureArr[type][id]++; + } else { + this.exposureArr[type] = { + ...this.exposureArr[type], + [id]: 1 + } + } + } else { + let obj = { + [type]: { [id]: 1 } + } + this.exposureArr = { + ...this.exposureArr, + ...obj + } + } + }) + // console.log("this.exposureArr", this.exposureArr) + } + + /** + * 曝光打点 + */ + bannerExposure() { + if (JSON.stringify(this.exposureArr) === '{}') return; + let obj = JSON.parse(JSON.stringify(this.exposureArr)); + this.exposureArr = {}; + for (let type in obj) { + let data = obj[type]; + for (let key2 in data) { + this.bannerExposure2(key2, type, data[key2]) + } + } + + } + + + /** + * 构建登录/弱登录公用参数 + */ + private buildParams() { + let APIVersion = '0.6.0';//固定值 + let gameid = GAMEDATA.game_id; + let channel = DataService.I.ChannelId; + let brand = WxSystem.I.brand; + let model = WxSystem.I.model; + let version = WxSystem.I.version; + let system = WxSystem.I.system; + let platform = WxSystem.I.platform; + let sdkversion = WxSystem.I.SDKVersion; + let scene = DataService.I.Scene + ''; + let uid = this.UserId; + let env = DataService.I.EnvEnum === 1 ? 'pre' : 'prod'; + return { + gameid, + channel, + APIVersion, + brand, + model, + version, + system, + platform, + sdkversion, + scene, + uid, + env + }; + } + + private static instance: LogService; + static get I(): LogService { + return this.instance || (this.instance = new LogService()); + } +} \ No newline at end of file diff --git a/wxsdk/service/OnlineService.ts b/wxsdk/service/OnlineService.ts new file mode 100644 index 0000000..af964c6 --- /dev/null +++ b/wxsdk/service/OnlineService.ts @@ -0,0 +1,125 @@ +import LogService from './LogService'; +import DataService from './DataService'; +import { __LOG__ } from '../base/SDKConst'; +import { SDKApi } from '../http/SDKApi'; + +/** + * 在线参数服务类 + */ +export default class OnlineService { + private isDebug: boolean; + private data: any; + + private constructor() { + this.isDebug = false; + } + + setData(data){ + if(!data) return + this.data = data; + } + + // get OnlineKey() { + // return Keys.SDKOnlienKey.replace('env', DataService.I.EnvEnum.toString()); + // } + + /** + * 设置debug模式:true表示进入游戏后,实时获取后台在线参数 false表示缓存10分钟,10分钟后进入刷新最新参数信息 + * 默认在非Debug模式下,是受请求时间间隔的限制的,即两次请求的时间间隔应该大于10分钟;而在Debug模式下,是不受时间间隔限制的。 + * 开发模式下建议设置为true,发布提审设置为false + * @param isDebug 是否debug模式 + */ + setDebugMode(isDebug: boolean) { + this.isDebug = isDebug; + } + + /** + * 请求在线参数 + * 在游戏启动时候请求在线参数,最早的获取得的在线参数。 + */ + updateOnlineConfig() { + return new Promise((resolve, reject) => { + this.reqOnlineConfig() + .then(ret => resolve(ret)) + .catch(err => reject(err)); + }); + } + + getParams(key: string) { + if (!key) { + __LOG__ && console.warn('SDK OnlineService 请传递key!'); + return null; + } + + let data = this.data; + if (!data) + return null; + + if (!data.hasOwnProperty(key)) { + __LOG__ && console.warn('SDK OnlineService 请在后台配置在线参数key:【' + key + '】'); + return null; + } + + return data[key]; + } + + getParamsObj(...args: any[]): any { + let ret = this.getParams(args[0]); + if (args.length === 2 && !ret) + return args[1]; + + if (!ret) + return ret; + + return JSON.parse(ret); + } + + getParamsInt(...args: any[]): number { + let ret = this.getParams(args[0]); + if (args.length === 2 && !ret) + return args[1]; + + return +ret; + } + + getParamsString(...args: any[]): string { + let ret = this.getParams(args[0]); + if (args.length === 2 && !ret) + return args[1]; + + return ret; + } + + private reqOnlineConfig() { + return new Promise((resolve, reject) => { + SDKApi.getConfig() + .then((ret: any) => { + if (this.data = ret && ret.data && ret.data.config) { + this.data = ret.data.config + } + // console.log("OnlineConfig",ret) + resolve(this.data); + }) + .catch(err => { + reject(err); + }); + }); + } + + // private initParams() { + // let adObj = this.getParamsObj(OnlineKeys.ADCrossPromotion, { + // 'Cross': 0, // 交叉推广 + // 'Drawer': 0, // 抽屉广告位 + // 'GuessLike': 0 // 猜你喜欢 + // }); + // DataService.I + // .setIsCross(adObj.Cross === 1) + // .setIsDrawer(adObj.Drawer === 1) + // .setIsGuessLike(adObj.GuessLike === 1); + // } + + private static instance: OnlineService; + static get I(): OnlineService { + return this.instance || (this.instance = new OnlineService()); + } +} diff --git a/wxsdk/service/ShareVideoService.ts b/wxsdk/service/ShareVideoService.ts new file mode 100644 index 0000000..ec4281c --- /dev/null +++ b/wxsdk/service/ShareVideoService.ts @@ -0,0 +1,419 @@ +import { ShareVideoType, ShareVideoFrom } from "../base/SDKEnum"; +import RandomUtils from "../utils/RandomUtils"; +import ShareData from "./entity/ShareData"; +import SDKShare from "../share/SDKShare"; +import { __LOG__, GAMEDATA } from "../base/SDKConst"; +import { SDKApi } from "../http/SDKApi"; +import DataService from "./DataService"; +import SDKVideo from "../share/SDKVideo"; + +export default class ShareVideoService { + private forwardKey?: string; + private shareObjs: { [key: string]: Array }; + + private constructor() { + this.forwardKey = 'forward'; + this.shareObjs = { + 'default': [GAMEDATA.default_share] + } + } + + /** + * 设置右上角转发key + * @param shareKey 右上角转发key + */ + setForwardKey(shareKey: string) { + this.forwardKey = shareKey; + this.forward(this.forwardKey); + } + + async init() { + let data = await SDKApi.ShareList(); + this.setShareVideoData(data); + // console.log(JSON.stringify(data)) + SDKShare.I.updateShareMenu(true);//打开群分享 + + if (GAMEDATA.shareMessageToFriend.scene > 0) { + wx.setMessageToFriendQuery({ shareMessageToFriendScene: GAMEDATA.shareMessageToFriend.scene }) + } + if (this.forwardKey) this.forward(this.forwardKey); + } + + preload = true; + preloadVideo() { + SDKVideo.I.preloadVideo(GAMEDATA.videoAd); + } + + /** + * 分享 + * @param shareKey + * @param params + * @param opts 目前支持4个key 1,title自定义分享标题 2,img_url自定义分享图片 3,share_type(不走后台配置写死走视频or分享。1分享2视频3无视频则分享)4,closeSimulate是否关闭模拟分享 + */ + share(shareKey: string, params: any = {}, opts: any = {}): Promise { + return new Promise((resolve, reject) => { + let { id, typ, content, key, icon, videoid } = this.getShareVideoData(shareKey); + let _params = { + title: content, + imageUrl: icon, + share_id: id, + query: this.createQuery({ share_key: key, share_id: id }), + }; + if (opts) { + if (opts.title) _params.title = opts.title; + if (opts.img_url) _params.imageUrl = opts.img_url; + if (opts.share_type) typ = opts.share_type; + } + // console.log("share_query",JSON.stringify(_params), _params.query); + switch (+typ) { + case ShareVideoType.Video: + SDKVideo.I.show(shareKey, videoid).then(success => { + resolve(success) + }).catch(err => { + reject(err); + }) + break; + case ShareVideoType.VideoToShare: + SDKVideo.I.show(shareKey, videoid).then(success => { + resolve(success) + }).catch(err => { + if (err.code !== 1000 && err.code !== 1003) {//1000关闭1003正在播放 + SDKShare.I.share(shareKey, _params, opts).then(success => { + resolve(success) + }).catch(err2 => { + reject(err2) + }) + } else { + reject(err) + } + }) + break; + case ShareVideoType.Share: + SDKShare.I.share(shareKey, _params, opts).then(success => { + resolve(success) + }).catch(err => { + reject(err) + }) + break; + case ShareVideoType.None: + default: + resolve({}) + break; + } + // share_desc = opts.shareTitle || share_desc; + // share_desc = StringUtils.stringFormat(share_desc, opts.formater); + // let shareMsg = { + // title: share_desc, + // imageUrl: opts.shareImg || share_icon, + // query + // }; + // // __LOG__ && console.error(`SDK ShareVideoService ${opts.shareForward ? 'forward 右上角分享:' : 'share 主动拉起分享:'}`); + // // __LOG__ && console.error(`SDK ShareVideoService 分享参数对象shareMsg: ${JSON.stringify(shareMsg)}`); + // // __LOG__ && console.error('SDK ShareVideoService 分享扩展参数', opts); + // DebugUtils.I.dynamic(`====> PCSDK ShareVideoService share 分享query: ${query}`); + // let success = (ret: any) => { + // // __LOG__ && console.error(`SDK ShareVideoService ${opts.shareForward ? 'forward 右上角分享:' : 'share 主动拉起分享:'} 成功回调: ${share_id}`); + // if (!opts.__GROUP) { + // // 不是群分享的情况下,进行统计,群分享需要验证 + // TempService.I.add(TempDataKeys.ShareSuccess, 1); + // share_id && LogService.I.tacticShare(share_id, share_key, TacticType.ShareSuccess); + // } + // resolve({ ...ret, share_id, share_key: shareKey }); + // }; + // let fail = (err: any) => { + // // __LOG__ && console.error(`SDK ShareVideoService ${opts.shareForward ? 'forward 右上角分享:' : 'share 主动拉起分享:'} 失败回调: ${share_id}`); + // if (!opts.__GROUP) { + // share_id && LogService.I.tacticShare(share_id, share_key, TacticType.ShareInterrupt); + // } + // reject({ ...err, share_id, share_key: shareKey }); + // }; + // if (opts.shareForward) + // // 右上角转发 + // Platform.I.forward(shareMsg, { + // ...opts, + // success, + // fail, + // context: this + // }); + // else + // // 普通分享 + // Platform.I.share(shareMsg, opts) + // .then((ret: any) => success(ret)) + // .catch((err: any) => fail(err)); + }); + } + + /** + * 右上角转发 + */ + forward(shareKey?: string) { + let data = this.getShareVideoData(shareKey ? shareKey : this.forwardKey); + let params = { + title: data.content, + imageUrl: data.icon, + query: this.createQuery({ share_key: data.key, share_id: data.id }), + }; + // console.log("forward", JSON.stringify(params)); + SDKShare.I.forward(params); + } + + /** + * 分享自动入口 + * @param shareKey + * @param opts { + * type: 当shareType为VideoAndShare时候,该参数有用,0:分享 1:视频 + * context: 函数执行上下文 + * fail: Function 失败函数 + * success: Function 成功函数 + * } + */ + // dispatch(shareKey: string, opts: any = {}, queryObj: { [key: string]: number | string } = {}) { + // let shareData = this.getShareVideoData(shareKey); + // let { share_open, share_wxad_id } = shareData; + // opts = { + // ...opts, + // share_wxad_id, + // __ShareData__: shareData + // }; + // this.dispatchType(+share_open, shareKey, opts, queryObj); + // } + + shareDispatch(shareKey: string, opts: any = {}, queryObj: { [key: string]: number | string } = {}) { + // this.dispatch(shareKey, opts, queryObj); + } + + /** + * 根据分享类型进行处理 + * @param shareType + * @param shareKey + * @param opts { + * type: 当shareType为VideoAndShare时候,该参数有用,0:分享 1:视频 + * context: 函数执行上下文 + * fail: Function 失败函数 + * success: Function 成功函数 + * } + */ + dispatchType(shareType: ShareVideoType, shareKey: string, opts: any = {}, queryObj: { [key: string]: number | string } = {}) { + // if (Platform.IsWx && OnlineService.I.getParamsInt(OnlineKeys.ShareUnlock, 1) !== 1) + // shareType = ShareVideoType.None; + + // let share_wxad_id = opts.share_wxad_id || (opts.__ShareData__ || this.getShareVideoData(shareKey) || {}).share_wxad_id; + // switch (shareType) { + // case ShareVideoType.None: // 无分享 + // this.handleSuccess(opts, shareType, ShareVideoFrom.None, null); + // break; + + // case ShareVideoType.Share: // 同步分享 + // // 同步分享点:是否开启后台配置中如果存在视频ID,则自动切换为视频点 + // if (opts.shareAutoVideo && share_wxad_id) { + // this.dispatchType(ShareVideoType.VideoToShare, shareKey, opts, queryObj); + // break; + // } + // this.group(shareKey, queryObj, opts) + // .then(ret => this.handleSuccess(opts, shareType, ShareVideoFrom.Share, ret)) + // .catch(err => this.handleFail(opts, shareType, ShareVideoFrom.Share, err)); + // break; + + // case ShareVideoType.ShareAysnc: // 异步分享 + // break; + + // case ShareVideoType.ShareIntegral: // 分享积分 + // // 是否开启分享积分,且配置为分享积分 + // if (IntegralService.I.IsOpen) { + // let data = IntegralService.I.convert(); + // if (data) { + // opts.shareIntegralData = data; + // this.dispatchType(data.shareType, shareKey, opts, queryObj); + // } else { + // // 超过了现在,直接执行失败:今日已达分享上限次数,请明日再来 + // this.handleFail(opts, shareType, ShareVideoFrom.Share, { ...ShareVideoError.ShareOverLimit }); + // } + // } else { + // // 如果配置了分享积分,但是未开启开关,则推:无视频则分享 + // this.dispatchType(ShareVideoType.VideoToShare, shareKey, opts, queryObj); + // } + // break; + + // case ShareVideoType.Video: // 看视频 + // Platform.I.video(shareKey, share_wxad_id) + // .then(ret => this.handleSuccess(opts, shareType, ShareVideoFrom.Video, ret)) + // .catch((err: any) => this.handleFail(opts, shareType, ShareVideoFrom.Video, err)); + // break; + + // case ShareVideoType.VideoToShare: // 无视频则分享 + // if (!share_wxad_id) { + // this.group(shareKey, queryObj, opts) + // .then(ret => this.handleSuccess(opts, shareType, ShareVideoFrom.Share, ret)) + // .catch(err => this.handleFail(opts, shareType, ShareVideoFrom.Share, err)); + // return; + // } + // Platform.I.video(shareKey, share_wxad_id) + // .then((ret) => this.handleSuccess(opts, shareType, ShareVideoFrom.Video, ret)) + // .catch((err: any) => { + // // 拉取视频失败/视频UID不存在/微信版本过低,暂不支持看视频,自动切换到分享 + // let { VideoFail, VideoInvalid, VideoNotOpen } = ShareVideoError; + // if (err && (err.code === VideoFail.code || err.code === VideoInvalid.code || err.code === VideoNotOpen.code)) + // this.group(shareKey, queryObj, opts) + // .then(ret => this.handleSuccess(opts, shareType, ShareVideoFrom.Share, ret)) + // .catch(err => this.handleFail(opts, shareType, ShareVideoFrom.Share, err)); + // else + // this.handleFail(opts, shareType, ShareVideoFrom.Video, err); + // }); + // break; + + // case ShareVideoType.VideoAndShare: // 看视频 + // if (share_wxad_id && opts.type === 1) { + // Platform.I.video(shareKey, share_wxad_id) + // .then(ret => this.handleSuccess(opts, shareType, ShareVideoFrom.Video, ret)) + // .catch((err: any) => this.handleFail(opts, shareType, ShareVideoFrom.Video, err)); + // } else { + // this.group(shareKey, queryObj, opts) + // .then(ret => this.handleSuccess(opts, shareType, ShareVideoFrom.Share, ret)) + // .catch(err => this.handleFail(opts, shareType, ShareVideoFrom.Share, err)); + // } + // break; + // } + } + + shareWithType(shareType: ShareVideoType, shareKey: string, opts: any = {}, queryObj: { [key: string]: number | string } = {}) { + this.dispatchType(shareType, shareKey, opts, queryObj); + } + + /** + * 分享id + * @param shareKey + */ + getShareVideoID(shareKey: string): string { + let shareData = this.getShareVideoData(shareKey); + return shareData.id; + } + /** + * videoid + * @param shareKey + */ + getShareVideoID2(shareKey: string): string { + let shareData = this.getShareVideoData(shareKey); + return shareData.videoid; + } + + getShareVideoType(shareKey: string): ShareVideoType { + let shareData = this.getShareVideoData(shareKey); + let shareType: ShareVideoType = +shareData.typ; + // // 木有开启分享,直接返回None + // if (Platform.IsWx && OnlineService.I.getParamsInt(OnlineKeys.ShareUnlock, 1) !== 1) + // return ShareVideoType.None; + + // // 后台配置视频,判断是否加载出错过,出错返回分享,否则直接返回 + // if (shareType === ShareVideoType.Video || shareType === ShareVideoType.VideoAndShare || shareType === ShareVideoType.VideoToShare) { + // // 检测视频加载失败 + // if (Platform.I.isVideoErrored()) + // return ShareVideoType.Share; + // else + // return shareType; + // } + + // // 是否开启分享积分,且配置为分享积分 + // if (shareType === ShareVideoType.ShareIntegral && IntegralService.I.IsOpen) { + // let data = IntegralService.I.convert(); + // if (data) + // return data.shareType; + // } + return shareType; + } + + getShareType(shareKey: string): ShareVideoType { + return this.getShareVideoType(shareKey); + } + + getType(shareKey: string): ShareVideoType { + return this.getShareVideoType(shareKey); + } + + private handleSuccess(opts: any, shareType: ShareVideoType, from: ShareVideoFrom, ret: any) { + // // 判断分享规则 + // if (IntegralService.I.IsOpen && opts.shareIntegralData && from === ShareVideoFrom.Share) { + // let shareNum = IntegralService.I.IntegralShareNum; + // if (shareNum === 0) { + // // 第1次分享 + // IntegralService.I.setIntegralShareNum(); + // IntegralService.I.resetShareRatio(); + // __LOG__ && console.error(`SDK ShareVideoService 分享积分 第1次分享: 初始来源:${shareType}, 类型:${from}, 分享次数:${shareNum}`); + // // 设定:第一次强制失败 + // // return this.handleFail(opts, shareType, from, { ...ShareVideoError.ShareRuleFail }, true); + // } else { + // // 第n次分享 + // IntegralService.I.setIntegralShareNum(); + // IntegralService.I.addShareRatio(); + + // let shareRatio = IntegralService.I.ShareRatio; + // let radomRatio = +Math.random().toFixed(2); + // __LOG__ && console.error(`SDK ShareVideoService 分享积分 第${shareNum}次分享: 初始来源:${shareType}, 类型:${from}, 分享次数:${shareNum}, 随机概率:${radomRatio}, 失败概率:${shareRatio}`); + // if (radomRatio <= shareRatio) { + // __LOG__ && console.error(`SDK ShareVideoService 分享积分 触发失败规则:分享强制失败, radomRatio <= shareRatio: ${radomRatio} <= ${shareRatio}`); + // return this.handleFail(opts, shareType, from, { ...ShareVideoError.ShareRuleFail }, true); + // } + // } + + // // 是否同步分享积分数据 + // let { intergral, isAsyncNum } = opts.shareIntegralData; + // isAsyncNum && LocalService.I.saveVideoOverShareNum(intergral); + // __LOG__ && isAsyncNum && console.error(`SDK ShareVideoService 分享积分 触发视频看完后,再次分享次数限制更新成功,成功后数量:${LocalService.I.getVideoOverShareNum(intergral)}`); + // } + + // let success = opts.success || function () { }; + // let context = opts.context || this; + // typeof success === 'function' && success.call(context, from, ret); + // (from === ShareVideoFrom.Share) && TempService.I.add(TempDataKeys.ShareSuccess, 1); + } + + private handleFail(opts: any, shareType: ShareVideoType, from: ShareVideoFrom, err: any, isFromIntegral?: boolean) { + // if (err instanceof Error) return; + // // 判断来自分享积分规则,则重置分享概率 + // if (IntegralService.I.IsOpen && opts.shareIntegralData && from === ShareVideoFrom.Share) { + // __LOG__ && console.error(`SDK ShareVideoService 强制失败分享,回滚到: ${IntegralService.I.ShareRatioInit}`); + // IntegralService.I.resetShareRatio(); + // } + // let fail = opts.fail || function () { }; + // let context = opts.context || this; + // fail && fail.call(context, from, err); + } + + private setShareVideoData(data: any) { + (data.data || []).forEach((item: any) => { + this.shareObjs[item.key] = this.shareObjs[item.key] || []; + this.shareObjs[item.key].push(item); + }); + } + + private getShareVideoData(shareKey: string): ShareData { + let list: Array = this.shareObjs[shareKey]; + if (!list) { + list = this.shareObjs.default; + // let shareData: ShareData = CfgManager.I.config.ShareData; + // if (!shareData) throw new TypeError('SDK ShareVideoService - 请在config.js中配置ShareData'); + // let share_open = shareData.share_wxad_id ? ShareVideoType.Video : ShareVideoType.Share; + // return { ...shareData, share_id: 99999 + '', share_key: shareKey, share_open }; + } + let index = RandomUtils.rand(0, list.length); + return list[index]; + } + + private createQuery(params = {}): string { + params = { + ...params, + channel_id: GAMEDATA.channel_id, + user_invite_uid: DataService.I.UserId + }; + let query = ''; + for (let key in params) { + query.length && (query += '&'); + query += `${key}=${params[key]}`; + } + return query; + } + + private static instance: ShareVideoService; + static get I(): ShareVideoService { + return this.instance || (this.instance = new ShareVideoService()); + } +} diff --git a/wxsdk/service/entity/SdkData.ts b/wxsdk/service/entity/SdkData.ts new file mode 100644 index 0000000..ebef0eb --- /dev/null +++ b/wxsdk/service/entity/SdkData.ts @@ -0,0 +1,82 @@ +import { Gender, NetworkType } from "../../base/SDKEnum"; + +export default class SdkData { + public token: string; // 游戏token + public refToken: string; // 刷新凭证,可以在token失效前刷新token有效期 + public expice: number; // token失效时间 + public isnew: number; // 是否新用户1新用户0老用户 + public scene: string; // 微信场景 + public openId: string; // 用户openid + public userId: number; // 用户id + public regTime: string; // 注册账号时间 + public launchTime: number; // 启动时间 + public shareId: number; // 分享id + public shareKey: string; // 分享key + public platform: string; // 手机平台 ios, android + public networkType: NetworkType; // 网络类型 + public gender: Gender; // 性别 + public channelId: number; // 渠道ID, 原始最终渠道id,会被后台返回的渠道覆盖 + public queryChannelId: number; // 渠道ID, query渠道id,不会被后台返回的渠道覆盖 + public inviteType: any; // 会话来源类型 + public shareTicket: string; // 会话群分享ticket + public systemId: number; // 系统类型: 0 iOS, 1 Android + public launchKey: string; // 启动key,自动生成并缓存 + public launchSource: number; // 启动来源 + public authorize: boolean; // 是否已经登录授权 + public referrerInfo: any; // referrerInfo信息 + public version: string; // 服务端接口版本号 + public cdnUrl: string; // cdn动态地址 + public envEnum: number; // api环境变量 1测试 2正式 + public inviteUid: number; // 用户来源邀请者ID,login后获取得到 + public queryUserInviteUid: number; // 分享获取得到的query的邀请者id + public queryExtData: object; // query扩展数据 + public shield: number; // 是否开启屏蔽 + public configParams: any; // 启动配置功能参数 + public userState: UserState; // 启动配置功能参数 + public isCross: boolean; // 是否开启交叉悬浮框推广位 + public isDrawer: boolean; // 是否开启交叉抽屉推广位 + public isGuessLike: boolean; // 是否开启交叉猜你喜欢推广位 + + constructor() { + this.token = ''; + this.refToken = ''; + this.expice = 0; + this.isnew = 0; + this.scene = ''; + this.openId = ''; + this.userId = 0; + this.regTime = ''; + this.launchTime = 0; + this.shareId = 0; + this.shareKey = ''; + this.platform = ''; + this.networkType = NetworkType.Unknown; + this.gender = Gender.Unknown; // 未知 + this.channelId = 0; + this.queryChannelId = 0; + this.queryExtData = {}; + this.inviteType = 0; + this.shareTicket = ''; + this.systemId = 0; + this.launchKey = ''; + this.launchSource = -1; + this.authorize = false; + this.cdnUrl = ''; + this.envEnum = 2; + this.version = '1.0'; + this.inviteUid = 0; + this.queryUserInviteUid = 0; + this.shield = 0; + this.configParams = null; + this.userState = UserState.Default; + this.isCross = false; + this.isDrawer = false; + this.isGuessLike = false; + } +} + +export enum UserState { + Default = 0, + Old, + New +} \ No newline at end of file diff --git a/wxsdk/service/entity/SdkData.ts.meta b/wxsdk/service/entity/SdkData.ts.meta new file mode 100644 index 0000000..9e6fe1a --- /dev/null +++ b/wxsdk/service/entity/SdkData.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "f09e38ff-4d68-417e-8904-949127e4d321", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/wxsdk/service/entity/ShareData.ts b/wxsdk/service/entity/ShareData.ts new file mode 100644 index 0000000..3365e2b --- /dev/null +++ b/wxsdk/service/entity/ShareData.ts @@ -0,0 +1,21 @@ +import { ShareVideoType } from "../../base/SDKEnum"; + +export default class ShareData { + public content: string; + public icon: string; + public id: string; + public key: string; + public title: string; + public typ: number; + public videoid: string; + + constructor() { + this.content = ''; + this.icon = ''; + this.id = ''; + this.key = ''; + this.title = ''; + this.typ = ShareVideoType.None; + this.videoid = ''; + } +} \ No newline at end of file diff --git a/wxsdk/service/entity/ShareData.ts.meta b/wxsdk/service/entity/ShareData.ts.meta new file mode 100644 index 0000000..f510aaa --- /dev/null +++ b/wxsdk/service/entity/ShareData.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.0.8", + "uuid": "d9db9b13-470b-456d-95ff-75354ec23ca0", + "isPlugin": false, + "loadPluginInWeb": true, + "loadPluginInNative": true, + "loadPluginInEditor": false, + "subMetas": {} +} \ No newline at end of file diff --git a/wxsdk/share/SDKShare.ts b/wxsdk/share/SDKShare.ts new file mode 100644 index 0000000..950bf0b --- /dev/null +++ b/wxsdk/share/SDKShare.ts @@ -0,0 +1,123 @@ +import SimulateShare from "./SimulateShare"; +import { __LOG__, ShareVideoError } from "../base/SDKConst"; +import LogService from "../service/LogService"; +import { DOT_SHARE_TYPE } from "../base/SDKEnum"; + +export default class SDKShare { + private shareSimulate: boolean + + private static instance: SDKShare; + static get I(): SDKShare { + return this.instance || (this.instance = new SDKShare()); + } + + private constructor() { + this.shareSimulate = true; + } + + /** + * 分享 + * @param shareKey + * @param params { + * imageUrlId ?: string; 审核通过的图片 ID,详见 使用审核通过的转发图片 + * } + * @param opts + */ + share(shareKey: string, params: any, opts: any = {}): Promise { + return new Promise((resolve, reject) => { + let { title, imageUrl, query, imageUrlId, share_id, withShareTicket } = params; + let commonObj = { + title, + imageUrl, + query, + imageUrlId: imageUrlId || '', + }; + let callbackObj = { + success: (ret?: any) => { + __LOG__ && console.error('WxShare share success'); + LogService.I.share(shareKey, share_id, DOT_SHARE_TYPE.share) + resolve(ret); + }, + fail: (err?: any) => { + __LOG__ && console.error('WxShare share fail'); + reject({ ...ShareVideoError.ShareNotGroup }); + }, + cancel: () => { + __LOG__ && console.error('WxShare share cancel', null); + reject(null); + } + }; + // 是否模拟分享 + this.shareSimulate && !opts.closeSimulate && this.simulate({ ...callbackObj }); + + if(this.shareSimulate && opts.closeSimulate){ + LogService.I.share(shareKey, share_id, DOT_SHARE_TYPE.share) + } + // 主动拉起转发 + wx.shareAppMessage(commonObj); + }); + } + /** + * 打开群分享 + * @param value + */ + updateShareMenu(value) { + if (typeof wx == 'undefined') return + wx.updateShareMenu({ + withShareTicket: value + }); + } + /** + * 右上角转发 + * @param params + * @param opts + */ + forward(params: any, opts: any = {}) { + let me = this; + let { title, imageUrl, query, imageUrlId } = params; + wx.onShareAppMessage(function () { + let obj = { + title, + imageUrl, + query, + imageUrlId: imageUrlId || '', + success: (ret?: any) => { + __LOG__ && console.error('WxShare forward success'); + opts.success && opts.success.call(opts.context, ret); + }, + fail: (err?: any) => { + __LOG__ && console.error('WxShare forward fail'); + opts.fail && opts.fail.call(opts.context, err); + }, + cancel: () => { + __LOG__ && console.error('WxShare forward cancel', null); + opts.fail && opts.fail.call(opts.context, null); + } + }; + // 模拟分享 + me.shareSimulate && !opts.closeSimulate && me.simulate({ ...obj }); + return obj; + }); + // 显示当前页面的转发按钮 + wx.showShareMenu({}); + } + + private createQuery(params = {}): string { + params = { + ...params, + // channel_id: DataService.I.ChannelId, + // user_invite_uid: DataService.I.UserId + }; + let query = ''; + for (let key in params) { + query.length && (query += '&'); + query += `${key}=${params[key]}`; + } + return query; + } + + private simulate(data: any) { + SimulateShare.I.bind(data); + } + +} \ No newline at end of file diff --git a/wxsdk/share/SDKVideo.ts b/wxsdk/share/SDKVideo.ts new file mode 100644 index 0000000..af4cbb4 --- /dev/null +++ b/wxsdk/share/SDKVideo.ts @@ -0,0 +1,189 @@ +import { ShareVideoError, __LOG__ } from "../base/SDKConst"; +import { DOT_AD_TYPE, DOT_AD_STATUS } from "../base/SDKEnum"; +import LogService from "../service/LogService"; + +/* +* 激烈视频 +*/ +export default class SDKVideo { + private _isPlaying: boolean; + private _isErrored: boolean; + private resolve: any; + private reject: any; + private videoAd: any; + private videoKey: string; + private adUnitId: string; + + constructor() { + this.videoKey = ''; + this._isPlaying = false; + this._isErrored = false; + } + + get isErrored() { + return this._isErrored; + } + + get isPlaying(): boolean { + return this._isPlaying; + } + private videoAd2: any + private preloadVideoAd: any + private isPreload: boolean = false + preloadVideo(adUnitId: string) { + if (this.preloadVideoAd || this.isPreload) return + this.isPreload = true; + let ad = wx.createRewardedVideoAd({ + adUnitId + }); + ad.onError(this.preError); + ad.load().then(this.handleLoaded2).catch(() => { + this.isPreload = false + }) + this.videoAd2 = ad; + + } + private preError() { + let that = SDKVideo.I; + that.isPreload = false + } + + private handleLoaded2() { + let that = SDKVideo.I; + that.preloadVideoAd = that.videoAd2; + that.isPreload = false + __LOG__ && console.warn("视频预加载成功", that.preloadVideoAd) + // that.preloadVideoAd.ttttttt = '111111' + console.warn(that.preloadVideoAd.isReady()); + } + + offPreload() { + let that = SDKVideo.I; + that.preloadVideoAd.offError(that.preError); + } + + async show(videoKey: string, adUnitId: string): Promise { + return new Promise(async (resolve, reject) => { + if (this.isPlaying) + return reject({ ...ShareVideoError.VideoPlaying }); + + if (!adUnitId) + return reject({ ...ShareVideoError.VideoInvalid }); + + LogService.I.adStat(videoKey, this.adUnitId, DOT_AD_TYPE.video, DOT_AD_STATUS.request); + let videoAd + let hasVideo + if (this.preloadVideoAd) { + __LOG__ && console.log("使用预加载视频", this.preloadVideoAd) + hasVideo = true + this.offPreload() + videoAd = this.preloadVideoAd; + this.preloadVideoAd = null; + // this.preloadVideo(adUnitId); + } else { + __LOG__ && console.log("不使用预加载视频") + hasVideo = false + videoAd = wx.createRewardedVideoAd({ + adUnitId + }); + // this.preloadVideo(adUnitId); + } + // let videoAd = wx.createRewardedVideoAd({ + // adUnitId + // }); + if (!videoAd) + return reject({ ...ShareVideoError.VideoNotOpen }); + + this._isPlaying = true; + this._isErrored = false; + this.videoKey = videoKey; + this.adUnitId = adUnitId; + this.resolve = resolve; + this.reject = reject; + + this.videoAd = videoAd; + videoAd.onClose(this.handleClose); + videoAd.onError(this.onError); + // console.log("hasVideo",hasVideo) + if (hasVideo) { + try { + await this.handleLoaded() + } catch (error) { + __LOG__ && console.log("error", error) + await videoAd.load() + this.handleLoaded() + } + } else { + try { + await videoAd.load() + await this.handleLoaded() + } catch (error) { + this.handleError({}) + } + + } + this.preloadVideo(adUnitId); + __LOG__ && console.warn('====> PCSDK WxVideo 请求视频adUnitId', adUnitId); + }); + } + + private onError(err: any) { + // console.error("onError"); + let that = SDKVideo.I; + !that.isErrored && that.handleError(err); + } + + private async handleLoaded() { + let that = SDKVideo.I; + try { + __LOG__ && console.warn("handleLoaded2", that.videoAd, that.videoAd.isReady()); + LogService.I.adStat(this.videoKey, this.adUnitId, DOT_AD_TYPE.video, DOT_AD_STATUS.show) + + if (that.videoAd.isReady()) { + await that.videoAd.show(); + } else { + that.videoAd.load().then(() => { + that.videoAd.show(); + }).catch(that.handleError) + } + __LOG__ && console.log("handleLoaded_show") + //打点 + } catch (e) { + __LOG__ && console.log("handleLoaded_show__err"); + that.handleError({}) + } + // //数据保存 + // TempService.I.add(TempDataKeys.VideoShow, 1); + } + + private handleClose(res: any) { + __LOG__ && console.warn('====> PCSDK WxVideo 广告关闭', res && res.isEnded || res === undefined); + let that = SDKVideo.I; + if (res && res.isEnded || res === undefined) { + // 统计看视频成功 + LogService.I.adStat(that.videoKey, that.adUnitId, DOT_AD_TYPE.video, DOT_AD_STATUS.complete) + that.resolve && that.resolve({ type: 2 }); + } else { + LogService.I.adStat(that.videoKey, that.adUnitId, DOT_AD_TYPE.video, DOT_AD_STATUS.interrupt) + that.reject && that.reject({ ...ShareVideoError.VideoQuit }); + } + that.videoAd.offClose(that.handleClose); + that._isPlaying = false; + that._isErrored = false; + } + + private handleError(err: any) { + __LOG__ && console.warn('====> PCSDK WxVideo 加载视频广告失败', err); + let that = SDKVideo.I; + LogService.I.adStat(that.videoKey, that.adUnitId, DOT_AD_TYPE.video, DOT_AD_STATUS.fail) + that.reject && that.reject({ ...ShareVideoError.VideoFail }); + that.videoAd && that.videoAd.offClose && that.videoAd.offClose(that.handleClose); + that._isPlaying = false; + that._isErrored = true; + } + + private static instance: SDKVideo; + static get I(): SDKVideo { + return this.instance || (this.instance = new SDKVideo); + } +} \ No newline at end of file diff --git a/wxsdk/share/SimulateShare.ts b/wxsdk/share/SimulateShare.ts new file mode 100644 index 0000000..793601f --- /dev/null +++ b/wxsdk/share/SimulateShare.ts @@ -0,0 +1,91 @@ +import SDKBaseData from "../base/SDKBaseData"; +import SDKEventCenter from "../base/SDKEventCenter"; +import { SDKEventEnum } from "../base/SDKEventEnum"; +import { __LOG__ } from "../base/SDKConst"; + +/* +* 模拟分享; +*/ +export default class SimulateShare { + private callbacks: any; + private hideTime: number; + private delayTime: number; + + constructor() { + this.hideTime = 0; + this.delayTime = 300; + } + + private get SuccessTime(): number { + return SDKBaseData.SimulateShareTime; + } + + bind(callbacks: any) { + __LOG__ && console.warn('SDK SimulateShare bind'); + let { success, fail, cancel } = callbacks; + let flag = true; + callbacks.success = (e: any) => { + flag && success(e); + flag = false; + }; + callbacks.fail = (e: any) => { + flag && fail(e); + flag = false; + }; + callbacks.cancel = (e: any) => { + flag && cancel(e); + flag = false; + }; + this.hideTime = 0; + this.callbacks = callbacks; + SDKEventCenter.I.add(SDKEventEnum.APP_SHOW, this.onShow, this); + SDKEventCenter.I.add(SDKEventEnum.APP_HIDE, this.onHide, this); + } + + clear() { + this.hideTime = 0; + SDKEventCenter.I.remove(SDKEventEnum.APP_SHOW, this.onShow, this); + SDKEventCenter.I.remove(SDKEventEnum.APP_HIDE, this.onHide, this); + } + + private onHide() { + __LOG__ && console.warn('SDK SimulateShare hide'); + this.hideTime = new Date().getTime(); + } + + private onShow() { + let successTime = this.SuccessTime; + __LOG__ && console.warn('SDK SimulateShare show', successTime); + let { success, fail } = this.callbacks; + let curTime = Date.now(); + let time = curTime - this.hideTime; + let timeout: any; + + if (time < successTime) { + timeout = setTimeout(() => { + __LOG__ && console.warn('SDK SimulateShare 模拟分享失败!'); + fail && fail({ + errMsg: '模拟分享失败!' + }); + clearTimeout(timeout); + timeout = null; + }, this.delayTime); + } else { + timeout = setTimeout(() => { + __LOG__ && console.warn('SDK SimulateShare 模拟分享成功!'); + success && success({ + errMsg: '模拟分享成功!', + shareTickets: ['simulate_ticket'] + }); + clearTimeout(timeout); + timeout = null; + }, this.delayTime); + } + this.clear(); + } + + private static instance: SimulateShare; + static get I(): SimulateShare { + return this.instance || (this.instance = new SimulateShare); + } +} \ No newline at end of file diff --git a/wxsdk/utils/Base64Utils.ts b/wxsdk/utils/Base64Utils.ts new file mode 100644 index 0000000..e29b20c --- /dev/null +++ b/wxsdk/utils/Base64Utils.ts @@ -0,0 +1,296 @@ +// /** +// * base64.ts +// * +// * Licensed under the BSD 3-Clause License. +// * http://opensource.org/licenses/BSD-3-Clause +// * +// * References: +// * http://en.wikipedia.org/wiki/Base64 +// * +// * @author Dan Kogai (https://github.com/dankogai) +// */ +// const version = '3.5.2'; +// /** +// * @deprecated use lowercase `version`. +// */ +// const VERSION = version; +// const _hasatob = typeof atob === 'function'; +// const _hasbtoa = typeof btoa === 'function'; +// const _hasBuffer = typeof Buffer === 'function'; +// const _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined; +// const _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined; +// const b64ch ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; +// const b64chs = [...b64ch]; +// const b64tab = ((a) => { +// let tab = {}; +// a.forEach((c, i) => tab[c] = i); +// return tab; +// })(b64chs); +// const b64re = +// /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/; +// const _fromCC = String.fromCharCode.bind(String); +// const _U8Afrom = typeof Uint8Array.from === 'function' +// ? Uint8Array.from.bind(Uint8Array) +// : (it, fn: (any) => number = (x) => x) => +// new Uint8Array(Array.prototype.slice.call(it, 0).map(fn)); +// const _mkUriSafe = (src: string) => src +// .replace(/[+\/]/g, (m0) => m0 == '+' ? '-' : '_') +// .replace(/=+$/m, ''); +// const _tidyB64 = (s: string) => s.replace(/[^A-Za-z0-9\+\/]/g, ''); +// /** +// * polyfill version of `btoa` +// */ +// const btoaPolyfill = (bin: string) => { +// // console.log('polyfilled'); +// let u32, c0, c1, c2, asc = ''; +// const pad = bin.length % 3; +// for (let i = 0; i < bin.length;) { +// if ((c0 = bin.charCodeAt(i++)) > 255 || +// (c1 = bin.charCodeAt(i++)) > 255 || +// (c2 = bin.charCodeAt(i++)) > 255) +// throw new TypeError('invalid character found'); +// u32 = (c0 << 16) | (c1 << 8) | c2; +// asc += b64chs[u32 >> 18 & 63] +// + b64chs[u32 >> 12 & 63] +// + b64chs[u32 >> 6 & 63] +// + b64chs[u32 & 63]; +// } +// return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc; +// }; +// /** +// * does what `window.btoa` of web browsers do. +// * @param {String} bin binary string +// * @returns {string} Base64-encoded string +// */ +// const _btoa = _hasbtoa ? (bin: string) => btoa(bin) +// : _hasBuffer ? (bin: string) => Buffer.from(bin, 'binary').toString('base64') +// : btoaPolyfill; +// const _fromUint8Array = _hasBuffer +// ? (u8a: Uint8Array) => Buffer.from(u8a).toString('base64') +// : (u8a: Uint8Array) => { +// // cf. https://stackoverflow.com/questions/12710001/how-to-convert-uint8-array-to-base64-encoded-string/12713326#12713326 +// const maxargs = 0x1000; +// let strs = []; +// for (let i = 0, l = u8a.length; i < l; i += maxargs) { +// strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs))); +// } +// return _btoa(strs.join('')); +// }; +// /** +// * converts a Uint8Array to a Base64 string. +// * @param {boolean} [urlsafe] URL-and-filename-safe a la RFC4648 §5 +// * @returns {string} Base64 string +// */ +// const fromUint8Array = (u8a: Uint8Array, urlsafe = false) => +// urlsafe ? _mkUriSafe(_fromUint8Array(u8a)) : _fromUint8Array(u8a); +// // This trick is found broken https://github.com/dankogai/js-base64/issues/130 +// // const utob = (src: string) => unescape(encodeURIComponent(src)); +// // reverting good old fationed regexp +// const cb_utob = (c: string) => { +// if (c.length < 2) { +// var cc = c.charCodeAt(0); +// return cc < 0x80 ? c +// : cc < 0x800 ? (_fromCC(0xc0 | (cc >>> 6)) +// + _fromCC(0x80 | (cc & 0x3f))) +// : (_fromCC(0xe0 | ((cc >>> 12) & 0x0f)) +// + _fromCC(0x80 | ((cc >>> 6) & 0x3f)) +// + _fromCC(0x80 | (cc & 0x3f))); +// } else { +// var cc = 0x10000 +// + (c.charCodeAt(0) - 0xD800) * 0x400 +// + (c.charCodeAt(1) - 0xDC00); +// return (_fromCC(0xf0 | ((cc >>> 18) & 0x07)) +// + _fromCC(0x80 | ((cc >>> 12) & 0x3f)) +// + _fromCC(0x80 | ((cc >>> 6) & 0x3f)) +// + _fromCC(0x80 | (cc & 0x3f))); +// } +// }; +// const re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g; +// /** +// * @deprecated should have been internal use only. +// * @param {string} src UTF-8 string +// * @returns {string} UTF-16 string +// */ +// const utob = (u: string) => u.replace(re_utob, cb_utob); +// // +// const _encode = _hasBuffer +// ? (s: string) => Buffer.from(s, 'utf8').toString('base64') +// : _TE +// ? (s: string) => _fromUint8Array(_TE.encode(s)) +// : (s: string) => _btoa(utob(s)); +// /** +// * converts a UTF-8-encoded string to a Base64 string. +// * @param {boolean} [urlsafe] if `true` make the result URL-safe +// * @returns {string} Base64 string +// */ +// const encode = (src: string, urlsafe = false) => urlsafe +// ? _mkUriSafe(_encode(src)) +// : _encode(src); +// /** +// * converts a UTF-8-encoded string to URL-safe Base64 RFC4648 §5. +// * @returns {string} Base64 string +// */ +// const encodeURI = (src: string) => encode(src, true); +// // This trick is found broken https://github.com/dankogai/js-base64/issues/130 +// // const btou = (src: string) => decodeURIComponent(escape(src)); +// // reverting good old fationed regexp +// const re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g; +// const cb_btou = (cccc: string) => { +// switch (cccc.length) { +// case 4: +// var cp = ((0x07 & cccc.charCodeAt(0)) << 18) +// | ((0x3f & cccc.charCodeAt(1)) << 12) +// | ((0x3f & cccc.charCodeAt(2)) << 6) +// | (0x3f & cccc.charCodeAt(3)), +// offset = cp - 0x10000; +// return (_fromCC((offset >>> 10) + 0xD800) +// + _fromCC((offset & 0x3FF) + 0xDC00)); +// case 3: +// return _fromCC( +// ((0x0f & cccc.charCodeAt(0)) << 12) +// | ((0x3f & cccc.charCodeAt(1)) << 6) +// | (0x3f & cccc.charCodeAt(2)) +// ); +// default: +// return _fromCC( +// ((0x1f & cccc.charCodeAt(0)) << 6) +// | (0x3f & cccc.charCodeAt(1)) +// ); +// } +// }; +// /** +// * @deprecated should have been internal use only. +// * @param {string} src UTF-16 string +// * @returns {string} UTF-8 string +// */ +// const btou = (b: string) => b.replace(re_btou, cb_btou); +// /** +// * polyfill version of `atob` +// */ +// const atobPolyfill = (asc: string) => { +// // console.log('polyfilled'); +// asc = asc.replace(/\s+/g, ''); +// if (!b64re.test(asc)) throw new TypeError('malformed base64.'); +// asc += '=='.slice(2 - (asc.length & 3)); +// let u24, bin = '', r1, r2; +// for (let i = 0; i < asc.length;) { +// u24 = b64tab[asc.charAt(i++)] << 18 +// | b64tab[asc.charAt(i++)] << 12 +// | (r1 = b64tab[asc.charAt(i++)]) << 6 +// | (r2 = b64tab[asc.charAt(i++)]); +// bin += r1 === 64 ? _fromCC(u24 >> 16 & 255) +// : r2 === 64 ? _fromCC(u24 >> 16 & 255, u24 >> 8 & 255) +// : _fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255); +// } +// return bin; +// }; +// /** +// * does what `window.atob` of web browsers do. +// * @param {String} asc Base64-encoded string +// * @returns {string} binary string +// */ +// const _atob = _hasatob ? (asc: string) => atob(_tidyB64(asc)) +// : _hasBuffer ? (asc: string) => Buffer.from(asc, 'base64').toString('binary') +// : atobPolyfill; +// // +// const _toUint8Array = _hasBuffer +// ? (a: string) => _U8Afrom(Buffer.from(a, 'base64')) +// : (a: string) => _U8Afrom(_atob(a), c => c.charCodeAt(0)); +// /** +// * converts a Base64 string to a Uint8Array. +// */ +// const toUint8Array = (a: string): Uint8Array => _toUint8Array(_unURI(a)); +// // +// const _decode = _hasBuffer +// ? (a: string) => Buffer.from(a, 'base64').toString('utf8') +// : _TD +// ? (a: string) => _TD.decode(_toUint8Array(a)) +// : (a: string) => btou(_atob(a)); +// const _unURI = (a: string) => +// _tidyB64(a.replace(/[-_]/g, (m0) => m0 == '-' ? '+' : '/')); +// /** +// * converts a Base64 string to a UTF-8 string. +// * @param {String} src Base64 string. Both normal and URL-safe are supported +// * @returns {string} UTF-8 string +// */ +// const decode = (src: string) => _decode(_unURI(src)); +// // +// const _noEnum = (v) => { +// return { +// value: v, enumerable: false, writable: true, configurable: true +// }; +// }; +// /** +// * extend String.prototype with relevant methods +// */ +// const extendString = function () { +// const _add = (name, body) => Object.defineProperty( +// String.prototype, name, _noEnum(body) +// ); +// _add('fromBase64', function () { return decode(this) }); +// _add('toBase64', function (urlsafe) { return encode(this, urlsafe) }); +// _add('toBase64URI', function () { return encode(this, true) }); +// _add('toBase64URL', function () { return encode(this, true) }); +// _add('toUint8Array', function () { return toUint8Array(this) }); +// }; +// /** +// * extend Uint8Array.prototype with relevant methods +// */ +// const extendUint8Array = function () { +// const _add = (name, body) => Object.defineProperty( +// Uint8Array.prototype, name, _noEnum(body) +// ); +// _add('toBase64', function (urlsafe) { return fromUint8Array(this, urlsafe) }); +// _add('toBase64URI', function () { return fromUint8Array(this, true) }); +// _add('toBase64URL', function () { return fromUint8Array(this, true) }); +// }; +// /** +// * extend Builtin prototypes with relevant methods +// */ +// const extendBuiltins = () => { +// extendString(); +// extendUint8Array(); +// } +// const gBase64 = { +// version: version, +// VERSION: VERSION, +// atob: _atob, +// atobPolyfill: atobPolyfill, +// btoa: _btoa, +// btoaPolyfill: btoaPolyfill, +// fromBase64: decode, +// toBase64: encode, +// encode: encode, +// encodeURI: encodeURI, +// encodeURL: encodeURI, +// utob: utob, +// btou: btou, +// decode: decode, +// fromUint8Array: fromUint8Array, +// toUint8Array: toUint8Array, +// extendString: extendString, +// extendUint8Array: extendUint8Array, +// extendBuiltins: extendBuiltins, +// } +// // makecjs:CUT // +// export { version }; +// export { VERSION }; +// export { _atob as atob }; +// export { atobPolyfill }; +// export { _btoa as btoa }; +// export { btoaPolyfill } +// export { decode as fromBase64 }; +// export { encode as toBase64 }; +// export { utob }; +// export { encode }; +// export { encodeURI }; +// export { encodeURI as encodeURL }; +// export { btou }; +// export { decode }; +// export { fromUint8Array }; +// export { toUint8Array }; +// export { extendString }; +// export { extendUint8Array }; +// export { extendBuiltins }; +// // and finally, +// export { gBase64 as Base64 }; diff --git a/wxsdk/utils/DateUtils.ts b/wxsdk/utils/DateUtils.ts new file mode 100644 index 0000000..f9fda17 --- /dev/null +++ b/wxsdk/utils/DateUtils.ts @@ -0,0 +1,24 @@ +/** + * 日期工具类 + */ +export default class DateUtils { + static get now(): number { + return Math.floor(this.nowTime / 1000); + } + + static get nowTime(): number { + return new Date().getTime(); + } + + static get today(): string { + let time = new Date(this.nowTime); + let year = time.getFullYear(); + let month = time.getMonth() + 1; + let date = time.getDate(); + return `${year}-${this.add(month)}-${this.add(date)}`; + } + + static add(num: number): string { + return num < 10 ? '0' + num : '' + num; + } +} diff --git a/wxsdk/utils/RandomUtils.ts b/wxsdk/utils/RandomUtils.ts new file mode 100644 index 0000000..d3d8d24 --- /dev/null +++ b/wxsdk/utils/RandomUtils.ts @@ -0,0 +1,33 @@ +/** + * 随机数工具类 + */ +export default class RandomUtils { + /** + * 在一个数组中随机获取一个元素 + * @param arr 数组 + * @returns {any} 随机出来的结果 + */ + static randomArray(arr: Array): any { + let index: number = Math.floor(Math.random() * arr.length); + return arr[index]; + } + + /** + * 随机范围值[0, max), 不包含max + * @param min + * @param max + */ + static rand(min: number, max: number) { + min = min || 0; + max = max || 10000; + return Math.floor(Math.random() * 10000) % (max - min) + min; + } + + static rang(min: number, max: number) { + return Math.round(Math.random() * (max - min) + min); + } + + static randFloat(min: number, max: number) { + return parseFloat((Math.random() * (max - min) + min).toFixed(2)); + } +} diff --git a/wxsdk/utils/SDKUtils.ts b/wxsdk/utils/SDKUtils.ts new file mode 100644 index 0000000..8c9b57d --- /dev/null +++ b/wxsdk/utils/SDKUtils.ts @@ -0,0 +1,167 @@ +export default class SDKUtils { + private static toString = Object.prototype.toString; + + static isString(o: any): boolean { + return this.toString.call(o) === '[object String]'; + } + + static isFunction(o: any): boolean { + return typeof o === 'function'; + } + + static isPlainObject(o: any): boolean { + return o !== null && this.toString.call(o) === '[object Object]' && 'isPrototypeOf' in o; + } + + static isObject(o: any): boolean { + return o !== null && this.toString.call(o) === '[object Object]'; + } + + static isArray(o: any): boolean { + return this.toString.call(o) === '[object Array]'; + } + + static isNumber(o: any): boolean { + return typeof o === 'number' && isFinite(o); + } + + static isUndefined(o: any): boolean { + return typeof o === 'undefined' || o === 'undefined'; + } + + static hasProperty(o: any, key: string): boolean { + if (!o) + return false; + return o.hasOwnProperty(key); + } + + // 去除左右空空格 + static trim(str: any) { + return str.replace(/^\s+/g, '').replace(/\s+$/g, ''); + } + + static isEmpty(obj: any) { + return !obj || typeof (obj) === 'undefined' || (this.isString(obj) && this.trim(obj) === '') || obj === 'null'; + } + + static covertArray(data: any): Array { + let datas: Array = []; + if (this.isArray(data)) + datas = [...data]; + else + datas = [data]; + return datas; + } + + // 对象序列化参数 + static serializeParams(params: any) { + if (!params) { + return ''; + } + const enc = encodeURIComponent; + return Object.keys(params) + .map((key: string) => (`${enc(key)}=${enc(params[key])}`)).join('&'); + } + + // 兼容方式 - 版本比较 + static compareVersion(v1: any, v2: any) { + v1 = v1.split('.'); + v2 = v2.split('.'); + let len = Math.max(v1.length, v2.length); + + while (v1.length < len) { + v1.push('0'); + } + while (v2.length < len) { + v2.push('0'); + } + + for (let i = 0; i < len; i++) { + let num1 = parseInt(v1[i]); + let num2 = parseInt(v2[i]); + + if (num1 > num2) { + return 1; + } else if (num1 < num2) { + return -1; + } + } + + return 0; + } + + /** + * 递归地将给定对象复制到新对象中 + */ + static clone(o: any): any { + if (o === null || typeof o !== 'object') + return o; + + if (this.isArray(o)) { + let arr = o.slice(); + for (let i = 0, len = arr.length; i < len; i++) { + arr[i] = this.clone(arr[i]); + } + return arr; + } else { + let obj = {}; + for (let k in o) { + obj[k] = this.clone(o[k]); + } + return obj; + } + } + + /** + * 递归地合并两个对象,将obj1的属性设置为obj2的属性,并在必要时创建属性。 + * @param {Object} obj1 + * @param {Object} obj2 + * @return {Object} + */ + static merge(obj1: any, obj2: any, appendOnly: boolean): any { + if (obj1 === null || typeof obj1 !== 'object') + throw new TypeError('SDK merge() - 第一个参数必须是object, 不能为 ' + typeof obj1 + '。'); + + if (obj2 === null || typeof obj2 !== 'object') + throw new TypeError('SDK merge() - 第二个参数必须是object, 不能为 ' + typeof obj2 + '。'); + + if (this.isArray(obj1) || this.isArray(obj2)) + throw new TypeError('SDK merge() - 不支持数组合并。'); + + for (let k in obj2) { + let obj1Val; + let obj2Val = obj2[k]; + if (Object.prototype.hasOwnProperty.call(obj1, k)) { + if (!appendOnly) { + obj1Val = obj1[k]; + if (obj1Val !== null && typeof obj1Val === 'object' && + obj2Val !== null && typeof obj2Val === 'object') + this.merge(obj1Val, obj2Val, false); + + else + obj1[k] = this.clone(obj2Val); + } + } else + obj1[k] = this.clone(obj2Val); + } + return obj1; + } + + /** + * 简单函数promise转换 + * @param fn 函数 例如: promisifyAsyncWrap( wx.getUserInfo )( { withCredentials : true } ).then( ret => {} ); + */ + static promisifyAsyncWrap(fn: any) { + return (options: any) => new Promise((resolve, reject) => { + let conf = { + success: (res: any) => { + resolve(res); + }, + fail: (err: any) => { + reject(err); + } + }; + fn(Object.assign({}, conf, options)); + }); + } +} \ No newline at end of file diff --git a/wxsdk/utils/Sha1Utils.ts b/wxsdk/utils/Sha1Utils.ts new file mode 100644 index 0000000..f13df14 --- /dev/null +++ b/wxsdk/utils/Sha1Utils.ts @@ -0,0 +1,182 @@ +/* +* 签名工具Sha1Utils +*/ +export default class Sha1Utils { + private static instance: Sha1Utils; + + static get I(): Sha1Utils { + return this.instance || (this.instance = new Sha1Utils); + } + + private blockstart; + private W; + private H0; + private H1; + private H2; + private H3; + private H4; + private A; + private B; + private C; + private D; + private E; + private temp; + public msg; + private msg_len; + private word_array; + + private init(msg) { + this.blockstart = null; + this.W = new Array(80); + this.H0 = 0x67452301; + this.H1 = 0xEFCDAB89; + this.H2 = 0x98BADCFE; + this.H3 = 0x10325476; + this.H4 = 0xC3D2E1F0; + this.A = this.B = this.C = this.D = this.E = this.temp = this.msg = this.msg_len = null; + + this.word_array = []; + this.msg = this.Utf8Encode(msg); + this.msg_len = this.msg.length; + for (let i = 0; i < this.msg_len - 3; i += 4) { + let j = this.msg.charCodeAt(i) << 24 | this.msg.charCodeAt(i + 1) << 16 | + this.msg.charCodeAt(i + 2) << 8 | this.msg.charCodeAt(i + 3); + this.word_array.push(j); + } + this.initMsg(this.msg_len); + } + + private initMsg(msg_len) { + let i; + switch (msg_len % 4) { + case 0: + i = 0x080000000; + break; + case 1: + i = this.msg.charCodeAt(msg_len - 1) << 24 | 0x0800000; + break; + case 2: + i = this.msg.charCodeAt(msg_len - 2) << 24 | this.msg.charCodeAt(msg_len - 1) << 16 | 0x08000; + break; + case 3: + i = this.msg.charCodeAt(msg_len - 3) << 24 | this.msg.charCodeAt(msg_len - 2) << 16 | this.msg.charCodeAt(msg_len - 1) << 8 | 0x80; + break; + } + this.word_array.push(i); + while ((this.word_array.length % 16) !== 14) this.word_array.push(0); + this.word_array.push(msg_len >>> 29); + this.word_array.push((msg_len << 3) & 0x0ffffffff); + this.getTemp(); + } + + private getTemp() { + for (this.blockstart = 0; this.blockstart < this.word_array.length; this.blockstart += 16) { + for (let i = 0; i < 16; i++) this.W[i] = this.word_array[this.blockstart + i]; + for (let i = 16; i <= 79; i++) this.W[i] = this.rotate_left(this.W[i - 3] ^ this.W[i - 8] ^ this.W[i - 14] ^ this.W[i - 16], 1); + this.A = this.H0; + this.B = this.H1; + this.C = this.H2; + this.D = this.H3; + this.E = this.H4; + for (let i = 0; i <= 19; i++) { + this.temp = (this.rotate_left(this.A, 5) + ((this.B & this.C) | (~this.B & this.D)) + this.E + this.W[i] + 0x5A827999) & 0x0ffffffff; + this.E = this.D; + this.D = this.C; + this.C = this.rotate_left(this.B, 30); + this.B = this.A; + this.A = this.temp; + } + for (let i = 20; i <= 39; i++) { + this.temp = (this.rotate_left(this.A, 5) + (this.B ^ this.C ^ this.D) + this.E + this.W[i] + 0x6ED9EBA1) & 0x0ffffffff; + this.E = this.D; + this.D = this.C; + this.C = this.rotate_left(this.B, 30); + this.B = this.A; + this.A = this.temp; + } + for (let i = 40; i <= 59; i++) { + this.temp = (this.rotate_left(this.A, 5) + ((this.B & this.C) | (this.B & this.D) | (this.C & this.D)) + this.E + this.W[i] + 0x8F1BBCDC) & 0x0ffffffff; + this.E = this.D; + this.D = this.C; + this.C = this.rotate_left(this.B, 30); + this.B = this.A; + this.A = this.temp; + } + for (let i = 60; i <= 79; i++) { + this.temp = (this.rotate_left(this.A, 5) + (this.B ^ this.C ^ this.D) + this.E + this.W[i] + 0xCA62C1D6) & 0x0ffffffff; + this.E = this.D; + this.D = this.C; + this.C = this.rotate_left(this.B, 30); + this.B = this.A; + this.A = this.temp; + } + this.H0 = (this.H0 + this.A) & 0x0ffffffff; + this.H1 = (this.H1 + this.B) & 0x0ffffffff; + this.H2 = (this.H2 + this.C) & 0x0ffffffff; + this.H3 = (this.H3 + this.D) & 0x0ffffffff; + this.H4 = (this.H4 + this.E) & 0x0ffffffff; + } + } + + /** + * 获取sha1加密字符串 + */ + hex_sha1(msg) { + this.init(msg); + let temp = this.cvt_hex(this.H0) + this.cvt_hex(this.H1) + this.cvt_hex(this.H2) + this.cvt_hex(this.H3) + this.cvt_hex(this.H4); + return temp.toLowerCase(); + } + + private rotate_left(n, s) { + let t4 = (n << s) | (n >>> (32 - s)); + return t4; + } + + private lsb_hex(val) { + let str = ''; + let i; + let vh; + let vl; + + for (i = 0; i <= 6; i += 2) { + vh = (val >>> (i * 4 + 4)) & 0x0f; + vl = (val >>> (i * 4)) & 0x0f; + str += vh.toString(16) + vl.toString(16); + } + return str; + } + + private cvt_hex(val) { + let str = ''; + let i; + let v; + + for (i = 7; i >= 0; i--) { + v = (val >>> (i * 4)) & 0x0f; + str += v.toString(16); + } + return str; + } + + + private Utf8Encode(string = '') { + string = string.replace(/\r\n/g, '\n'); + let utfText = ''; + for (let n = 0; n < string.length; n++) { + let c = string.charCodeAt(n); + if (c < 128) { + utfText += String.fromCharCode(c); + } + else if ((c > 127) && (c < 2048)) { + utfText += String.fromCharCode((c >> 6) | 192); + utfText += String.fromCharCode((c & 63) | 128); + } + else { + utfText += String.fromCharCode((c >> 12) | 224); + utfText += String.fromCharCode(((c >> 6) & 63) | 128); + utfText += String.fromCharCode((c & 63) | 128); + } + } + return utfText; + } +} diff --git a/wxsdk/utils/SignUtils.ts b/wxsdk/utils/SignUtils.ts new file mode 100644 index 0000000..aab95bf --- /dev/null +++ b/wxsdk/utils/SignUtils.ts @@ -0,0 +1,85 @@ +import { Md5 } from "../lib/md5"; +import { GAMEDATA } from "../base/SDKConst"; +import SDKUtils from "./SDKUtils"; + +export default class SignUtils { + private static instance: any; + + static get I(): SignUtils { + return this.instance || (this.instance = new SignUtils()); + } + + createSign(params: any) { + let signStr = this.createQuery(params) + '' + GAMEDATA.appkey; + //console.error("signStr",signStr) + return Md5.hashStr(signStr); + } + + private ksort(inputArr: any) { + let tmp_arr = {}, + keys: any[] = [], + sorter, i, k: any, + strictForIn = false, + populateArr = {}; + + sorter = function (a, b) { + let aFloat = parseFloat(a), + bFloat = parseFloat(b), + aNumeric = aFloat + '' === a, + bNumeric = bFloat + '' === b; + if (aNumeric && bNumeric) { + return aFloat > bFloat ? 1 : aFloat < bFloat ? -1 : 0; + } else if (aNumeric && !bNumeric) { + return 1; + } else if (!aNumeric && bNumeric) { + return -1; + } + return a > b ? 1 : a < b ? -1 : 0; + }; + + // Make a list of key names + for (k in inputArr) { + if (inputArr.hasOwnProperty(k)) { + keys.push(k); + } + } + keys.sort(sorter); + + // Rebuild array with sorted key names + for (i = 0; i < keys.length; i++) { + k = keys[i]; + tmp_arr[k] = inputArr[k]; + if (strictForIn) { + delete inputArr[k]; + } + } + for (i in tmp_arr) { + if (tmp_arr.hasOwnProperty(i)) { + populateArr[i] = tmp_arr[i]; + } + } + + return strictForIn || populateArr; + } + + private createQuery(params) { + params = params || {}; + // 1、ksort排序参数对象 + let keys = Object.keys(this.ksort(params)); + // 2、拼贴字符串 + let key = ''; + let query = ''; + for (let i = 0, len = keys.length; i < len; i++) { + // 为空,为 0的参数不参与签名,参数名为ver的参数不参与签名, 字符集为 utf-8 + if (params[keys[i]] === '' || params[keys[i]] === '0' || params[keys[i]] === 0 || keys[i] === 'ver') continue + key = keys[i]; + i && (query += ''); + if(SDKUtils.isArray(params[key])){ + query += `${key}=${JSON.stringify(params[key])}`; + }else{ + query += `${key}=${params[key]}`; + } + } + return query; + } +} \ No newline at end of file diff --git a/wxsdk/utils/StorageUtils.ts b/wxsdk/utils/StorageUtils.ts new file mode 100644 index 0000000..aad06df --- /dev/null +++ b/wxsdk/utils/StorageUtils.ts @@ -0,0 +1,93 @@ +/** + * 微信存储功能工具 + */ + +import WxStorage from "../platform/wx/WxStorage"; +import Storage from "../platform/Storage"; + +export default class StorageUtils { + private store: any = null + + constructor() { + if (typeof localStorage === 'object') + this.store = new Storage; + + if (typeof (wx) != "undefined") + this.store = new WxStorage; + } + + private __isExpired(entity: any) { + if (!entity) return true; + let isExpired = this.timestamp - (entity.timestamp + entity.expiration) >= 0; + return isExpired; + } + + get timestamp() { + return +(new Date().getTime() / 1000).toFixed(0); + } + + set(key: string, value: Object, expiration = 0) { + const entity = { + timestamp: this.timestamp, + expiration, + key, + value + }; + this.store.setItem(key, JSON.stringify(entity)); + return this; + } + + get(key: string) { + let entity: any; + try { + entity = this.store.getItem(key); + if (entity) { + entity = JSON.parse(entity); + } else { + return null; + } + } catch (err) { + console.warn(err); + return null; + } + + // 没有设置过期时间, 则直接返回值 + if (!entity.expiration) + return entity.value; + + // 已过期 + if (this.__isExpired(entity)) { + this.remove(key); + return null; + } else { + return entity.value; + } + } + + remove(key: string) { + try { + this.store.removeItem(key); + } catch (err) { + console.warn(err); + } + return this; + } + + clear() { + try { + this.store.clear(); + } catch (err) { + console.warn(err); + } + return this; + } + + isExist(key: string): boolean { + return this.store.hasKey(key); + } + + private static instance: StorageUtils; + static get I(): StorageUtils { + return this.instance || (this.instance = new StorageUtils); + } +} \ No newline at end of file diff --git a/wxsdk/wx/Version.ts b/wxsdk/wx/Version.ts new file mode 100644 index 0000000..5a3e6c9 --- /dev/null +++ b/wxsdk/wx/Version.ts @@ -0,0 +1,137 @@ +import { __LOG__ } from "../base/SDKConst"; + +enum PlatformType { + WX = 'wx', + QQ = 'qq' +} +enum Platform{ + PlatformType = 'wx' +} +export default class Version { + private DEFUALT = '0.0.0'; + private VERSION = { + createRewardedVideoAd: { + [PlatformType.WX]: '2.0.4', + [PlatformType.QQ]: '0.1.26' + }, + createInterstitialAd: { + [PlatformType.WX]: '2.6.0' + }, + createFeedbackButton: { + [PlatformType.WX]: '2.1.2' + }, + createUserInfoButton: { + [PlatformType.WX]: '2.0.1' + }, + createBannerAd: { + [PlatformType.WX]: '2.0.4', + [PlatformType.QQ]: '0.1.26' + }, + createGameBanner: { + [PlatformType.WX]: '2.7.5' + }, + createGamePortal: { + [PlatformType.WX]: '2.7.5' + }, + createGameIcon: { + [PlatformType.WX]: '2.8.2' + }, + setClipboardData: { + [PlatformType.WX]: '1.1.0' + }, + setSubscribeMessage: { + [PlatformType.WX]: '2.8.0' + }, + getUpdateManager: { + [PlatformType.WX]: '1.9.90' + }, + updateShareMenu: { + [PlatformType.WX]: '1.2.0' + }, + showShareMenu: { + [PlatformType.WX]: '1.1.0' + }, + vibrate: { + [PlatformType.WX]: '1.2.0' + }, + getShareInfo: { + [PlatformType.WX]: '1.1.0' + }, + openCustomerServiceConversation: { + [PlatformType.WX]: '2.0.3' + } + }; + + private constructor() { + __LOG__ && console.error('SDK Version ', this.VERSION, this.getVRewardedVideoAd(), this.getVInterstitialAd()); + } + + getVRewardedVideoAd() { + return this.VERSION.createRewardedVideoAd[Platform.PlatformType] || this.DEFUALT; + } + + getVInterstitialAd() { + return this.VERSION.createInterstitialAd[Platform.PlatformType] || this.DEFUALT; + } + + getVFeedbackButton() { + return this.VERSION.createFeedbackButton[Platform.PlatformType] || this.DEFUALT; + } + + getVUserInfoButton() { + return this.VERSION.createUserInfoButton[Platform.PlatformType] || this.DEFUALT; + } + + getVBannerAd() { + return this.VERSION.createBannerAd[Platform.PlatformType] || this.DEFUALT; + } + + getVGameBanner() { + return this.VERSION.createGameBanner[Platform.PlatformType] || this.DEFUALT; + } + + getVGamePortal() { + return this.VERSION.createGamePortal[Platform.PlatformType] || this.DEFUALT; + } + + getVGameIcon() { + return this.VERSION.createGameIcon[Platform.PlatformType] || this.DEFUALT; + } + + getVClipboardData() { + return this.VERSION.setClipboardData[Platform.PlatformType] || this.DEFUALT; + } + + getVSubscribeMessage() { + return this.VERSION.setSubscribeMessage[Platform.PlatformType] || this.DEFUALT; + } + + getVUpdateManager() { + return this.VERSION.getUpdateManager[Platform.PlatformType] || this.DEFUALT; + } + + getVVibrate() { + return this.VERSION.vibrate[Platform.PlatformType] || this.DEFUALT; + } + + getVShareInfo() { + return this.VERSION.getShareInfo[Platform.PlatformType] || this.DEFUALT; + } + + getVUpdateShareMenu() { + return this.VERSION.updateShareMenu[Platform.PlatformType] || this.DEFUALT; + } + + getVShowShareMenu() { + return this.VERSION.showShareMenu[Platform.PlatformType] || this.DEFUALT; + } + + getVCustomerService() { + return this.VERSION.openCustomerServiceConversation[Platform.PlatformType] || this.DEFUALT; + } + + private static instance: Version; + static get I(): Version { + return this.instance || (this.instance = new Version()); + } +} \ No newline at end of file diff --git a/wxsdk/wx/WxApi.ts b/wxsdk/wx/WxApi.ts new file mode 100644 index 0000000..60324f6 --- /dev/null +++ b/wxsdk/wx/WxApi.ts @@ -0,0 +1,415 @@ +import SDKUtils from "../utils/SDKUtils"; +import WxSystem from "./WxSystem"; +import Version from "./Version"; +import { __LOG__ } from "../base/SDKConst"; +import { NetworkType } from "../base/SDKEnum"; +import DataService from "../service/DataService"; + +export default class WxApi { + private getVersionError(version: string): { errMsg: string; errCode: number } { + return { errMsg: `支持最低版本:${version}`, errCode: -1 }; + } + + canIUse(version: string): boolean { + return SDKUtils.compareVersion(WxSystem.I.SDKVersion, version) >= 0; + } + + // 注意 + // Android 6.7.2 以下版本,点击取消或蒙层时,回调 fail, errMsg 为 "fail cancel"; + // Android 6.7.2 及以上版本 和 iOS 点击蒙层不会关闭模态弹窗,所以尽量避免使用「取消」分支中实现业务逻辑 + showModal(data: any) { + wx.showModal(data); + } + + // copy(str: string) { + // let version = Version.I.getVClipboardData(); + // if (!this.canIUse(version)) return Promise.reject(this.getVersionError(version)); + + // return new Promise((resolve, reject) => { + // wx.setClipboardData({ + // data: str, + // success: (res: any) => { + // resolve(res); + // }, + // fail: (err: any) => { + // reject(err); + // } + // }); + // }); + // } + + subscribeMessage(tmplIds: Array) { + let version = Version.I.getVSubscribeMessage(); + if (!this.canIUse(version)) return Promise.reject(this.getVersionError(version)); + console.log("tmplIds",tmplIds) + return new Promise((resolve, reject) => { + wx.requestSubscribeMessage({ + tmplIds, + success: (ret: { errMsg: string }) => { + resolve(ret); + }, + fail: (err: { errMsg: string; errCode: number }) => { + reject(err); + } + }); + }); + } + + /** + * 创建banner广告 + */ + createBannerAd(adUnitId: string, style: _StyleObject, adIntervals?: number) { + if (!this.canIUse(Version.I.getVBannerAd())) return null; + + adIntervals = adIntervals || 30; + return wx.createBannerAd({ + adUnitId, + style + }); + } + + /** + * 创建小游戏推荐banner + */ + createGameBanner(adUnitId: string, style: _StyleGameObject) { + if (!this.canIUse(Version.I.getVGameBanner())) return null; + + return wx.createGameBanner({ + adUnitId, + style + }); + } + + /** + * 创建插屏广告组件 + */ + createInterstitialAd(adUnitId: string) { + if (!this.canIUse(Version.I.getVInterstitialAd())) return null; + return wx.createInterstitialAd({ + adUnitId + }); + } + + /** + * 创建小游戏推荐icon组件 + */ + createGameIcon(adUnitId: string, opts: { count: number; style: Array<_GameIconStyleItem> }) {// + if (!this.canIUse(Version.I.getVGameIcon())) return null; + + return wx.createGameIcon({ adUnitId, ...opts }); + } + + /** + * 创建小游戏推荐弹窗组件 + */ + createGamePortal(adUnitId: string) { + if (!this.canIUse(Version.I.getVGamePortal())) return null; + + return wx.createGamePortal({ + adUnitId + }); + } + + /** + * 创建激励视频广告组件 + */ + createRewardedVideoAd(adUnitId: string) { + if (!this.canIUse(Version.I.getVRewardedVideoAd())) return null; + + return wx.createRewardedVideoAd({ + adUnitId + }); + } + + /** + * 创建打开意见反馈页面的按钮 + */ + createFeedbackButton(data: _FeedbackButtonObject) { + if (!this.canIUse(Version.I.getVFeedbackButton())) return null; + + return wx.createFeedbackButton(data); + } + + /** + * 创建用户信息按钮 + */ + createUserInfoButton(data: _UserInfoButtonObject): _UserInfoButton | null {// + if (!this.canIUse(Version.I.getVUserInfoButton())) return null; + return wx.createUserInfoButton(data); + } + + /** + * 进入客服会话。要求在用户发生过至少一次 touch 事件后才能调用 + */ + openCustomerServiceConversation(params: _CustomerServiceConversationObject) {// + if (!this.canIUse(Version.I.getVCustomerService())) return -1; + wx.openCustomerServiceConversation({ ...params }); + return 1; + } + + vibrateLong() { + if (!this.canIUse(Version.I.getVVibrate())) return Promise.reject(null); + + return new Promise((resolve, reject) => { + wx.vibrateLong({ + success: () => { + resolve(1); + }, + fail: () => { + resolve(0); + } + }); + }); + } + + vibrateShort() { + if (!this.canIUse(Version.I.getVVibrate())) return Promise.reject(null); + + return new Promise((resolve, reject) => { + wx.vibrateShort({ + success: () => { + resolve(1); + }, + fail: () => { + resolve(0); + } + }); + }); + } + + /** + * 更新转发属性 + */ + updateShareMenu(value: boolean) { + if (!this.canIUse(Version.I.getVUpdateShareMenu())) return; + wx.updateShareMenu({ + withShareTicket: value + }); + } + + /** + * 检测更新 + */ + checkUpdate(data: _ShowModalObject): void {// + if (!this.canIUse(Version.I.getVUpdateManager())) return; + + data = data || {}; + // tslint:disable-next-line: no-this-assignment + let that = this; + // 默认参数 + let params: _ShowModalObject = {// + title: data.title || '更新提示', + content: data.content || '新版本已经准备好,是否重启应用?', + showCancel: false, + success: (res: _ShowModalSuccessObject) => {// + data.success && data.success(res); + if (res.confirm) { + updateManager.applyUpdate(); + } + }, + fail: (err: any) => data.fail && data.fail(err), + complete: () => data.complete && data.complete() + }; + // 合并参数 + let keys = ['cancelText', 'cancelColor', 'confirmText', 'confirmColor']; + keys.forEach(key => SDKUtils.hasProperty(data, key) && (params = { ...params, [key]: data[key] })); + + let updateManager = wx.getUpdateManager(); + updateManager.onCheckForUpdate((res: any) => { + __LOG__ && console.warn('====> PCSDK WxApi updateManager onCheckForUpdate', res.hasUpdate); + }); + updateManager.onUpdateReady(function () { + that.showModal(params); + }); + updateManager.onUpdateFailed(function () { + // 新的版本下载失败 + __LOG__ && console.warn('SDK WxApi updateManager onUpdateFailed'); + }); + } + + /** + * 显示当前页面的转发按钮 + */ + showShareMenu(shareApp: _ShareAppMessageObject, shareMenu: _UpdateShareMenuObject) { + if (!this.canIUse(Version.I.getVShowShareMenu())) return; + __LOG__ && console.error('showShareMenu shareApp'); + __LOG__ && console.error('showShareMenu shareMenu'); + wx.onShareAppMessage(() => shareApp); + wx.showShareMenu(shareMenu); + } + + /** + * 主动拉起转发,进入选择通讯录界面。 + */ + shareAppMessage(data: _ShareAppMessageObject) { + wx.shareAppMessage(data); + } + + previewImage(imgList: Array, index: number) { + return new Promise((suc, fail) => { + if (!imgList || imgList.length === 0) { + fail(); + return; + } + wx.previewImage({ + current: imgList[index], // 当前显示图片的http链接 + urls: imgList, // 需要预览的图片http链接列表 + success(res: any) { + suc({ ...res, qrcode: 1 }); + }, + fail(err: any) { + fail(err); + } + }); + }); + } + + getNetworkType(): Promise<_NetworkTypeSuccessObject> { + return new Promise((resolve, reject) => { + wx.getNetworkType({ + success(res: _NetworkTypeSuccessObject) { + resolve(res); + }, + fail(err: any) { + reject({ networkType: NetworkType.Unknown }); + } + }); + }); + } + + navigateToMiniProgram(appId: string, path: string, opts: any = {}) { + return new Promise((resolve, reject) => { + wx.navigateToMiniProgram({ + appId, + path, + extraData: opts.extraData || {}, + envVersion: opts.envVersion || 'release', + success(res: any) { + resolve(res); + }, + fail(err: any) { + reject(err); + } + }); + }); + } + + /** + * login获取code接口 + */ + login(): Promise { + return new Promise((resolve, reject) => { + wx.login({ + success: (ret: any) => { + resolve(ret.code); + }, + fail: (err: any) => { + reject(err); + WxApi.I.setAuthorize({ + errorTip: 'wx.login fail', + ...err + }); + } + }); + }); + } + + /** + * 用户信息授权接口 + */ + getUserinfo(): Promise { + return new Promise((resolve, reject) => { + wx.getUserInfo({ + lang: "zh_CN", + withCredentials: true, + success: (ret: any) => { + if (!ret) { + reject(ret); + return WxApi.I.setAuthorize({ + errorTip: 'getUserInfo Success result is null', + ...ret + }); + } + resolve(ret); + }, + fail: (err: any) => { + reject(err); + WxApi.I.setAuthorize({ + errorTip: 'getUserInfo Fail', + ...err + }); + } + }); + }); + } + + /** + * 分享接口 + * @param shareTicket + */ + getShareInfo(shareTicket: string): Promise { + if (!this.canIUse(Version.I.getVShareInfo())) return Promise.reject(null); + + return new Promise((resolve, reject) => { + wx.getShareInfo({ + shareTicket, + success: (ret: _getShareInfoSuccessObject) => { + let { errMsg } = ret; + if (errMsg === 'getShareInfo:ok') { + resolve(ret); + } else { + reject(ret); + } + }, + fail: (err: any) => { + reject(err); + } + }); + }); + } + + /** + * 米大师充值 + */ + requestMidasPayment(params: { mode: string; env: number; offerId: string; currencyType: string; platform: string; buyQuantity: number; zoneId: string }): Promise { + let { mode, env, offerId, currencyType, platform, buyQuantity, zoneId } = params; + console.warn('====> PCSDK WxApi requestMidasPayment 支付参数', { + mode, + env, + offerId, + currencyType, + platform, + buyQuantity, + zoneId + }); + return new Promise((resolve, reject) => { + wx.requestMidasPayment({ + mode, + env, + offerId, + currencyType, + platform, + buyQuantity, + zoneId, + success: ret => { + if (ret && ret.errMsg === 'requestMidasPayment:ok') + resolve(ret); + else + reject(ret); + }, + fail: err => { + reject(err); + } + }); + }); + } + + private setAuthorize(error: any) { + DataService.I.setAuthorize(false); + } + + private static _instance: WxApi; + static get I(): WxApi { + return this._instance || (this._instance = new WxApi); + } +} \ No newline at end of file diff --git a/wxsdk/wx/WxBanner.ts b/wxsdk/wx/WxBanner.ts new file mode 100644 index 0000000..7d9f3c0 --- /dev/null +++ b/wxsdk/wx/WxBanner.ts @@ -0,0 +1,230 @@ +import WxSystem from "./WxSystem"; +import SDKUtils from "../utils/SDKUtils"; +import { BannerError, __LOG__ } from "../base/SDKConst"; +import LogService from "../service/LogService"; +import { DOT_AD_STATUS, DOT_AD_TYPE } from "../base/SDKEnum"; + +/* +* banner +*/ +export default class WxBanner { + private static instance: WxBanner; + static get I(): WxBanner { + return this.instance || (this.instance = new WxBanner(750, 750)); + } + private designWidth: number; + private bannerWidth: number; + private bannerHeight: number; + private bannerScale: number; + private bannerParams: any; + private _isErrored: boolean; + private adUnitId: string; + private bannerAd: any; + private resolve: any; + private reject: any; + + constructor(designWidth: number, bannerWidth: number) { + designWidth = designWidth || 750; + bannerWidth = bannerWidth || 750; + + this.adUnitId = ''; + this._isErrored = false; + this.designWidth = designWidth; + this.bannerScale = WxSystem.I.winWidth / this.designWidth; + this.bannerWidth = Math.max(this.bannerScale * bannerWidth, 300); + this.bannerHeight = WxSystem.I.winHeight / this.bannerScale; + } + + get isErrored() { + return this._isErrored; + } + + private queue: Function[] = []; + private isEnd: boolean = false; + + create(adUnitId: string, opts?: { type?: number; bannerWidth?: number, offsetY?: number; adIntervals?: number, isOff?: boolean }) { + this.bannerParams = opts || {}; + if (opts && opts.bannerWidth) { + this.bannerWidth = opts.bannerWidth; + } + this.isEnd = true; + this.bannerParams.type = this.bannerParams.type || 1; + this.bannerParams.offsetY = -this.bannerParams.offsetY || 0; + this.bannerParams.adIntervals = this.bannerParams.adIntervals || 120; + return new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + this._isErrored = false; + this.adUnitId = adUnitId; + + if (SDKUtils.isEmpty(adUnitId)) { + WxBanner.I.handleQueue(); + return this.reject({ ...BannerError.BannerInvalid, adUnitId: this.adUnitId }); + } + + // 设置样式(hack:修复qq版本) + this.bannerParams.type === 2 && (this.bannerWidth = WxSystem.I.winWidth); + let style = { top: 0, left: (WxSystem.I.winWidth - this.bannerWidth) / 2, width: this.bannerWidth }; + style = { + ...style, + top: 0 + this.bannerParams.offsetY, + }; + // 创建并判断是否存在 + // if (this.bannerParams.type === 2) + // this.bannerAd = wx.createGameBanner({ adUnitId, style: { left: style.left, top: this.bannerHeight } }); + // else + if (this.bannerAd) { + if (!opts || (opts && !opts.isOff)) { + this.show(false); + } + return + } + this.bannerAd = wx.createBannerAd({ adUnitId, style, adIntervals: this.bannerParams.adIntervals }); + LogService.I.adStat('banner', this.adUnitId, DOT_AD_TYPE.banner, DOT_AD_STATUS.request) + if (!this.bannerAd) { + WxBanner.I.handleQueue(); + return this.reject({ ...BannerError.BannerNotOpen, adUnitId: this.adUnitId }); + } + + this.bannerAd.onLoad(this.onLoad); + this.bannerAd.onError(this.onError); + this.bannerAd.onResize(this.onResize); + if (!opts || (opts && !opts.isOff)) { + this.show(false); + } + }); + } + + /** + * + * @param bol + */ + show(bol: boolean = true): boolean { + if (WxBanner.I.isEnd && bol) { + WxBanner.I.queue.push(WxBanner.I.show.bind(WxBanner.I)) + return + } + WxBanner.I.isEnd = true + if (this.bannerAd) { + if (this.bannerAd.style.realHeight) + this.bannerAd.style.top = WxSystem.I.winHeight - this.bannerAd.style.realHeight + this.bannerParams.offsetY; + if (this.bannerParams.type === 2) { + if (this.bannerAd.style.width) + this.bannerAd.style.left = (WxSystem.I.winWidth - this.bannerAd.style.width) / 2; + else + this.bannerAd.style.left = (WxSystem.I.winWidth - this.bannerWidth) / 2; + } else + this.bannerAd.style.left = (WxSystem.I.winWidth - this.bannerWidth) / 2; + + LogService.I.adStat('banner', this.adUnitId, DOT_AD_TYPE.banner, DOT_AD_STATUS.show) + this.bannerAd.show().catch((err: any) => this.handleShowError(err)); + __LOG__ && console.error('wxBanner - show: ' + this.adUnitId, this.bannerAd.style); + WxBanner.I.handleQueue() + return true; + } + WxBanner.I.handleQueue() + return false; + } + + hide() { + let that = WxBanner.I; + if (that.isEnd) { + that.queue.push(that.hide.bind(that)) + return + } + that.isEnd = true + if (this.bannerAd) { + this.bannerAd.style.left = -9999; + this.bannerAd.hide(); + LogService.I.adStat('banner', that.adUnitId, DOT_AD_TYPE.banner, DOT_AD_STATUS.interrupt) + __LOG__ && console.error('wxBanner - hide: ' + this.adUnitId); + } + that.handleQueue() + } + + toggle(isshow: boolean) { + isshow ? this.show() : this.hide(); + } + + destory() { + if (this.bannerAd) { + this.bannerAd.style.left = -9999; + this.bannerAd.destroy(); + this.bannerAd = null; + LogService.I.adStat('banner', this.adUnitId, DOT_AD_TYPE.banner, DOT_AD_STATUS.interrupt) + __LOG__ && console.error('wxBanner - destory: ' + this.adUnitId); + } + } + + private onLoad() { + let that = WxBanner.I; + let bannerAd = that.bannerAd; + if (!bannerAd) return; + // Platform.IsQQ && that.show(); + __LOG__ && console.error('wxBanner - onLoad: ' + that.adUnitId); + if (bannerAd.style.realHeight) + bannerAd.style.top = WxSystem.I.winHeight - bannerAd.style.realHeight + that.bannerParams.offsetY; + that.unbind(); + that.handleQueue(); + that.resolve && that.resolve({ + adUnitId: that.adUnitId, + scale: that.bannerScale, + width: bannerAd.style.realWidth / that.bannerScale, + height: bannerAd.style.realHeight / that.bannerScale + }); + } + + private onError(err: any) { + __LOG__ && console.error('wxBanner - onError', err); + let that = WxBanner.I; + !that._isErrored && that.handleError(err, { ...BannerError.BannerFail, adUnitId: that.adUnitId }); + } + + private onResize() { + let that = WxBanner.I; + let bannerAd = that.bannerAd; + if (!bannerAd) return; + bannerAd.style.top = WxSystem.I.winHeight - bannerAd.style.realHeight + that.bannerParams.offsetY; + bannerAd.style.left = (WxSystem.I.winWidth - bannerAd.style.realWidth) / 2; + } + + private handleShowError(ret: { errCode: number; errMsg: string }) { + __LOG__ && console.error('wxBanner - handleShowError', ret); + let that = WxBanner.I; + let { errCode, errMsg } = ret; + !that.isErrored && that.handleError(ret, { code: errCode, msg: errMsg }); + } + + private handleError(ret: any, err: any) { + let that = WxBanner.I; + that.unbind(); + that._isErrored = true; + that.bannerAd = null; + that.handleQueue() + LogService.I.adStat('banner', that.adUnitId, DOT_AD_TYPE.banner, DOT_AD_STATUS.fail) + that.reject && that.reject({ ...err, adUnitId: that.adUnitId }); + __LOG__ && console.error('wxBanner - onError: ' + that.adUnitId); + } + + private unbind() { + if (this.bannerAd) { + this.bannerAd.offLoad(this.onLoad); + this.bannerAd.offError(this.onError); + this.bannerAd.offResize(this.onResize); + } + } + + /** + * 处队列 + */ + handleQueue() { + let that = WxBanner.I; + if (that.queue.length > 0) { + that.isEnd = false; + let fn = that.queue.shift(); + fn(); + } else { + that.isEnd = false; + } + } +} \ No newline at end of file diff --git a/wxsdk/wx/WxCustom.ts b/wxsdk/wx/WxCustom.ts new file mode 100644 index 0000000..b4039af --- /dev/null +++ b/wxsdk/wx/WxCustom.ts @@ -0,0 +1,200 @@ +import WxSystem from "./WxSystem"; +import SDKUtils from "../utils/SDKUtils"; +import { BannerError, __LOG__ } from "../base/SDKConst"; +import LogService from "../service/LogService"; +import { DOT_AD_STATUS, DOT_AD_TYPE } from "../base/SDKEnum"; + +/* +* 原生模板广告 +*/ +export default class WxCustom { + private static instance: WxCustom; + static get I(): WxCustom { + return this.instance || (this.instance = new WxCustom()); + } + private bannerParams: any; + private _isErrored: boolean; + private adUnitId: string; + private customAd: any; + private resolve: any; + private reject: any; + + constructor() { + this.adUnitId = ''; + this._isErrored = false; + } + + get isErrored() { + return this._isErrored; + } + + private queue: Function[] = []; + private isEnd: boolean = false; + + create(adUnitId: string, opts?: { top: number, left: number, adIntervals?: number }) { + this.bannerParams = opts || {}; + this.bannerParams.adIntervals = this.bannerParams.adIntervals || 60; + + if (this.customAd) { + // this.show(false); + if (WxCustom.I.isEnd) { + WxCustom.I.queue.push(WxCustom.I.show.bind(WxCustom.I)) + return + } + } + this.isEnd = true; + return new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + this._isErrored = false; + this.adUnitId = adUnitId; + + if (SDKUtils.isEmpty(adUnitId)) { + WxCustom.I.handleQueue(); + return this.reject({ ...BannerError.BannerInvalid, adUnitId: this.adUnitId }); + } + + if (this.customAd) { + this.show(false); + return + } + let style = { top: opts.top, left: opts.left }; + // console.log("style", style) + // 创建并判断是否存在 + this.customAd = wx.createCustomAd({ adUnitId, style, adIntervals: this.bannerParams.adIntervals }); + // console.log("this.customAd") + // console.log(this.customAd) + // console.log(JSON.stringify(this.customAd)) + LogService.I.adStat('custom', this.adUnitId, DOT_AD_TYPE.custom, DOT_AD_STATUS.request) + // console.log("create_custom", this.customAd) + if (!this.customAd) { + return this.reject({ ...BannerError.BannerNotOpen, adUnitId: this.adUnitId }); + } + + this.customAd.onLoad(this.onLoad); + this.customAd.onClose(this.onClose); + this.customAd.onError(this.onError); + }); + } + + show(bol: boolean = true): boolean { + if (WxCustom.I.isEnd && bol) { + WxCustom.I.queue.push(WxCustom.I.show.bind(WxCustom.I)) + return + } + WxCustom.I.isEnd = true; + // console.log("that.isEnd", this.isEnd, this.customAd) + if (this.customAd) { + LogService.I.adStat('custom', this.adUnitId, DOT_AD_TYPE.custom, DOT_AD_STATUS.show) + this.customAd.show().then(res=>{ + WxCustom.I.handleQueue(); + }).catch((err: any) => this.handleShowError(err)); + __LOG__ && console.error('WxCustom - show: ' + this.adUnitId, this.customAd); + return true; + } + WxCustom.I.handleQueue(); + return false; + } + + hide() { + let that = WxCustom.I; + // console.log("that.isEnd", that.isEnd) + if (that.isEnd) { + that.queue.push(that.hide.bind(that)) + return + } + that.isEnd = true + if (this.customAd) { + this.customAd.hide(); + LogService.I.adStat('custom', that.adUnitId, DOT_AD_TYPE.custom, DOT_AD_STATUS.interrupt) + __LOG__ && console.error('WxCustom - hide: ' + that.adUnitId); + } + that.handleQueue() + } + + toggle(isshow: boolean) { + isshow ? this.show() : this.hide(); + } + + destory() { + if (this.customAd) { + this.customAd.destroy(); + this.customAd = null; + LogService.I.adStat('custom', this.adUnitId, DOT_AD_TYPE.custom, DOT_AD_STATUS.interrupt) + __LOG__ && console.error('WxCustom - destory: ' + this.adUnitId); + } + } + + private onLoad() { + let that = WxCustom.I; + let customAd = that.customAd; + if (!customAd) return; + that.show(false); + __LOG__ && console.error('WxCustom - onLoad: ' + that.adUnitId); + // if (customAd.style.top) { + // customAd.style.top = that.bannerParams.top; + // customAd.style.left = that.bannerParams.left; + // } + that.unbind(); + that.resolve && that.resolve({ + adUnitId: that.adUnitId, + top: that.bannerParams.top, + left: that.bannerParams.left, + }); + } + + private onError(err: any) { + __LOG__ && console.error('WxCustom - onError', err); + let that = WxCustom.I; + !that._isErrored && that.handleError(err, { ...BannerError.BannerFail, adUnitId: that.adUnitId }); + } + //关闭广告 + private onClose() { + let that = WxCustom.I; + that.customAd = null; + LogService.I.dot("wxCustom_close") + } + + private handleShowError(ret: { errCode: number; errMsg: string }) { + __LOG__ && console.error('WxCustom - handleShowError', ret); + let that = WxCustom.I; + let { errCode, errMsg } = ret; + !that.isErrored && that.handleError(ret, { code: errCode, msg: errMsg }); + } + + private handleError(ret: any, err: any) { + let that = WxCustom.I; + that.unbind(); + that._isErrored = true; + if(that.customAd){ + that.customAd.destroy() + } + that.customAd = null + that.handleQueue() + that.reject && that.reject({ ...err, adUnitId: that.adUnitId }); + LogService.I.adStat('custom', this.adUnitId, DOT_AD_TYPE.custom, DOT_AD_STATUS.fail) + __LOG__ && console.error('WxCustom - onError: ' + that.adUnitId); + } + + private unbind() { + if (this.customAd) { + this.customAd.offLoad(this.onLoad); + this.customAd.offClose(this.onClose); + this.customAd.offError(this.onError); + } + } + + /** + * 处队列 + */ + handleQueue() { + let that = WxCustom.I; + if (that.queue.length > 0) { + that.isEnd = false; + let fn = that.queue.shift(); + fn(); + } else { + that.isEnd = false; + } + } +} \ No newline at end of file diff --git a/wxsdk/wx/WxGrid.ts b/wxsdk/wx/WxGrid.ts new file mode 100644 index 0000000..536cf75 --- /dev/null +++ b/wxsdk/wx/WxGrid.ts @@ -0,0 +1,233 @@ +import WxSystem from "./WxSystem"; +import SDKUtils from "../utils/SDKUtils"; +import { BannerError, __LOG__ } from "../base/SDKConst"; +import LogService from "../service/LogService"; +import { DOT_AD_STATUS, DOT_AD_TYPE } from "../base/SDKEnum"; + +/* +* banner +*/ +export default class WxGrid { + private static instance: WxGrid; + static get I(): WxGrid { + return this.instance || (this.instance = new WxGrid(750, 750)); + } + private designWidth: number; + private bannerWidth: number; + private bannerHeight: number; + private bannerScale: number; + private bannerParams: any; + private _isErrored: boolean; + private adUnitId: string; + private gridAd: any; + private resolve: any; + private reject: any; + private AdList: object + + constructor(designWidth: number, bannerWidth: number) { + designWidth = designWidth || 750; + bannerWidth = bannerWidth || 750; + + this.AdList = {} + this.adUnitId = ''; + this._isErrored = false; + this.designWidth = designWidth; + this.bannerScale = WxSystem.I.winWidth / this.designWidth; + this.bannerWidth = Math.max(this.bannerScale * bannerWidth, 300); + this.bannerHeight = WxSystem.I.winHeight / this.bannerScale; + } + + get isErrored() { + return this._isErrored; + } + + create(key: string, adUnitId: string, opts?: { gridCount?: number; bannerWidth?: number, offsetY: number; adIntervals?: number }) { + this.bannerParams = opts || {}; + this.AdList[key] = { + ...opts, + adUnitId, + key, + isEnd: false, + queue: [] + } + if (opts && opts.bannerWidth) { + this.bannerWidth = opts.bannerWidth; + } + this.bannerParams.type = this.bannerParams.type || 1; + this.bannerParams.gridCount = this.bannerParams.gridCount || 5; + this.bannerParams.offsetY = -this.bannerParams.offsetY || 0; + this.bannerParams.adIntervals = this.bannerParams.adIntervals || 60; + return new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + this._isErrored = false; + this.adUnitId = adUnitId; + + if (SDKUtils.isEmpty(adUnitId)) + return this.reject({ ...BannerError.BannerInvalid, adUnitId: this.adUnitId }); + + // 设置样式(hack:修复qq版本) + this.bannerParams.type === 2 && (this.bannerWidth = WxSystem.I.winWidth); + let style = { top: 0, left: (WxSystem.I.winWidth - this.bannerWidth) / 2, width: this.bannerWidth }; + style = { + ...style, + top: 0 + this.bannerParams.offsetY + }; + if (this.AdList[key].gridAd) { + if (this.AdList[key].isOff) { + this.show(key) + } + resolve() + return + } + // 创建并判断是否存在 + this.gridAd = wx.createGridAd({ adUnitId, style: { left: style.left, top: this.bannerHeight }, gridCount: this.bannerParams.gridCount, }); + LogService.I.adStat('grid', adUnitId, DOT_AD_TYPE.grid, DOT_AD_STATUS.request) + if (!this.gridAd) + return this.reject({ ...BannerError.BannerNotOpen, adUnitId: this.adUnitId }); + this.AdList[key].gridAd = this.gridAd; + this.gridAd.onLoad(this.onLoad); + this.gridAd.onError(this.onError); + this.gridAd.onResize(this.onResize); + if (!this.AdList[key].isOff) { + this.show(key); + } + }); + } + + show(key: string): boolean { + if (this.AdList[key].gridAd) { + if (this.AdList[key].isEnd) { + let fun = (key) => { + WxGrid.I.show(key) + } + this.AdList[key].queue.push(fun); + return + } + this.AdList[key].isEnd = true; + + if (this.AdList[key].gridAd.style.realHeight) + this.AdList[key].gridAd.style.top = WxSystem.I.winHeight - this.AdList[key].gridAd.style.realHeight + this.AdList[key].offsetY; + if (this.bannerParams.type === 2) { + if (this.AdList[key].gridAd.style.width) + this.AdList[key].gridAd.style.left = (WxSystem.I.winWidth - this.AdList[key].gridAd.style.width) / 2; + else + this.AdList[key].gridAd.style.left = (WxSystem.I.winWidth - this.bannerWidth) / 2; + } else + this.AdList[key].gridAd.style.left = (WxSystem.I.winWidth - this.bannerWidth) / 2; + + LogService.I.adStat('grid', this.AdList[key].adUnitId, DOT_AD_TYPE.grid, DOT_AD_STATUS.show) + this.AdList[key].gridAd.show().catch((err: any) => this.handleShowError(err)); + this.onResize(); + __LOG__ && console.error('WxGrid - show: ' + this.adUnitId, this.AdList[key].gridAd.style); + WxGrid.I.handleQueue(key) + return true; + } + WxGrid.I.handleQueue(key) + return false; + } + + hide(key: string) { + if (this.AdList[key].gridAd) { + if (this.AdList[key].isEnd) { + let fun = (key) => { + WxGrid.I.hide(key) + } + this.AdList[key].queue.push(fun); + return + } + this.AdList[key].isEnd = true; + this.AdList[key].gridAd.style.left = -9999; + this.AdList[key].gridAd.hide(); + LogService.I.adStat('grid', this.AdList[key].adUnitId, DOT_AD_TYPE.grid, DOT_AD_STATUS.interrupt); + __LOG__ && console.error('WxGrid - hide: ' + this.adUnitId); + + WxGrid.I.handleQueue(key) + } + } + + destory(key: string) { + if (this.AdList[key] && this.AdList[key].gridAd) { + this.AdList[key].gridAd.style.left = -9999; + this.AdList[key].gridAd.destroy(); + this.AdList[key].gridAd = null; + LogService.I.adStat('grid', this.AdList[key].adUnitId, DOT_AD_TYPE.grid, DOT_AD_STATUS.interrupt) + __LOG__ && console.error('WxGrid - destory: ' + this.AdList[key].adUnitId); + } + } + + private onLoad() { + let that = WxGrid.I; + let gridAd = that.gridAd; + if (!gridAd) return; + // Platform.IsQQ && that.show(); + __LOG__ && console.error('WxGrid - onLoad: ' + that.adUnitId); + if (gridAd.style.realHeight) { + gridAd.style.top = WxSystem.I.winHeight - gridAd.style.realHeight + that.bannerParams.offsetY; + gridAd.style.left = (WxSystem.I.winWidth - gridAd.style.realWidth) / 2; + } + + that.unbind(); + that.resolve && that.resolve({ + adUnitId: that.adUnitId, + scale: that.bannerScale, + width: gridAd.style.realWidth / that.bannerScale, + height: gridAd.style.realHeight / that.bannerScale + }); + } + + private onError(err: any) { + __LOG__ && console.error('WxGrid - onError', err); + let that = WxGrid.I; + !that._isErrored && that.handleError(err, { ...BannerError.BannerFail, adUnitId: that.adUnitId }); + } + + private onResize() { + let that = WxGrid.I; + let gridAd = that.gridAd; + if (!gridAd) return; + gridAd.style.top = WxSystem.I.winHeight - gridAd.style.realHeight + that.bannerParams.offsetY; + gridAd.style.left = (WxSystem.I.winWidth - gridAd.style.realWidth) / 2; + } + + private handleShowError(ret: { errCode: number; errMsg: string }) { + __LOG__ && console.error('WxGrid - handleShowError', ret); + let that = WxGrid.I; + let { errCode, errMsg } = ret; + !that.isErrored && that.handleError(ret, { code: errCode, msg: errMsg }); + } + + private handleError(ret: any, err: any) { + let that = WxGrid.I; + that.unbind(); + if (that.gridAd) { + that.gridAd.destroy() + } + that.gridAd = null; + that._isErrored = true; + that.reject && that.reject({ ...err, adUnitId: that.adUnitId }); + LogService.I.adStat('grid', that.adUnitId, DOT_AD_TYPE.grid, DOT_AD_STATUS.fail) + __LOG__ && console.error('WxGrid - onError: ' + that.adUnitId); + } + + private unbind() { + if (this.gridAd) { + this.gridAd.offLoad(this.onLoad); + this.gridAd.offError(this.onError); + this.gridAd.offResize(this.onResize); + } + } + /** + * 处队列 + */ + handleQueue(key) { + let that = WxGrid.I; + if (that.AdList[key].queue.length > 0) { + that.AdList[key].isEnd = false; + let fn = that.AdList[key].queue.shift(); + fn(); + } else { + that.AdList[key].isEnd = false; + } + } +} \ No newline at end of file diff --git a/wxsdk/wx/WxInit.ts b/wxsdk/wx/WxInit.ts new file mode 100644 index 0000000..6124e54 --- /dev/null +++ b/wxsdk/wx/WxInit.ts @@ -0,0 +1,155 @@ +import WxApi from './WxApi'; +import WxSystem from './WxSystem'; +import WxLaunch from './WxLaunch'; +import { __LOG__, GAMEDATA } from '../base/SDKConst'; +import DataService from '../service/DataService'; +import SDKUtils from '../utils/SDKUtils'; +import SDKEventCenter from '../base/SDKEventCenter'; +import { SDKEventEnum } from '../base/SDKEventEnum'; +import { NetworkType, DOT_SHARE_TYPE } from '../base/SDKEnum'; +import DateUtils from '../utils/DateUtils'; +import LogService from '../service/LogService'; + +export default class WxInit { + private onlineTime: number; + + private constructor() { + this.onlineTime = DateUtils.nowTime; + wx.onShow(this.onShow.bind(this)); + wx.onHide(this.onHide.bind(this)); + wx.onNetworkStatusChange(this.onNetworkStatusChange.bind(this)); + } + + init() { + let { platform } = WxSystem.I.SystemData; + let launchData = WxLaunch.I.LaunchData; + console.log("启动信息", launchData) + let { query, scene, referrerInfo } = launchData; + let { + invite_type, + channelId, + channel_id, + td_channelid, + channel, + share_id, + share_key, + user_invite_uid, + weixinadinfo, + account_id, + service_id, + shareMessageToFriendScene + } = query; + + let fromChannel = channelId || channel_id || channel || td_channelid; + // 设置微信转化跟踪:第一项为来源广告的广告id,替换来源渠道id + if (weixinadinfo) {//!fromChannel && + let infoArr = weixinadinfo.split('.'); + if (infoArr && infoArr.length) + fromChannel = infoArr[0]; + } + + //定向分享统计 + if (shareMessageToFriendScene && shareMessageToFriendScene >= 0 && shareMessageToFriendScene <= 50) { + share_key = GAMEDATA.shareMessageToFriend.sharekey; + share_id = GAMEDATA.shareMessageToFriend.share_id; + } + // 计算最终渠道ID + // let lchannelId: number; + // let { IntegralChannelId, ChannelId } = CfgManager.I.config; + // if (fromChannel) + // lchannelId = fromChannel; + // else if (scene === SceneCode.WX_INTEGRAL && IntegralChannelId) + // lchannelId = IntegralChannelId; + // else + // lchannelId = ChannelId; + + __LOG__ && console.warn(`====> PCSDK wxinit query user_invite_uid:${user_invite_uid}; fromChannel:${fromChannel}; channelId:${fromChannel}; scene:${scene}; share_id:${share_id}; share_key:${share_key};`); + + // 设置sdk 数据的信息 + DataService.I + .setScene(scene) + .setReferrerInfo(referrerInfo || {}) + .setShareId(share_id) + .setPlatform(platform) + .setShareKey(share_key) + .setChannelId(fromChannel) + .setQueryChannelId(fromChannel || GAMEDATA.channel_id) + .setInviteType(invite_type) + .setQueryUserInviteUid(user_invite_uid || 0) + .setQueryExtData({ account_id, service_id }) + .setSystemId() + .setLaunchSource(); + // 设置网络类型 + WxApi.I.getNetworkType() + .then((ret: _NetworkTypeSuccessObject) => this.setNetworkType(ret)) + .catch((err: _NetworkTypeSuccessObject) => this.setNetworkType(err)); + + // 从会话过来,且没有渠道id,打点 + // let shareScenes = [SceneCode.WX_SHARE_FRIEND, SceneCode.WX_SHARE_GROUP, SceneCode.WX_SHARE_TICKET]; + // if (!fromChannel && shareScenes.indexOf(scene) >= 0) { + // LogService.I.dot(SDKDotType.Share); + // } + } + + private onShow(opts: any) { + console.log("onshow_opts", opts) + let nowTime = DateUtils.nowTime; + let { scene, shareTicket } = opts; + this.onlineTime = nowTime; + // 设置sdk 数据的信息 + DataService.I + .setScene(scene) + .setLaunchTime(nowTime) + .setLaunchSource(); + + if (opts && opts.query) { + let { share_key, share_id, invite_type, user_invite_uid } = opts.query; + // DebugUtils.I.dynamic('====> PCSDK onShow 参数:', JSON.stringify(opts)); + // DebugUtils.I.dynamic(`====> PCSDK onShow scene:${scene}; share_id:${share_id}; share_key:${share_key};`); + !SDKUtils.isUndefined(share_id) && DataService.I.setShareId(share_id); + !SDKUtils.isUndefined(share_key) && DataService.I.setShareKey(share_key); + !SDKUtils.isUndefined(invite_type) && DataService.I.setInviteType(invite_type); + !SDKUtils.isUndefined(shareTicket) && DataService.I.setShareTicket(shareTicket); + !SDKUtils.isUndefined(user_invite_uid) && DataService.I.setQueryUserInviteUid(user_invite_uid); + if (share_id && share_key) { + LogService.I.share(share_key, share_id, DOT_SHARE_TYPE.click); + } + } + + // 抛出onShow事件 + SDKEventCenter.I.emit(SDKEventEnum.APP_SHOW, opts); + } + + private onHide() { + // 清除banner定时器 + // Platform.I.banner && Platform.I.banner.clear(); + + SDKEventCenter.I.emit(SDKEventEnum.APP_HIDE); + // 曝光同步 + LogService.I.bannerExposure(); + // // 资源变更同步 + // LogService.I.syncLogRes(); + // // 关卡同步 + // LogService.I.syncLogLevel(); + // 同步logout:在线时长同步 + // console.error("this.onlineTime", this.onlineTime) + if (this.onlineTime) { + LogService.I.logOut(DateUtils.nowTime - this.onlineTime); + this.onlineTime = 0; + } + } + + private onNetworkStatusChange(res: { isConnected: boolean; networkType: NetworkType }) { + console.warn('网络变化了:', res); + this.setNetworkType(res); + } + + private setNetworkType(res: any) { + res && res.networkType && DataService.I.setNetworkType(res.networkType as NetworkType); + } + + private static instance: WxInit; + static get I(): WxInit { + return this.instance || (this.instance = new WxInit); + } +} \ No newline at end of file diff --git a/wxsdk/wx/WxInterstitial.ts b/wxsdk/wx/WxInterstitial.ts new file mode 100644 index 0000000..65d1258 --- /dev/null +++ b/wxsdk/wx/WxInterstitial.ts @@ -0,0 +1,49 @@ +import { GAMEDATA } from "../base/SDKConst"; +import { DOT_AD_STATUS, DOT_AD_TYPE } from "../base/SDKEnum"; +import LogService from "../service/LogService"; +import WxApi from "./WxApi"; + +export default class WxInterstitial { + /** 插屏广告ID */ + private static interstitialAdId: string = GAMEDATA.interstitialAdId; + /** 插屏广告实例 */ + public static interstitialAd: any; + /** 插屏广告是否加载完成 */ + private static interstitialSuccell: boolean = false; + /** 初始化插屏广告 */ + public static initInterstitialAd(adUnitId: string = GAMEDATA.interstitialAdId) { + this.interstitialAdId = adUnitId; + this.createInterstitial(); + } + + /** 显示插屏广告 */ + public static showInterstitialAd(adUnitId: string = GAMEDATA.interstitialAdId) { + this.interstitialAdId = adUnitId; + if (!this.interstitialSuccell) { + this.createInterstitial(true); + return; + } + this.interstitialAd.show(); + setTimeout(() => { + this.createInterstitial(false); + }, 5000); + } + + /** 创建插屏广告 */ + private static createInterstitial(isShow: boolean = false) { + LogService.I.adStat('InterstitialAd', this.interstitialAdId, DOT_AD_TYPE.interstitial, DOT_AD_STATUS.request) + this.interstitialAd = WxApi.I.createInterstitialAd(this.interstitialAdId); + this.interstitialAd.onLoad(() => { + WxInterstitial.interstitialSuccell = true; + LogService.I.adStat('InterstitialAd', this.interstitialAdId, DOT_AD_TYPE.interstitial, DOT_AD_STATUS.rt) + if (isShow) { + LogService.I.adStat('InterstitialAd', this.interstitialAdId, DOT_AD_TYPE.interstitial, DOT_AD_STATUS.show) + this.interstitialAd.show() + } + }) + this.interstitialAd.onError(err => { + LogService.I.adStat('InterstitialAd', this.interstitialAdId, DOT_AD_TYPE.interstitial, DOT_AD_STATUS.fail) + WxInterstitial.interstitialSuccell = false; + }) + } +} \ No newline at end of file diff --git a/wxsdk/wx/WxLaunch.ts b/wxsdk/wx/WxLaunch.ts new file mode 100644 index 0000000..d3e89ac --- /dev/null +++ b/wxsdk/wx/WxLaunch.ts @@ -0,0 +1,32 @@ +export default class WxLaunch { + private static instance: WxLaunch; + private data: LaunchInfoSyncReturnValue; + + private constructor() { + this.data = wx.getLaunchOptionsSync(); + } + + get LaunchData() { + return this.data; + } + + get query() { + return this.data.query; + } + + get scene() { + return this.data.scene; + } + + get shareTicket() { + return this.data.shareTicket; + } + + get referrerInfo() { + return this.data.referrerInfo; + } + + static get I(): WxLaunch { + return this.instance || (this.instance = new WxLaunch()); + } +} \ No newline at end of file diff --git a/wxsdk/wx/WxLogin.ts b/wxsdk/wx/WxLogin.ts new file mode 100644 index 0000000..0f9864d --- /dev/null +++ b/wxsdk/wx/WxLogin.ts @@ -0,0 +1,183 @@ +import WxApi from "./WxApi"; +import DataService from "../service/DataService"; +import { ErrorCode } from "../base/SDKConst"; +import { SDKApi } from "../http/SDKApi"; +import WxSystem from "./WxSystem"; +import DateUtils from "../utils/DateUtils"; +import LogService from "../service/LogService"; +import { DOT_SHARE_TYPE } from "../base/SDKEnum"; +import ShareVideoService from "../service/ShareVideoService"; +import OnlineService from "../service/OnlineService"; + +export default class WxLogin { + /** + * 发起登录 + * @param isAuthorize Boolean 是否重新登录 + * tip1:如果是重新登录,需要把之前的token清空 + * tip2:必须先调用wxLogin才能使用getUserInfo + */ + async login(isAuthorize: boolean): Promise { + return new Promise(async (resolve, reject) => { + let code = await WxApi.I.login(); + if (isAuthorize) { + WxApi.I.getUserinfo() + .then((ret: any) => this.authedlogin(ret, code, resolve, reject)) + .catch((err: any) => this.weakLogin(err, code, resolve, reject)); + } else { + this.weakLogin({ errCode: 1, msg: '默认未授权登录' }, code, resolve, reject) + } + }); + } + private isFirst: boolean + + /** + * 授权后登录登陆 + * @param ret + * @param resolve + * @param reject + */ + private async authedlogin(ret: _GetUserInfoSuccessObject, code: string, resolve: any, reject: any) {//_GetUserInfoSuccessObject + let { rawData, iv, encryptedData, signature, userInfo } = ret; + let params = { + ...this.buildParams(), + code, + signature, + iv: iv,//encodeURIComponent(), + // raw_data: encodeURIComponent(Base64.encode(rawData)), + encryptedData: encryptedData//encodeURIComponent(encryptedData) + }; + // 设置性别 + userInfo && DataService.I.setGender(userInfo.gender); + SDKApi.Login(params) + .then((data: any) => { + // console.log("登录", data) + this.handleLogin(data, resolve, true); + resolve(data); + }) + .catch((err: any) => reject(err)); + } + + /** + * 弱登录 + * @param err + * @param resolve + * @param reject + */ + private async weakLogin(err: any, code: string, resolve: any, reject: any) { + // console.log("弱登录") + // 重新登录不需要继续往下走 + if (!err || err.errCode === ErrorCode.InvalidLogin.code) + return; + + // 传递来源appid + let refererInfo = DataService.I.ReferrerInfo; + let params = { + ...this.buildParams(), + // refAppId: refererInfo.appId || 0, + code + }; + + SDKApi.weakLogin(params) + .then((data: any) => { + this.handleLogin(data, resolve, false); + }) + .catch((err: any) => reject(err)); + } + + /** + * 处理登录/弱登录请求结果 + * @param data + * @param isAuthorize 是否授权:true=已授权 false=没有授权成功 + */ + private handleLogin(data: any, resolve: any, isAuthorize: boolean) { + // console.log("登录请求结果data", data) + if (data) { + // 设置登录信息 + if (data.data) { + let { channel, uid, firstlogin, token, reftoken, openid, expire, isnew, gameconfig } = data.data; + LogService.I.setLogind({ + channel, + userId: uid, + regTime: firstlogin, + openId: openid, + isnew, + token, + refToken: reftoken, + expire, + }); + OnlineService.I.setData(gameconfig) + LogService.I.active(); + if (!this.isFirst && DataService.I.ShareKey && DataService.I.ShareId) { + this.isFirst = true; + LogService.I.share(DataService.I.ShareKey, DataService.I.ShareId, DOT_SHARE_TYPE.click); + } + this.handleExpire(expire) + ShareVideoService.I.forward() + } + } + resolve(data) + } + + /** + * 构建登录/弱登录公用参数 + */ + private buildParams() { + let channel = DataService.I.QueryChannelId + ''; + let brand = WxSystem.I.brand; + let model = WxSystem.I.model; + let version = WxSystem.I.version; + let system = WxSystem.I.system; + let platform = WxSystem.I.platform; + let sdkversion = WxSystem.I.SDKVersion; + let shareuid = +DataService.I.QueryUserInviteUid; + let scene = DataService.I.Scene + ''; + let sharekey = DataService.I.ShareKey; + let shareid = DataService.I.ShareId + ''; + return { + channel, + brand, + model, + version, + system, + platform, + sdkversion, + shareuid, + scene, + sharekey, + shareid + }; + } + + /** + * 刷新token + * @param expire + */ + private countDown + private handleExpire(expire) { + let time = expire - DateUtils.now; + this.countDown && clearTimeout(this.countDown) + this.countDown = setTimeout(this.refreshToken, time * 1000); + } + + private refreshToken() { + SDKApi.reftoken({ + channel: DataService.I.ChannelId + '', + uid: DataService.I.UserId, + token: DataService.I.Token, + reftoken: DataService.I.refToken + }).then(res => { + let { token, reftoken, expire } = res.data; + DataService.I.setLoginData({ + token, + refToken: reftoken, + expire, + }); + this.handleExpire(expire) + }) + } + + private static _instance: WxLogin; + static get I(): WxLogin { + return this._instance || (this._instance = new WxLogin); + } +} \ No newline at end of file diff --git a/wxsdk/wx/WxPay.ts b/wxsdk/wx/WxPay.ts new file mode 100644 index 0000000..827da86 --- /dev/null +++ b/wxsdk/wx/WxPay.ts @@ -0,0 +1,100 @@ +import { GAMEDATA } from '../base/SDKConst'; +import { EnvCode } from '../base/SDKEnum'; +import { SDKApi } from '../http/SDKApi'; +import DataService from '../service/DataService'; +import WxApi from './WxApi'; +import WxSystem from './WxSystem'; + +export default class WxPay { + /** + * 发起支付 + */ + pay(params: { money: number; source:string}, opts: any = {}): Promise { + return new Promise((resolve, reject) => { + let { Mode, OfferId, ZoneId, CurrencyType, Platform } = GAMEDATA.MidasPay; + // 环境配置,0:米大师正式环境 1:米大师沙箱环境 + let env =1;// DataService.I.EnvEnum === EnvCode.Prod ? 0 : 1; + // 平台类型,android or ios,config.js有配置走配置,否则判断系统id + let platform = Platform || (DataService.I.SystemId === 0 ? 'ios' : 'android'); + let pms = { + mode: Mode, + env, + platform, + offerId: OfferId, + currencyType: CurrencyType, + buyQuantity: params.money * 10, + zoneId: ZoneId + }; + console.log(pms); + WxApi.I.requestMidasPayment(pms) + .then(() => this.handlePaySuccess({ ...params, platform }, opts, resolve, reject)) + .catch(err => this.handlePayError(err, reject)); + }); + } + + private async handlePaySuccess(data: { money: number;source:string, platform: string }, opts: any, resolve: any, reject: any) { + // let code = await WxApi.I.login(); + + let extend =""; + for(let key in opts){ + extend +=(`${key}=${opts[key]}&`) + } + extend = extend.substring(0,extend.length-1); + extend = encodeURIComponent(extend); + let params = { + ...this.buildParams(), + ...opts, + extend, + source:data.source, + type:1, + amount: data.money * 100, + platform: data.platform + }; + SDKApi.pay(params) + .then(ret => resolve(ret)) + .catch(err => this.handlePayError(err, reject)); + } + + private handlePayError(err: any, reject: any) { + console.log("支付失败",JSON.stringify(err)); + reject(err); + } + + /** + * 构建支付公用参数 + */ + private buildParams() { + let APIVersion = '0.6.0';//固定值 + let gameid = GAMEDATA.game_id; + let channel = DataService.I.ChannelId; + let openid = DataService.I.OpenId; + let brand = WxSystem.I.brand; + let model = WxSystem.I.model; + let version = WxSystem.I.version; + let system = WxSystem.I.system; + let sdkversion = WxSystem.I.SDKVersion; + let scene = DataService.I.Scene + ''; + let uid = DataService.I.UserId; + let token = DataService.I.Token; + let env = DataService.I.EnvEnum === 1 ? 'pre' : 'prod'; + return { + gameid, + openid, + channel, + brand, + model, + version, + system, + sdkversion, + scene, + uid, + token, + env + }; + } + + private static _instance: WxPay; + static get I(): WxPay { + return this._instance || (this._instance = new WxPay); + } +} \ No newline at end of file diff --git a/wxsdk/wx/WxSystem.ts b/wxsdk/wx/WxSystem.ts new file mode 100644 index 0000000..8dce24c --- /dev/null +++ b/wxsdk/wx/WxSystem.ts @@ -0,0 +1,59 @@ +export default class WxSystem { + private static instance: WxSystem; + private data: SystemInfoSyncReturnValue; + + private constructor() { + this.data = wx.getSystemInfoSync(); + } + + get SystemData() { + return this.data; + } + + get version() { + return this.data.version; + } + get system() { + return this.data.system; + } + + get platform() { + return this.data.platform; + } + + get language() { + return this.data.language; + } + + get winWidth() { + return this.data.windowWidth; + } + + get winHeight() { + return this.data.windowHeight; + } + + get screenWidth() { + return this.data.screenWidth; + } + + get screenHeight() { + return this.data.screenHeight; + } + + get SDKVersion() { + return this.data.SDKVersion; + } + + get brand(){ + return this.data.brand; + } + + get model(){ + return this.data.model; + } + + static get I(): WxSystem { + return this.instance || (this.instance = new WxSystem()); + } +} -- libgit2 0.21.0