balidrop-chrome开发概述
-
定义应用的描述信息和插件的图标以及权限和prop说明。
-
动态的加载background.js。
这里采用动态的加载background.js主要是有助于代码的修改,默认是服务器下面的 “/borwser-google/background.js”,在background.js中主要执行了以下初始化: -
根据版本号缓存将要注入页面的JS脚本,例如,淘宝,天猫,京东,速卖通等JS脚本。
owner.asyncAjaxResources = function(func) { //想获取本地的版本控制名称 owner.getLocalStorage(owner.CACHE_JS_VERSION_DATAS_NAME, function(storeData) { //从后台服务器获取版本文件 owner.requestServer4StaticResource("/borwser-google/browser-json.json?random=" + Math.random(), "json", function(callbackData) { //查看当前的版本文件是否为空 if(callbackData && (typeof callbackData == 'object') && (!jQuery.isEmptyObject(callbackData)) && (!(callbackData instanceof Array))) { //开始比较数据 var newStoreData = {}; var nameVersionDatas = callbackData; if(jQuery.isEmptyObject(storeData)) { //清空 //开始存入值 for(var key in nameVersionDatas) { var nameVersionDataArray = nameVersionDatas[key]; var newVersionDataArray = []; for(var i = 0; i < nameVersionDataArray.length; i++) { var nameVersionData = nameVersionDataArray[i]; var newVersionData = { 'ov': 0, 'path': nameVersionData['path'], 'nv': nameVersionData['version'], 'js': "", 'match': nameVersionData['match'] } newVersionDataArray.push(nameVersionData); } //保存数据 newStoreData[key] = newVersionDataArray; } } else { //只更新部分 for(var key in nameVersionDatas) { if(key in storeData) { //之前已经存在,那么只写入版本更新 var sd = storeData[key]; var newVersionDataArray = []; var nameVersionDataArray = nameVersionDatas[key]; for(var i = 0; i < nameVersionDataArray.length; i++) { var nameVersionData = nameVersionDataArray[i]; var path = nameVersionData['path']; //之前的数据 var oldData = owner.findMatchJsByKeyFromArray(sd, path, "path"); //说明之前没有事新增的 if((!oldData) || (jQuery.isEmptyObject(oldData))) { var newVersionData = { 'ov': 0, 'path': nameVersionData['path'], 'nv': nameVersionData['version'], 'js': "", 'match': nameVersionData['match'] } } else { var newVersionData = { 'ov': oldData['ov'], 'path': nameVersionData['path'], 'nv': nameVersionData['version'], 'js': oldData['js'], 'match': nameVersionData['match'] } newVersionDataArray.push(newVersionData); } } newStoreData[key] = newVersionDataArray; } else { //新增的 for(var key in nameVersionDatas) { var nameVersionDataArray = nameVersionDatas[key]; var newVersionDataArray = []; for(var i = 0; i < nameVersionDataArray.length; i++) { var nameVersionData = nameVersionDataArray[i]; var newVersionData = { 'ov': 0, 'path': nameVersionData['path'], 'nv': nameVersionData['version'], 'js': "", 'match': nameVersionData['match'] } newVersionDataArray.push(nameVersionData); } //保存数据 newStoreData[key] = newVersionDataArray; } } } } //开始进行存储 owner.setLocalStorage(owner.CACHE_JS_VERSION_DATAS_NAME, newStoreData, function() { //开始进行执行调用 if(typeof func == 'function') { func(); } }); } }); }); }
-
监听页面的变化根据链接向页面中注入对应的执行脚本。
app.asyncAjaxResources(function() { //增加监听操作 chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { //查看当前是否已经登录了,如果登录了,那么 app.getLocalStorage("customer", function(customer) { if(customer && customer['id']) { var status = changeInfo.status; var currentUrl = tab.url; //获取Storage数据 app.getLocalStorage(app.CACHE_JS_VERSION_DATAS_NAME, function(storeData) { //开始加入自定义脚本 var hostDataMap = app.findExecuteJSByUrl(currentUrl, storeData); if(hostDataMap && (!jQuery.isEmptyObject(hostDataMap))) { var hostData = hostDataMap['value']; var hostKey = hostDataMap['key']; var index = hostDataMap['index']; if(status == 'loading') { chrome.tabs.insertCSS(tabId, { "file": "css/iziToast.min.css" }); chrome.tabs.insertCSS(tabId, { "file": "css/loaders.css" }); chrome.tabs.insertCSS(tabId, { "file": "css/balidrop-modal.css" }); chrome.tabs.executeScript(tabId, { "file": "js/iziToast.min.js" }); chrome.tabs.executeScript(tabId, { "file": "js/loaders.js" }); chrome.tabs.executeScript(tabId, { "file": "js/balidropModal.js" }); //如果category 存在且数组大于0 if(customer['category'] && customer['category'].length > 0) { var datas = [{ id: 'root', pid: -1, title: '不分类' }]; var categorys = customer['category']; app.reCategorys(datas, categorys); var balidropTreeDatas = { 'title': "选择产品类型", 'closeBtnText': '关闭', 'okBtnText': '确定', 'datas': datas } chrome.tabs.executeScript(tabId, { "code": "var balidropTreeDatas=" + JSON.stringify(balidropTreeDatas) }); chrome.tabs.executeScript(tabId, { "file": "js/blidropTree.js" }); } //添加loadding等待框 var executeCode = "function waitLoadding(){var isLoadingInteval=window.setInterval(function(){if(window.pageLoader){window.clearInterval(isLoadingInteval);var containerDiv=document.getElementById('loader-container-2018');if(!containerDiv){window.pageLoader.show(15000)}}},500)}waitLoadding();"; var serchaDataJson = app.getParameters(currentUrl); if(serchaDataJson && serchaDataJson['items']) { var loadText = '批量选品中<span class=batchLoadding >0/' + (serchaDataJson['items'].split(",")).length + '</span>,请不要关闭该窗口'; executeCode = "function waitLoadding(){var isLoadingInteval=window.setInterval(function(){if(window.pageLoader){window.clearInterval(isLoadingInteval);var containerDiv=document.getElementById('loader-container-2018');if(!containerDiv){window.pageLoader.show(600000,'" + loadText + "', 'line-scale-pulse-out')}}},500)} var containerDiv=document.getElementById('loader-container-2018'); if(!(containerDiv&&containerDiv.style.display=='none')){waitLoadding()};"; } chrome.tabs.executeScript(tabId, { "code": executeCode, "runAt": "document_end", "matchAboutBlank": true }); } else if(status == 'complete') { //获取最新脚本数据 if((hostData['ov'] != hostData['nv']) || (!hostData['js']) || (app.isDebug)) { app.requestServer4StaticResource(hostData['path'] + "?random=" + Math.random(), "text", function(callbackData) { if(callbackData) { hostData['ov'] = hostData['nv']; hostData['js'] = callbackData; //开始进行保存 storeData[hostKey][index] = hostData; //保存且执行监听 app.setLocalStorage(app.CACHE_JS_VERSION_DATAS_NAME, storeData, function() { chrome.tabs.executeScript(tabId, { "code": hostData['js'], "runAt": "document_end", "matchAboutBlank": true }); }); } }); } else { if(hostData['js']) { chrome.tabs.executeScript(tabId, { "code": hostData['js'], "runAt": "document_end", "matchAboutBlank": true }); } } } } }); } }); }); });
-
监听页面上提交 代购、刊登等 商品的信息将其提交到balidrop服务器。
//监听,且提交到服务器 chrome.extension.onMessage.addListener(function(request, sender, callback) { if(typeof request == 'string') { request = JSON.parse(request); } var dataType = request.dataType; var datas = request.datas; var tabId = sender.tab.id; //校验 if(!datas) { chrome.tabs.executeScript(tabId, { "code": "iziToast.error({title: 'Balidrop',message: 'Please wait for loading to complete',position: 'topRight',transitionIn: 'bounceInLeft'})" }); callback("success"); return true; } else { try { var validate = (typeof datas == 'string') ? JSON.parse(datas) : JSON.stringify(datas); } catch(e) { chrome.tabs.executeScript(tabId, { "code": "iziToast.error({title: 'Balidrop',message: 'Please wait for loading to complete',position: 'topRight',transitionIn: 'bounceInLeft'})" }); callback("success"); return true; } } switch(dataType) { case 'Product': chrome.tabs.executeScript(tabId, { "code": "pageLoader.show();" }); app.requestServer4JSON("/products", (typeof datas == 'string') ? datas : JSON.stringify(datas), function(response) { if(response && response.flag) { chrome.tabs.executeScript(tabId, { "code": "pageLoader.hide();iziToast.success({title: 'Balidrop',timeout:2000,message: '" + (response.msg || 'success') + "',position: 'topRight',transitionIn: 'bounceInLeft'})" }); } else { var message = (response && response.msg) || 'unknown msg'; chrome.tabs.executeScript(tabId, { "code": "pageLoader.hide();iziToast.error({title: 'Balidrop',timeout:2000,message: '" + message + "',position: 'topRight',transitionIn: 'bounceInLeft'})" }); } }); break; case 'Purchasing': chrome.tabs.executeScript(tabId, { "code": "pageLoader.show();" }); app.requestServer("/agentOrder/save", 'post', (typeof datas == 'string') ? JSON.parse(datas) : datas, function(response) { if(response && response.flag) { chrome.tabs.executeScript(tabId, { "code": "pageLoader.hide();iziToast.success({title: 'Balidrop',timeout:2000,message: '" + (response.msg || 'success') + "' || 'success',position: 'topRight',transitionIn: 'bounceInLeft'})" }); } else { var message = (response && response.msg) || 'unknown msg'; chrome.tabs.executeScript(tabId, { "code": "pageLoader.hide();iziToast.error({title: 'Balidrop',timeout:2000,message: '" + message + "',position: 'topRight',transitionIn: 'bounceInLeft'})" }); } }); break; case 'batchProduct': app.requestServer4JSON("/products/batch", (typeof datas == 'string') ? datas : JSON.stringify(datas), function(response) { if(response && response.flag) { chrome.tabs.executeScript(tabId, { "code": "iziToast.success({title: 'Balidrop',timeout:10000,message: '" + (response.msg || 'success') + "' || 'success',position: 'topRight',transitionIn: 'bounceInLeft'});pageLoader.hide();" }); } else { var message = (response && response.msg) || 'unknown msg'; chrome.tabs.executeScript(tabId, { "code": "iziToast.error({title: 'Balidrop',timeout:5000,message: '" + message + "',position: 'topRight',transitionIn: 'bounceInLeft'});pageLoader.hide();" }); } }, function(xhr, type, errorThrown) { alert(type + ":" + errorThrown); var message = 'unknown msg'; chrome.tabs.executeScript(tabId, { "code": "iziToast.error({title: 'Balidrop',timeout:5000,message: '" + message + "',position: 'topRight',transitionIn: 'bounceInLeft'});pageLoader.hide();" }); }, 50000); break; } callback("success"); return true; });
4、同步该用户下的balidrop的产品库存信息。
//开始进行监控操作 function watchProductProcess() { app.getLocalStorage("stockWindowObject", function(stockData) { //如果是运行的状态,开始进行循环处理了 var datas = stockData.datas || []; var executeIndex = stockData.executeIndex || 0; try { if(stockData.running == true) { if(executeIndex > (datas.length - 1)) { stockData.message = "<span class='glyphicon glyphicon-off' style='width: 18px;height: 18px;'></span> Completed..." + (datas.length); stockData.running = false; } else { stockData.message = "<span class=\"mui-spinner\" style='width: 18px;height: 18px;'></span> Processing..." + (stockData.executeIndex + 1) + "/" + (datas.length); //ajax进行处理 var processData = datas[executeIndex]; var watchUrl = stockData.watchUrl; if(!watchUrl) { stockData.message = "<span class='glyphicon glyphicon-exclamation-sign' style='width: 18px;height: 18px;'></span> Error...The WatchUrl Not Found"; stockData.running = false; } else { //加1 stockData.executeIndex = executeIndex + 1; var intervalTime = (stockData.formData && stockData.formData.singleIntervalTime) || 1; var itemId = processData['platformProductId']; if(itemId) { watchUrl = watchUrl.replace(/\{platformProductId\}/gi, itemId); app.requestServer4StaticResource(watchUrl, "text", function(returnData) { try { //-----解析数据-----start function mtopjsonp1(data) { return data; } var resultData = eval(returnData); //开始同步服务器。。。。。。。。。。。。。...开始解析数据 if(resultData.data.trade) { var redirectUrl = resultData.data.trade.redirectUrl; //说明已经下架了 if(redirectUrl && redirectUrl.toLowerCase().indexOf('expired') >= 0) { console.info("xxxxx下架了"); var submitData = { "productId": processData['id'], "skuIds": [] } //提交到服务器进行更新 app.requestServer4JSON("/products/downProductByMonitor", JSON.stringify(submitData), function(response) { if(response && response.flag) { console.info("type 1 down success..." + processData['id']); }; }); } } else if(resultData.data.apiStack) { var apiStack = resultData.data.apiStack; if(apiStack && (apiStack instanceof Array) && apiStack.length > 0) { var value = apiStack[0]['value']; var jsonValue = JSON.parse(value); if(jsonValue && jsonValue.trade) { var trade = jsonValue.trade; if(trade.buyEnable == false) { var submitData = { "productId": processData['id'], "skuIds": [] } //提交到服务器进行更新 app.requestServer4JSON("/products/downProductByMonitor", JSON.stringify(submitData), function(response) { if(response && response.flag) { console.info("type 2 down success..." + processData['id']); }; }); } else { var skus = resultData.data.skuBase && resultData.data.skuBase.skus; //说明含有sku的商品,只下架部分 if((skus instanceof Array) && skus.length > 0) { var sku2info = (jsonValue.skuCore && jsonValue.skuCore.sku2info) || {}; var skuIdPropMapping = {}; for(var i = 0; i < skus.length; i++) { var skuId = skus[i]['skuId']; if(skuId in sku2info) { var sku2InfoItem = sku2info[skuId]; var propPath = skus[i]['propPath']; skuIdPropMapping[propPath] = sku2InfoItem['quantity']; } } //开始进行比较 if(Object.getOwnPropertyNames(skuIdPropMapping).length > 0) { //开始进行处理 var deleteSkuIdsArray = [] var serverSkuIds = (processData['skuIds'] && JSON.parse(processData['skuIds'])) || []; if(serverSkuIds.length > 0) { for(var i = 0; i < serverSkuIds.length; i++) { if(!(serverSkuIds[i] in skuIdPropMapping)) { deleteSkuIdsArray.push(serverSkuIds[i]); } } } //开始进行删除 if(deleteSkuIdsArray > 0) { var submitData = { "productId": processData['id'], "skuIds": deleteSkuIdsArray } //提交到服务器进行更新 app.requestServer4JSON("/products/downProductByMonitor", JSON.stringify(submitData), function(response) { if(response && response.flag) { console.info("type 3 down success..." + processData['id']); }; }); } } }; } } } } //继续递归调用上面的 app.setLocalStorage("stockWindowObject", stockData, function() { //通知 notifyStockMonitor(stockData, 'RUNNING'); //继续执行 window.setTimeout(function() { watchProductProcess(); }, intervalTime * 1000); }); } catch(e) { console.error("process ajax data error") //继续递归调用上面的 app.setLocalStorage("stockWindowObject", stockData, function() { //通知 notifyStockMonitor(stockData, 'RUNNING'); //继续执行 window.setTimeout(function() { watchProductProcess(); }, intervalTime * 1000); }); } }, function(a1, a2, a3) { console.error("reqeust url failed:" + a2); //继续递归调用上面的 app.setLocalStorage("stockWindowObject", stockData, function() { //通知 notifyStockMonitor(stockData, 'RUNNING'); //继续执行 window.setTimeout(function() { watchProductProcess(); }, intervalTime * 1000); }); }); } else { app.setLocalStorage("stockWindowObject", stockData, function() { notifyStockMonitor(stockData, 'RUNNING'); //继续执行 window.setTimeout(function() { watchProductProcess(); }, intervalTime * 1000) }); } } } if(stockData.running == false) { app.setLocalStorage("stockWindowObject", stockData, function() { notifyStockMonitor(stockData, 'RUNNING'); }); } }; } catch(e) { console.error("execute " + executeIndex + " Error...."); if(stockData.running == true) { if(executeIndex <= (datas.length - 1)) { //重试 if(stockData.executeIndex == executeIndex) { stockData.executeIndex = executeIndex + 1; stockData.message = "<span class=\"mui-spinner\" style='width: 18px;height: 18px;'></span> Processing " + (executeIndex + 1) + "/" + (datas.length) + " Error:" + e; } } else { stockData.message = "<span class='glyphicon glyphicon-off' style='width: 18px;height: 18px;'></span> Completed..." + (datas.length); stockData.running = false; } app.setLocalStorage("stockWindowObject", stockData, function() { var intervalTime = (stockData.formData && stockData.formData.singleIntervalTime) || 1; //通知 notifyStockMonitor(stockData, 'RUNNING'); //继续执行 window.setTimeout(function() { watchProductProcess(); }, intervalTime * 1000); }); } } }); } function notifyStockMonitor(stockData, status) { var showWinId = stockData.id; if(showWinId) { try { chrome.tabs.query({ 'active': true, 'windowId': showWinId }, function(tabs) { if(tabs && tabs.length > 0) { chrome.tabs.sendMessage(tabs[0]['id'], status, function(response) { console.info(response); }); } }); } catch(e) { console.info(e); } } }
5、注意事项:插件生效的前提是用户已经登录了,否则不会注入脚本的。
针对具体平台的脚本开发
-
在脚本的JS中主要包含两个步骤:商品的直接采购和商品的刊登。
采购:{ "listingTitle": "商品名称", "orignalUrl": "该商品的链接", "currencyName": "CNY",//币种 "imageUrl": "图片的链接", "fromType": "BROWSER_APP" "price":200.00,//金额 "skuMapKeyName":"蓝色/xl", "skuCnt":1,//数量 "freightPrice":0,//运费 }
2.刊登含SKU商品
{ "productName":"商品名称", "hasSku":false,//是否是sku的商品 "platform":"JD",//平台的名称, "productUrl":"产品的原始链接", "desc":"商品的详情", "image":{ "ShopWindowImg":["a.jpg","b.jpg"],//橱窗图 "DescImg":["c.jpg","d.jpg"],//详情图 "AttrImg":["e.jpg","f.jpg"]//属性图 } "attrNames":[ { "name":"颜色", "attrNo":"A1"//名字任意取,不重复即可 }, { "name":"尺码", "attrNo":"B1"//名字任意取,不重复即可 } ], "attrValues":[ { "value":"红色", "attrNo":"AA1",//名字任意取,不重复即可 "attrNameNo":"A1"/** 必须和上面的attrNmaes中的attrNo一样 */ }, { "value":"黄色",//名字任意取,不重复即可 "attrNo":"AA2", "attrNameNo":"A1"/** 必须和上面的attrNmaes中的attrNo一样 */ },{ { "value":"XL", "attrNo":"BB1",//名字任意取,不重复即可 "attrNameNo":"B1"/** 必须和上面的attrNmaes中的attrNo一样 */ }, { "value":"XXL", "attrNo":"BB2", "attrNameNo":"B1"/** 必须和上面的attrNmaes中的attrNo一样 */ } ], "productSku":[ { "skuAttr":"A1:AA1;B1:BB1",//顺序很关键:名称:值;名称:值....... 且都是attrNo "skuId":"skuid"//真实的skuId "stock":"100",//库存 "price":100,//价格 } ] }
3、不含SKU
{ "productName":"商品名称", "sku":false,//是否是sku的商品 "platform":"JD",//平台的名称, "productUrl":"产品的原始链接", "desc":"商品的详情", "image":{ "ShopWindowImg":["a.jpg","b.jpg"],//橱窗图 "DescImg":["c.jpg","d.jpg"],//详情图 "AttrImg":["e.jpg","f.jpg"]//属性图 } "noSkuProp":{ "platformPrice":100,//价格 "stock":1000,//库存, "price":999,//金额 } }
浏览器登录的逻辑代码:
- 先判断用户是否登录balidrop的网站,如果登录,那么插件里面直接显示已经登录。
2.如果客户没有登录toppgo的网站应用,那么执行登录的信息。
根据手机APP的逻辑将用户的 用户名和密码,进行加密,然后直接请求用户信息。