一、功能介绍
主要是用于导出我们app 应用的埋点数据,2个app ,4个端的埋点数据,聚合成一个excel文档
二、实现思路
主要通过content.js页面注入,操作dom,抓取页面信息,利用浏览器缓存数据信息,通过jsontoexcel,转成文件
三、详细设计及编码
1、service_worker相关配置和编码
manifest.json 配置
"background": {
"service_worker": "background.js",
"type": "module"
},
background.js 编码
chrome.runtime.onInstalled.addListener(() => {
chrome.contextMenus.create({
id: "chatglm",
title: "使用智谱清言",
contexts: ["selection"] // "page" 表示菜单项将出现在页面的右键菜单中
});
chrome.contextMenus.create({
id: "chatglmPage",
title: "使用智谱清言",
contexts: ["page"] // "page" 表示菜单项将出现在页面的右键菜单中
});
chrome.contextMenus.create({
id: "allApp",
title: "全部应用",
contexts: ["page"] // "page" 表示菜单项将出现在页面的右键菜单中
});
chrome.contextMenus.create({
id: "Android1",
title: "安卓1埋点",
contexts: ["page"] // "page" 表示菜单项将出现在页面的右键菜单中
});
chrome.contextMenus.create({
id: "Android2",
title: "安卓2埋点",
contexts: ["page"] // "page" 表示菜单项将出现在页面的右键菜单中
});
chrome.contextMenus.create({
id: "IOS2",
title: "IOS1埋点",
contexts: ["page"] // "page" 表示菜单项将出现在页面的右键菜单中
});
chrome.contextMenus.create({
id: "IOS2",
title: "IOS2埋点",
contexts: ["page"] // "page" 表示菜单项将出现在页面的右键菜单中
});
});
在浏览器插件上安装的时候添加菜单项
效果如下
2、 监听右键菜单项的点击事件,并跳转到指定页面
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (["chatglmPage", "allApp", "Android1", "Android2", "IOS1", "IOS1"].includes(info.menuItemId)) {
// 当用户点击右键菜单的“示例菜单项”时,执行以下操作
chrome.scripting.executeScript({
target: { tabId: tab.id },
function: openInNewTab,
args: [info.menuItemId]
});
}
if (info.menuItemId == "chatglm") {
chrome.tabs.create({
url: "https://chatglm.cn/",
active: true
});
}
});
function openInNewTab(id) {
let url = ''
let obj = {
"Android1": '6461d579', "Android2": '6461d6b', "IOS1": '6461d65', "IOS2": '6461d6c'
}
if (id == "allApp") {
url = 'https://mobile.umeng.com/platform/apps/list'
} else if (id == "chatglmPage") {
url = 'https://chatglm.cn/'
} else {
url = `https://mobile.umeng.com/platform/${obj[id]}/function/events/dashboard`
console.log(id, obj[id], url)
}
window.location.href = url;
}
3、content_scripts 相关配置和编码
manifest.json 配置
"content_scripts": [
{
"matches": [
"https://*/*"
],
"css": [
"bossjob.css"
],
"js": [
"jquery-2.1.4.min.js",
"JsonExportExcel.min.js",
"bossjob.js"
]
}
],
bossjob.js 编码
浏览器缓存函数封装
async function getAllAppEvent() {
// let ret = await clearStorage()
// console.log(ret,'dsfdfds')
let arr = ["Android1", "Android2", "IOS1", "IOS2"]
let ret = await getStorageItem(null)
if (!ret.Android1) {
chrome.runtime.sendMessage({ action: "showNotification", title: "Notification", message: `缺少安卓1数据!` });
return openInNewTab("Android1")
}
if (!ret.Android2) {
chrome.runtime.sendMessage({ action: "showNotification", title: "Notification", message: `缺少安卓2数据!` });
return openInNewTab("Android2")
}
if (!ret.IOS1) {
chrome.runtime.sendMessage({ action: "showNotification", title: "Notification", message: `缺少IOS1数据!` });
return openInNewTab("IOS1")
}
if (!ret.IOS2) {
chrome.runtime.sendMessage({ action: "showNotification", title: "Notification", message: `缺少IOS2数据!` });
return openInNewTab("IOS2")
}
console.log('all', ret)
let promiseArr = []
arr.map((item) => {
promiseArr.push(getStorageItem([item]))
})
let mergedData = []
try {
let mergedDataArr = await Promise.all(promiseArr)
if (mergedDataArr) {
mergedData = mergeObjectsWithSameEventID(mergedDataArr);
console.log(mergedData);
}
} catch (error) {
console.error('发生错误:', error);
}
try {
let titleData = await getStorageItem('titleData')
if (titleData) {
console.log(mergedData, titleData)
let option = {};
let date = getcurrentDate()
option.fileName = `全部应用Event-${date}`
option.datas = [
{
sheetData: mergedData,
sheetHeader: titleData,
sheetFilter: ['EventID', 'EventName', 'NumberofMessagesYesterday', 'NumberofMessagesToday', 'NumberofIndependentUsersYesterday']
}
]
//引用ExportJsonExcel方法导出Excel文件
let toExcel = new ExportJsonExcel(option);
toExcel.saveExcel();
}
} catch (error) {
console.error('发生错误:', error);
}
}
function getStorageItem(key) {
return new Promise((resolve, reject) => {
chrome.storage.sync.get(key, (result) => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {
if (key == null) {
resolve(result);
}
resolve(result[key]);
}
});
});
}
function setStorageItem(data) {
return new Promise((resolve, reject) => {
chrome.storage.sync.set(data, () => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {
resolve(true);
}
});
});
}
function removeStorageItem(key) {
return new Promise((resolve, reject) => {
chrome.storage.sync.remove(key, (result) => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {
resolve(true);
}
});
});
}
function clearStorage() {
return new Promise((resolve, reject) => {
chrome.storage.sync.clear((result) => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {
resolve(true);
}
});
});
}
特定页面注入dom,并绑定函数执行相关操作
//向页面注入一个div并添加a标签
$('body').append('<div class="mydiv"></div>');
let obj = {
"allApp": "platform/apps/list",
"Android1": '6461d579', "Android2": '6461d6b', "IOS1": '6461d65', "IOS2": '6461d6cf'
}
let currentUrl = window.location.href
if (currentUrl.includes('umeng')) {
//向页面注入一个div并添加a标签
$('.mydiv').append('<a id="clearAllApp" class="mya" href="javascript:;">清除缓存</a>'
);
//向页面注入一个div并添加a标签
$('.mydiv').append('<a id="allApp" class="mya" href="javascript:;">导出应用列表</a>'
);
//向页面注入一个div并添加a标签
$('.mydiv').append('<a id="Android1" class="mya" href="javascript:;">导出安卓1埋点</a>'
);
//向页面注入一个div并添加a标签
$('.mydiv').append('<a id="Android2" class="mya" href="javascript:;">导出安卓2埋点</a>');
//向页面注入一个div并添加a标签
$('.mydiv').append('<a id="IOS1" class="mya" href="javascript:;">导出IOS1埋点</a>'
);
//向页面注入一个div并添加a标签
$('.mydiv').append('<a id="IOS2" class="mya" href="javascript:;">导出IOS2埋点</a>');
//向页面注入一个div并添加a标签
$('.mydiv').append('<a id="allAppEvent" class="mya" href="javascript:;">导出所有应用埋点</a>');
$('#clearAllApp').on('click', function () {
clearStorage()
})
//向注入的a标签绑定点击事件
$('#Android1').on('click', function () {
getDataApp('Android1', '安卓1埋点')
})
$('#Android2').on('click', function () {
getDataApp('Android2', '安卓2埋点')
})
//向注入的a标签绑定点击事件
$('#IOS1').on('click', function () {
getDataApp('IOS1', 'IOS1埋点')
})
$('#IOS2').on('click', function () {
getDataApp('IOS2', 'IOS2埋点')
})
$('#allAppEvent').on('click', function () {
getAllAppEvent()
})
//向注入的a标签绑定点击事件
$('#allApp').on('click', function () {
//判断jq是否查找到元素
if ($('.ant-table-tbody').find('.ant-table-row').length > 0) {
//创建数组
let data = [];
let titleData = []
$('.ant-table-thead').find('.ant-table-header-column').each(function (index) {
let a = $(this).find('.ant-table-column-title').text().match(/\S+/g)
console.log(a, typeof a)
if (a && a[0] != '平台') {
titleData.push(a[0])
}
})
console.log(titleData, 'titleData')
$('.ant-table-tbody').find('.ant-table-row').each(function (index) {
let a = $(this).find('.ant-table-column-has-actions.ant-table-column-has-sorters.ant-table-row-cell-break-word')
let arr = []
a.each(function (index, value) {
let b = value.
innerText
.match(/\S+/g)[0]
// console.log(b, index, 'b')
arr.push(b)
})
console.log(arr, 'arr')
data[index] = {
ApplicationName: arr[0],
NewUserToday: arr[1],
NewUserYesterday: arr[2],
ActiveUserToday: arr[3],
ActiveUserYesterday: arr[4],
NumberofStartsToday: arr[5],
NumberofStartsYesterday: arr[6],
AccumulatedUsersToday: arr[7]
}
})
console.log(titleData, 'titleData', data)
//配置option,用于ExportJsonExcel
let option = {};
let date = getcurrentDate()
option.fileName = `应用列表用户分析-${date}`
option.datas = [
{
sheetData: data,
sheetHeader: titleData,
sheetFilter: ['ApplicationName', 'NewUserToday', 'NewUserYesterday', 'ActiveUserToday', 'ActiveUserYesterday', 'NumberofStartsToday', 'NumberofStartsYesterday', 'AccumulatedUsersToday']
}
]
//引用ExportJsonExcel方法导出Excel文件
let toExcel = new ExportJsonExcel(option);
toExcel.saveExcel();
} else {
chrome.runtime.sendMessage({ action: "showNotification", title: "Notification", message: "没有匹配到应用列表用户分析!" });
// alert('没有匹配到应用列表用户分析!');
}
})
Object.entries(obj).map(item => {
if (currentUrl.includes(`${item[1]}/function/events/dashboard`)) {
$(`#${item[0]}`).show()
} else {
$(`#${item[0]}`).hide()
}
console.log(item[0], item[1])
})
}
获取全部应用数据,通过缓存判断,四个应用缺失哪个,浏览器通知提示,跳转到哪个应用的埋点数据页面,去获取相关数据
// 在新标签页中打开网址
function openInNewTab(id) {
let url = ''
let obj = {
"Android1": '6461d57', "Android2": '6461d6', "IOS1": '6461d65', "IOS2": '6461'
}
url = `https://mobile.umeng.com/platform/${obj[id]}/function/events/dashboard`
console.log(id, obj[id], url)
// window.open(url);
redirect(url)
}
async function getAllAppEvent() {
// let ret = await clearStorage()
// console.log(ret,'dsfdfds')
let arr = ["Android1", "Android2", "IOS1", "IOS2"]
let ret = await getStorageItem(null)
if (!ret.Android1) {
chrome.runtime.sendMessage({ action: "showNotification", title: "Notification", message: `缺少安卓1数据!` });
return openInNewTab("Android1")
}
if (!ret.Android2) {
chrome.runtime.sendMessage({ action: "showNotification", title: "Notification", message: `缺少安卓2数据!` });
return openInNewTab("Android2")
}
if (!ret.IOS1) {
chrome.runtime.sendMessage({ action: "showNotification", title: "Notification", message: `缺少IOS1数据!` });
return openInNewTab("IOS1")
}
if (!ret.IOS2) {
chrome.runtime.sendMessage({ action: "showNotification", title: "Notification", message: `缺少IOS2数据!` });
return openInNewTab("IOS2")
}
console.log('all', ret)
let promiseArr = []
arr.map((item) => {
promiseArr.push(getStorageItem([item]))
})
let mergedData = []
try {
let mergedDataArr = await Promise.all(promiseArr)
if (mergedDataArr) {
mergedData = mergeObjectsWithSameEventID(mergedDataArr);
console.log(mergedData);
}
} catch (error) {
console.error('发生错误:', error);
}
try {
let titleData = await getStorageItem('titleData')
if (titleData) {
console.log(mergedData, titleData)
let option = {};
let date = getcurrentDate()
option.fileName = `全部应用Event-${date}`
option.datas = [
{
sheetData: mergedData,
sheetHeader: titleData,
sheetFilter: ['EventID', 'EventName', 'NumberofMessagesYesterday', 'NumberofMessagesToday', 'NumberofIndependentUsersYesterday']
}
]
//引用ExportJsonExcel方法导出Excel文件
let toExcel = new ExportJsonExcel(option);
toExcel.saveExcel();
}
} catch (error) {
console.error('发生错误:', error);
}
}
效果图如下
获取当前应用的数据并导出缓存,以及聚合所有应用数据导出文件
// 创建一个函数来合并具有相同 EventID 的对象的数值属性
function mergeObjectsWithSameEventID(arrays) {
// 创建一个结果对象,用于存储合并后的数据
let result = {};
// 遍历每个数组
arrays.forEach(array => {
array.forEach(obj => {
// 如果结果对象中还没有这个 EventID,创建一个新的对象
if (!result[obj.EventID]) {
result[obj.EventID] = {
EventID: obj.EventID,
EventName: obj.EventName,
NumberofMessagesToday: obj.NumberofMessagesToday,
NumberofIndependentUsersYesterday: 0,
NumberofMessagesYesterday: 0
};
}
// 将数值相加
result[obj.EventID].NumberofIndependentUsersYesterday += parseInt(obj.NumberofIndependentUsersYesterday, 10);
result[obj.EventID].NumberofMessagesYesterday += parseInt(obj.NumberofMessagesYesterday, 10);
});
});
// 将结果对象转换回数组
let mergedArray = Object.values(result);
// 返回合并后的数组
return mergedArray;
}
function getcurrentDate() {
// 创建一个新的Date对象,表示当前日期和时间
let currentDate = new Date();
// 获取年、月、日信息
let year = currentDate.getFullYear(); // 获取当前年份
let month = currentDate.getMonth() + 1; // 获取当前月份(注意:月份从0开始,需要加1)
let day = currentDate.getDate(); // 获取当前是几号
// 将年、月、日拼接成日期字符串
let formattedDate = year + '-' + (month < 10 ? '0' + month : month) + '-' + (day < 10 ? '0' + day : day);
console.log(formattedDate); // 输出当前日期,格式为YYYY-MM-DD
return formattedDate
}
async function getDataApp(id, name) {
setTimeout(async () => {
//判断jq是否查找到元素
if ($('.ant-table-tbody').find('.ant-table-row').length > 0) {
//创建数组
let titleData = []
//创建数组
let data = [];
$('.ant-table-thead').find('.ant-table-header-column').each(function (index) {
let a = $(this).find('.ant-table-column-title').text().match(/\S+/g)
console.log(a, typeof a, 'Android1')
if (a && a[0] != '详情') {
titleData.push(a[0])
}
})
console.log(titleData, 'titleData')
$('.ant-table-tbody').find('.ant-table-row').each(function (index) {
let a = $(this).find('.ant-table-column-has-actions.ant-table-column-has-sorters.ant-table-row-cell-break-word')
let arr = []
a.each(function (index, value) {
let b = value.
innerText
.match(/\S+/g)[0]
// console.log(b, index, 'b')
arr.push(b)
})
console.log(arr, 'arr')
data[index] = {
EventID: arr[0],
EventName: arr[1],
NumberofMessagesYesterday: arr[2],
NumberofMessagesToday: arr[3],
NumberofIndependentUsersYesterday: arr[4],
}
})
console.log(titleData, 'titleData', data)
//配置option,用于ExportJsonExcel
let option = {};
let date = getcurrentDate()
option.fileName = `${name}-${date}`
option.datas = [
{
sheetData: data,
sheetHeader: titleData,
sheetFilter: ['EventID', 'EventName', 'NumberofMessagesYesterday', 'NumberofMessagesToday', 'NumberofIndependentUsersYesterday']
}
]
try {
let ret = await setStorageItem({ [id]: data });
console.log(`${id}数据已存储`, ret);
} catch (error) {
console.error(`${id}数据error`, error);
}
try {
await setStorageItem({ titleData: titleData });
console.log(`titleData数据已存储`);
} catch (error) {
console.error(`titleData数据error`, error);
}
//引用ExportJsonExcel方法导出Excel文件
let toExcel = new ExportJsonExcel(option);
toExcel.saveExcel();
}
else {
chrome.runtime.sendMessage({ action: "showNotification", title: "Notification", message: `没有匹配到${name}!` });
}
}, 1000)
}
效果图如下
导出的结果文件如下