大侠
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

598 lines
24 KiB

export default class CySDK {
//sdk 0.4.1
//1、增加A/B测功能(增加是否开启AB处理)
//2、关卡埋点
//3、时长分析面板
//4、抖跳微
//5、首日次日用户,新老用户
//6、快手跳微信接入
//7、iaa2.0插件接入
static isABTest: boolean = false;//如果用不到AB测功能则关掉
static appKey: string = "";//后台策略appKey
static version: string = "GameName1.0.3-cy0.4.1"; //若不使用ab测试时的本地版本号(同一个版本号,每一个关卡序号需要跟每一个关卡名称一致)
//#region 其他参数
static logUrl = "https://log.liehujt.com/game_log"
static openId: string = ""; //用户唯一标识
static scene: number = -1; //场景值
static gameName: string = "";
static companyName: string = "";
static code: string = "";
static shareTitle: string = "分享游戏";
static shareImageUrl: string = "";
static launchRes: Object = {};//launch的参数
//#endregion
private static storageData: cyStoreData;//sdk存档数据
static init(cb: Function) {
console.log("=========创游SDK版本:0.4.1");
const self = this;
//=========创游SDK初始化
let finishCb = (data: any) => {
setInterval(() => {
self.storageData.playTime++;
self.saveCyStoreData();
}, 1000);//每秒更新一次时间
if (data?.shareTitle) {
this.shareTitle = data.shareTitle;
}
if (data?.shareImageUrl) {
this.shareImageUrl = data.shareImageUrl;
}
cb(data);
}
if (this.appKey == "") {
console.warn("=========创游SDK appkey 未设置,跳过sdk初始化");
this.cyStoreDataDeal();
finishCb(null);
if (window["wx"]) {
self.launchRes = window["wx"].getLaunchOptionsSync();
self.trackEvent_uma("launch", { res: JSON.stringify(self.launchRes) });
}
self.trackEvent({ event: "login", level_type: "normal" });
} else {
this.login((data: any) => {
finishCb(data);
self.trackEvent({ event: "login", level_type: "normal" });
})
}
}
//#region 埋点接口
/**友盟打点 */
static trackEvent_uma(event, obj = {}) {
obj["playTime"] = this.storageData.playTime;
obj["version"] = this.version;
obj["fisrtLoginDateStr"] = this.storageData.firstLoginDateStr;
for (let key in this.iaaData) {
const umaKey = "IAA" + key;
obj[umaKey] = this.iaaData[key];
}
console.log("友盟尝试打点", event, obj);
if (window["wx"] && window['wx'].uma) {
window['wx'].uma.trackEvent(event, obj);
console.log("wx友盟打点发送", event, obj);
}
}
/**游戏内埋点统一接口(主要减少一些通用参数的传输) */
private static trackEvent(data: object, cb?: Function) {
if (this.appKey == "") {
console.warn("=========创游SDKappKey缺失,无法发送埋点数据");
return;
}
if (this.openId == "") {
console.warn("=========创游SDKopenId缺失,无法发送埋点数据");
return;
}
if (this.scene == -1) {
console.warn("=========创游SDKscene缺失,无法发送埋点数据");
return;
}
const self = this;
let sendData = {
appkey: self.appKey,
scene: self.scene,
version: self.version,
isNew: self.storageData.isNew,//true 是新用户,false是老用户,此处信息前端会给到,用于判定新老用户
hasPlayDay: (self.getNowDay() - self.storageData.firstLoginDay),//用户已玩天数,此处信息前端传入,用户首次日筛选,(若没有则后端自行处理成0,给之前的版本做兼容)
gameTime: self.storageData.playTime,//游玩时长(秒),此处信息前端传入,用于后续制作用户时长分布面板
firstLoginDateStr: self.storageData.firstLoginDateStr,//用户首次登录日期字符串,格式yyyy-MM-dd,用于记录筛选对应日期进来用户的后续行为数据
data: {
event: "", // login, level_start, level_success, level_fail, ad_request, ad_cancel, ad_finish,encrypted_data
openid: "",
level_type: "",
level_name: "",
level_index: -1,
dur: -1,
mode: "",
mode_type: "",
mode_index: -1,
why: "",
encryptedData: "",
iv: "",
}
}
sendData.data["openid"] = self.openId;
sendData.data["dur"] = Math.ceil(sendData.data["dur"] || -1); // 确保dur为整数
for (let key in data) {
if (sendData.data.hasOwnProperty(key)) {
sendData.data[key] = data[key];
}
}
const sendStr = JSON.stringify(sendData);
console.log("=========创游SDK埋点数据发送", sendStr);
if (!window["wx"]) {
fetch(self.logUrl, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: sendStr
})
.then((value) => {
console.log("=========创游SDK埋点数据发送返回", sendStr);
cb && cb(value)
})
.catch(err => {
console.log("=========创游SDK埋点数据发送失败", sendStr, err);
});
} else {
window["wx"].request(
{
url: self.logUrl,
method: "POST",
data: JSON.stringify(sendData),
success(res) {
console.log("=========创游SDK埋点数据发送成功", sendData);
cb && cb(res);
},
fail(err) {
console.log("=========创游SDK埋点数据发送失败", sendData, err)
}
}
)
}
}
//#endregion
//#region 登录初始化
/** SDK登录
* @param version 版本(用来分辨不同版本的数据)
* @param cb 回调函数(data) =>{} data为后台策略返回数据
*/
private static login(cb: Function) {
const self = this;
if (this.appKey == "") {
cb(null);
return;
}
if (window["wx"]) {
let count = 2;
let loginCode = -1;
let scene = -1;
let clue_token = "";
let req_id = "";
let ad_id = "";
let clickid = "";
let callback = "";
let ksCampaignId = "";
let ksUnitId = "";
let ksCreativeId = "";
let ksChannel = "";
let ksSiteAccountId = "";
let ksPageId = "";
//拿登录
const loginCb = (backCb: Function) => {
window['wx'].login({
success: (res) => {
backCb(res.code);
},
fail: (err) => {
console.log("wx登录失败", err);
backCb(-1);
}
})
};
//拿场景值
const getSceneCb = (backCb: Function) => {
console.log("=========创游SDK获取场景值");
const res = window["wx"].getLaunchOptionsSync();
self.launchRes = res;
console.log("=========创游SDK冷启动成功", res, JSON.stringify(res));
const sceneGet = res.scene || -1;
if (sceneGet == 1069) {//如果是从外部app导入的用户
//抖音
clue_token = res.query["clue_token"] || "";
req_id = res.query["req_id"] || "";
ad_id = res.query["ad_id"] || "";
clickid = res.query["clickid"] || "";
//快手
callback = res.query["callback"] || "";
ksCampaignId = res.query["ksCampaignId"] || "";
ksUnitId = res.query["ksUnitId"] || "";
ksCreativeId = res.query["ksCreativeId"] || "";
ksChannel = res.query["ksChannel"] || "";
ksSiteAccountId = res.query["ksSiteAccountId"] || "";
ksPageId = res.query["ksPageId"] || "";
}
console.log("=========创游SDK场景值获取成功1", sceneGet);
backCb(sceneGet);
}
const finishCb = () => {
count--;
console.log("=========创游SDKFinishCb", count);
if (count > 0)
return;
//一些sdk的存档数据
self.cyStoreDataDeal();
//拿配置
window["wx"].request(
{
url: "https://liehujt.com//api/campaign/strategy-new-info/" + self.appKey + "/" + self.version,
success(res) {
cb(res.data);
},
fail(err) {
console.log("=========创游SDK策略参数获取失败", err)
cb(null);
}
}
)
}
loginCb((code:any) => {
loginCode = code;
let url = "https://log.liehujt.com/login?appkey=" + self.appKey + "&code=" + loginCode;
if (clue_token != "") {//抖音跳转微信
url = url + "&clue_token=" + clue_token + "&project_id=___&promotion_id=___&mid1=___&mid2=___&mid3=___&mid4=___&mid5=___" + "&req_id=" + req_id + "&advertiser_id=" + ad_id + "&clickid=" + clickid;
}
if (ksCampaignId != "")//快手跳转微信
{
url = url + "&callback=" + callback + "&ksCampaignId=" + ksCampaignId + "&ksUnitId=" + ksUnitId + "&ksCreativeId=" + ksCreativeId + "&ksChannel=" + ksChannel + "&ksSiteAccountId=" + ksSiteAccountId + "&ksPageId=" + ksPageId;
}
//登录
window["wx"].request(
{
method: "GET",
url: url,
success(res) {
console.log("=========创游SDK登录返回", res);
self.openId = res.data.openid || "";
if (self.isABTest) {
self.version = res.data.version || self.version;
} else {
self.version = self.version;
}
console.log("=========创游SDK登录成功", self.openId, self.version);
finishCb();
},
fail(err:any) {
console.log("=========创游SDK登录失败", err)
self.openId = "";
self.version = self.version;
finishCb();
}
}
)
})
getSceneCb((sceneGet:any) => {
scene = sceneGet;
self.scene = scene;
console.log("=========创游SDK场景值获取成功2", self.scene);
finishCb();
})
} else {
this.scene = 0; //默认场景值为0,等于web端
this.openId = localStorage.getItem("cy_sdk_web_openid") as string || "";
if (this.openId == null || this.openId == "") {
this.openId = "web_" + Date.now(); //web端默认openId为当前时间戳
localStorage.setItem("cy_sdk_web_openid", this.openId);
console.log("=========创游SDK web端登录成功(新用户)", this.openId);
} else {
console.log("=========创游SDK web端登录成功(老用户)", this.openId);
}
this.cyStoreDataDeal();
if (window["wx"]) {
self.trackEvent_uma("launch", { res: self.launchRes });
}
console.log("获取策略", this.appKey, this.version);
//拿配置
fetch("https://liehujt.com//api/campaign/strategy-new-info/" + this.appKey + "/" + this.version).then((response: Response) => {
// fetch("https://liehujt.com//api/campaign/strategy-info/" + this.appKey).then((response: Response) => {
return response.text()
}).then((value) => {
cb(JSON.parse(value))
}).catch((err) => {
console.log("创游策略参数获取失败", err)
cb(null);
})
}
}
//#endregion
//#region sdk存档
/**cySDK存档数据处理 */
private static cyStoreDataDeal() {
console.log("=========创游SDK存档数据处理");
const customData = new cyStoreData(this.version, this.getNowDay())
const localData = localStorage.getItem("cy_sdk_data");
this.storageData = localData ? JSON.parse(localData) : null;
if (!this.storageData) {
this.storageData = customData;
this.saveCyStoreData();
} else {
this.storageData.isNew = false;
for (let key in customData) {
if (!this.storageData.hasOwnProperty(key)) {
this.storageData[key] = customData[key];
}
}
this.saveCyStoreData();
}
}
private static saveCyStoreData() {
localStorage.setItem("cy_sdk_data", JSON.stringify(this.storageData));
}
//#endregion
//#region 关卡埋点
static levelStartTime = 0; //关卡开始时间
/**关卡开始
* @param level_type 关卡入口("normal"表示普通关卡,为后续拓展预留位置)
* @param level_name 关卡名称(全局唯一)
* @param level_index 关卡索引(全局唯一,与level_name一一对应)
*/
static onLevelStart(level_type: string, level_name: string, level_index: number) {
this.trackEvent({
event: "level_start",
level_type: level_type,
level_name: level_name,
level_index: level_index,
})
this.levelStartTime = new Date().getTime();
this.trackEvent_uma("startStage", { type: level_type, level: level_index });
}
/**关卡成功
* @param level_type 关卡入口("normal"表示普通关卡,为后续拓展预留位置)
* @param level_name 关卡名称(全局唯一)
* @param level_index 关卡索引(全局唯一,与level_name一一对应)
* @param dur 关卡耗时(单位:秒)
*/
static onLevelSuccess(level_type: string, level_name: string, level_index: number) {
let dur = Math.ceil((new Date().getTime() - this.levelStartTime) / 1000);
this.trackEvent({
event: "level_success",
level_type: level_type,
level_name: level_name,
level_index: level_index,
dur: dur
});
this.trackEvent_uma("passStage", { type: level_type, level: level_index });
}
/**关卡失败
* @param level_type 关卡类型("normal"表示普通关卡)
* @param level_name 关卡名称(全局唯一)
* @param level_index 关卡索引(全局唯一,与level_name一一对应)
* @param dur 关卡耗时(单位:秒)
*/
static onLevelFail(level_type: string, level_name: string, level_index: number) {
let dur = Math.ceil((new Date().getTime() - this.levelStartTime) / 1000);
this.trackEvent({
event: "level_fail",
level_type: level_type,
level_name: level_name,
level_index: level_index,
dur: dur
});
this.trackEvent_uma("failStage", { type: level_type, level: level_index });
}
/**广告请求
* @param mode 游戏模式("level"表示关卡模式,为后续拓展预留位置)
* @param mode_type 对应模式的类型(例如"normal"表示普通关卡)
* @param mode_index 对应模式的索引(例如:mode= "level" ,mode_type ="normal"时,等于关卡游戏模式,normal类型,的level_index,方便做关卡分析看板用)
* @param why 广告原因(例如:"tips")
*/
static onAdRequest(mode: string, mode_type: string, mode_index: number, why: string) {
this.trackEvent({
event: "ad_request",
mode: mode,
mode_type: mode_type,
mode_index: mode_index,
why: why,
});
this.trackEvent_uma("videoAdRequest", { type: why, mode: mode_type, level: mode_index });
this.getIaaInfo(mode_type, why, false);
}
/**广告取消
* @param mode 游戏模式("level"表示关卡模式,为后续拓展预留位置)
* @param mode_type 对应模式的类型(例如"normal"表示普通关卡)
* @param mode_index 对应模式的索引(例如:mode= "level" ,mode_type ="normal"时,等于关卡游戏模式,normal类型,的level_index,方便做关卡分析看板用)
* @param why 广告原因(例如:"tips")
*/
static onAdCancel(mode: string, mode_type: string, mode_index: number, why: string) {
this.trackEvent({
event: "ad_cancel",
mode: mode,
mode_type: mode_type,
mode_index: mode_index,
why: why,
});
this.trackEvent_uma("videoAdCancel", { type: why, mode: mode_type, level: mode_index });
}
/**广告完成
* @param mode 游戏模式("level"表示关卡模式,为后续拓展预留位置)
* @param mode_type 对应模式的类型(例如"normal"表示普通关卡)
* @param mode_index 对应模式的索引(例如:mode= "level" ,mode_type ="normal"时,等于关卡游戏模式,normal类型,的level_index,方便做关卡分析看板用)
* @param why 广告原因(例如:"tips")
*/
static onAdFinish(mode: string, mode_type: string, mode_index: number, why: string) {
this.trackEvent({
event: "ad_finish",
mode: mode,
mode_type: mode_type,
mode_index: mode_index,
why: why,
});
this.trackEvent_uma("videoAd", { type: why, mode: mode_type, level: mode_index });
this.getIaaInfo(mode_type, why, true);
}
//#endregion
//#region 时间获取接口
static getNowTime() {
return new Date().getTime() - new Date().getTimezoneOffset() * 60000;
}
static getNowDay() {
return Math.floor(this.getNowTime() / 86400000);
}
static getNowDateStr() {
const now = new Date(this.getNowTime());
const year = now.getFullYear();
const month = now.getMonth() + 1;
const day = now.getDate();
return year + "-" + (month < 10 ? "0" + month : month) + "-" + (day < 10 ? "0" + day : day);
}
//#endregion
//#region 加密接口
static encrypted(encryptedData, iv, cb) {
console.log("=========创游SDK上传用户加密数据");
this.trackEvent({ event: "encrypted_data", encryptedData: encryptedData, iv: iv },
(res) => {
console.log("=========创游SDK用户加密数据返回", res);
if (res.data) {
cb(res.data);
}
}
);
}
//#endregion
//#region iaa插件
static iaaData = {
isOn: false,
value: -1,
today_play_duration: -1,
weekly_login_cnt: -1,
continuous_login_days: -1,
value_exposure: -1, // 新增,本周期曝光次数,仅'labelId=='iaa_feature_v2'返回
};
static getIaaInfo(scene: string, type: string, isSuccess: boolean) {
let value: number;
let successCount: number;
for (let i = 0; i < this.storageData.videoCount.length; i++) {
let info = this.storageData.videoCount[i];
if (info.scene == scene && info.type == type) {
if (isSuccess) {
info.successTime++;
} else {
info.requestTime++;
}
value = info.requestTime;
successCount = info.successTime;
break;
}
}
if (!value) {
let info = new VideoCountInfo(scene, type);
if (isSuccess) {
info.successTime = 1;
info.requestTime = 1;
} else {
info.successTime = 0;
info.requestTime = 1;
}
value = info.requestTime;
successCount = info.successTime;
this.storageData.videoCount.push(info);
}
console.log("iaa广告次数计算,scene:", scene, "type:", type, "value:", value, "successCount:", successCount);
this.saveCyStoreData();
if (!isSuccess) return;
if (!window["miniGameCommon"] || !window["miniGameCommon"].getUserLabel) {
console.warn("=========创游SDK iaa插件未接入,无法获取iaa信息");
return;
}
const data = {
gift_scene: scene,//广告触发场景(如玩法单局内,局外签到,局外数值养成等)
gift_name: type,//广告设计点(如单局内的复活卡、刷新卡,单局外养成资源等)
gift_value: value,//游戏内用户点击该广告位的次数(指点击按钮的次数、无论用户是否成功拉起广告,都视为1次点击)
gift_value_success: successCount//游戏内用户成功看完广告的次数
}
const self = this;
//30秒后执行
setTimeout(() => {
window["miniGameCommon"].getUserLabel({
reportData: JSON.stringify(data),// reportdata是字符串,注意不要传入object
labelId: 'iaa_feature_v2' // 注意labelid必填 iaa_feature,若传入其他labelId会报错
}).then((res) => {
console.log("=========创游SDK iaa2.0插件获取信息成功", res);
self.trackEvent_uma("iaa2First", { res: res });
if (res.encryptedData) {
self.encrypted(res.encryptedData, res.iv, (data) => {
self.iaaData.isOn = true;
self.iaaData.value = data.value;
self.iaaData.today_play_duration = data.today_play_duration;
self.iaaData.weekly_login_cnt = data.weekly_login_cnt;
self.iaaData.continuous_login_days = data.continuous_login_days;
self.iaaData.value_exposure = data.value_exposure;
self.trackEvent_uma("iaa2Second");
});
}
}).catch((err) => {
console.log("=========创游SDK iaa2.0插件获取失败", err);
});
}, 30 * 1000);
}
//#endregion
}
class VideoCountInfo {
scene: string;
type: string;
requestTime: number = 0;
successTime: number = 0;
constructor(scene, type) {
this.scene = scene;
this.type = type;
this.requestTime = 0;
this.successTime = 0;
}
}
class cyStoreData {
version: string;
playTime: number;
isNew: boolean;
firstLoginDay: number;
videoCount: VideoCountInfo[];
firstLoginDateStr: string;
constructor(version, firstDay) {
this.version = version;
this.playTime = 0;
this.isNew = true;
this.firstLoginDay = firstDay;
this.videoCount = [];
this.firstLoginDateStr = CySDK.getNowDateStr();
}
}