Commit 20ceeb35 authored by aiduck's avatar aiduck

优化

parent 74890fea
......@@ -3205,7 +3205,8 @@
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npm.taobao.org/lodash/download/lodash-4.17.15.tgz",
"integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg="
"integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg=",
"dev": true
},
"loose-envify": {
"version": "1.4.0",
......
......@@ -22,7 +22,6 @@
"author": "Dec-F",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.15",
"md5": "^2.2.1",
"rrweb": "^0.7.18",
"whatwg-fetch": "^3.0.0"
......
import "whatwg-fetch";
import utils from "./utils";
/**
* fetch请求封装
* @param {*} action 请求线上地址
* @param {*} params 请求入参
* @param {*} method 请求方式,默认get
* @param {*} options 其他参数
* options为对象格式,值:
* isLoading(是否激活请求加载动画)
* isJson(是否设置post请求头contentType为application/json)
* content 自定义请求参数
*/
export function fetch(action, params = {}, method = "get", options = {}) {
const token = utils.getCookie("token");
const XCsrfToken = utils.getCookie("csrf_token");
let url = action;
let option = {
method,
credentials: "same-origin",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
token: token,
"X-Csrf-Token": XCsrfToken,
},
};
if (options && options.content) {
option = Object.assign({}, option, options.content);
}
if (method === "post") {
if (options && options.isJson) {
option.body = JSON.stringify(params);
} else {
option = Object.assign({}, option, {
headers: {
Accept: "application/json,text/plain,*/*",
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
token: token,
"X-Csrf-Token": XCsrfToken,
},
});
option.body = utils.serialize(params);
}
}
if (method === "get") {
url = Object.keys(params).length ? url + "?" + utils.serialize(params) : url;
}
return fetch(url, option).then(response => {
if (response.status >= 200 && response.status < 300) {
return response;
} else {
let error = new Error(response.statusText);
error.response = response;
throw error;
}
});
}
// 分片计数器
class Counter {
count = 0;
......
......@@ -3,16 +3,36 @@ import TracksWorker from "./tracks.worker";
export default class Monitor {
rrwebHandler = null;
tracksWorker = new TracksWorker();
// 记录
constructor() {
this.tracksWorker.onmessage = ({ data }) => {
console.log("local", data);
window.localStorage.setItem("rrevents", JSON.stringify(data));
// common.fetch('https://manager.tuiatest.cn/homePage/data','get');
this.tracksWorker.onmessage = ({ data: { type, payload } }) => {
switch (type) {
case "localData":
window.localStorage.setItem("rrevents", JSON.stringify(payload));
break;
default:
console.log("monitor unknow action");
}
};
}
record() {
resetRrwebHandler() {
this.rrwebHandler && this.rrwebHandler();
}
/**
* 初始化
* @param {*} event
* @memberof Monitor
*/
init(event) {
this.resetRrwebHandler();
console.log("初始化系统信息");
this.tracksWorker.postMessage({ type: "init", payload: event });
}
/**
* 录制
* @memberof Monitor
*/
record() {
this.resetRrwebHandler();
console.log("开始录制");
this.rrwebHandler = rrweb.record({
emit: event => {
......@@ -20,7 +40,10 @@ export default class Monitor {
},
});
}
// 停止
/**
* 停止录制
* @memberof Monitor
*/
stop() {
if (!this.rrwebHandler) {
throw new Error("没有正在录制的实例");
......@@ -28,13 +51,20 @@ export default class Monitor {
console.log("停止录制");
this.rrwebHandler();
}
// 重置
/**
* 重置
* @param {*} event
*/
reset(event) {
this.rrwebHandler && this.rrwebHandler();
this.resetRrwebHandler();
console.log("重置数据");
this.tracksWorker.postMessage({ type: "reset", payload: event });
}
// 发送信息
/**
* 发送信息
* @param {*} action
* @memberof Monitor
*/
postMessage(action) {
this.tracksWorker.postMessage(action);
}
......
// 数据维护,上报
import { fetch } from "./common";
import utils from './utils';
import Counter from "./counter";
class Reporter {
isUploading = false;
bus = [];
counter = new Counter();
system = ''; //系统名
userIdentifier = ''; //用户标示
path = ''; // 当前路径
isUploading = false; // 是否在上传cdn
bus = []; // 上传分片数据
counter = new Counter(); // 计数器和recordKey一起重置
cache = [] // cdn数据缓存
constructor(recordKey) {
this.recordKey = recordKey;
}
toCDN() {
// 设置系统名
setSystem(system) {
this.system = system;
}
// 设置用户标示
setUserIdentifier(userIdentifier) {
this.userIdentifier = userIdentifier;
}
// 设置当前访问路径
setPath(url) {
this.path = url
}
// 上传CDN
toCDN(payload) {
this.isUploading = true;
let index = this.bus.length;
let blob = new Blob([JSON.stringify(payload)], {type : 'application/json'});
let formData = new FormData();
let trackId = this.counter.next();
let recordKey = this.recordKey;
return fetch("cdn").then(res => {
this.isUploading = false;
if (recordKey !== this.recordKey) {
return;
}
return res;
});
formData.append('file', blob, `${this.recordKey + trackId}.json`);
try {
fetch(`http://hunter.duibadev.com.cn/upload`, {
method: 'POST',
credentials: 'include',
body: formData
}).then(res => {
return res.json();
}).then(res => {
// 如果cache中有数据,说明是多次提交并且数据缓存在了cache中,那么我们全量快照可能在cache[0]
let index = this.bus.findIndex(v => v.track.type === 2 );
console.log(index);
let extra = {
system: this.system,
userIdentifier: this.userIdentifier,
path: this.path,
recordKey: this.recordKey,
trackId: trackId,
isCdn: true
}
this.bus.splice(index, 1, utils.dataWrapper(extra, res.url));
this.isUploading = false;
// 如果cache里面有数据需要上传的,那么先上传
if(this.cache && this.cache.length > 0) {
this.cache.map(item => {
this.report(item);
});
this.cache = [];
}
return res;
});
} catch (e) {
console.log('上传失败,原因:');
console.log(e.message);
}
}
toBus(data) {
let trackId = this.counter.next();
data.trackId = trackId;
this.bus.push(data);
let extra = {
system: this.system,
userIdentifier: this.userIdentifier,
path: this.path,
recordKey: this.recordKey,
trackId: this.counter.next(),
isCdn: false
}
this.bus.push(utils.dataWrapper(extra, data));
}
// 上传
report(data) {
console.log('上传数据', data);
let reportData = data;
this.bus = [];
if (this.isUploading) {
console.log('cdn数据正在上传,先将内容存到cache');
this.cache.push(reportData);
return;
}
return fetch("report");
try {
fetch(`http://hunter.duibadev.com.cn/behavior/record`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: JSON.stringify({
tracks: data
})
}).then(res => {
console.log(res);
});
} catch (e) {
console.log('上传失败,原因:');
console.log(e.message);
}
}
// 重置数据(分条使用)
reset(recordKey) {
this.cache = [];
this.bus = [];
this.counter.reset();
this.recordKey = recordKey;
......
// 入口,格式化数据,模块管理
import md5 from "md5";
import utils from "./utils";
import _ from "lodash";
import Reporter from "./reporter";
const reporter = new Reporter("recordKey");
// 原始数据数组
let events = [];
// 包装后的数据对象
let wrapData = [];
// 分条视频id
let recordKey = "";
// 全量快照是否已经返回
let isCdnReturn = false;
onmessage = ({ data: { type, payload } }) => {
switch (type) {
case "init":
reporter.setSystem(payload);
break;
case "record":
// todo : 数据本地存储
console.log(wrapData);
wrapData.push(
utils.dataWrapper(
{
recordKey,
isCdn: false,
},
payload,
),
);
events.push(payload);
reporter.toBus(payload);
console.log(reporter.bus);
// todo : 全量快照上传cdn
if (payload.type === 2) {
const cdnIndex = _.findIndex(events, { type: 2 });
setTimeout(() => {
wrapData.splice(
cdnIndex,
1,
utils.dataWrapper(
{
recordKey,
isCdn: true,
},
payload,
),
);
isCdnReturn = true;
}, 1000);
reporter.toCDN(payload);
}
// todo : 数据压缩
// todo : 根据事件类型优先级触发上传策略(click)
if (payload.data.source === 2 && isCdnReturn) {
console.log("点击上上传events", wrapData);
postMessage(wrapData);
events = [];
wrapData = [];
if (payload.data.source === 2 && !reporter.isUploading) {
postMessage({ type: "localData", payload: reporter.bus })
reporter.report(reporter.bus);
}
// todo : 数据超出100条上线,自动上传
if (wrapData.length > 100 && isCdnReturn) {
console.log("超出线上100条上传events", wrapData);
postMessage(wrapData);
events = [];
wrapData = [];
if (reporter.bus.length > 100 && !reporter.isUploading) {
postMessage({ type: "localData", payload: reporter.bus })
reporter.report(reporter.bus);
}
break;
case "reset":
// todo : 重置参数,重新生成recordKey
events = [];
wrapData = [];
recordKey = "";
isCdnReturn = false;
const { url, email } = payload;
recordKey = md5(url + email + Date.parse(new Date()));
console.log(url, "分片id", recordKey);
reporter.setUserIdentifier(email);
reporter.setPath(url);
let recordKey = md5(url + email + Date.parse(new Date()));
reporter.reset(recordKey);
console.log(url, "分片id", reporter.recordKey);
break;
default:
console.log("unknow action");
......
......@@ -3,25 +3,19 @@ const utils = {
dataWrapper(extraData, event) {
return {
...extraData,
event
track: event
}
},
// 判断是否有值
isNothing(value) {
return value === '' || value === undefined || value === null || (typeof value === 'number' && (isNaN(value) || !isFinite(value)));
},
// 获取token
getCookie(name) {
const regexp = new RegExp('(^| )' + name + '=([^;]*)(;|$)');
const matches = regexp.exec(document.cookie);
return matches ? matches[2] : null;
},
// 拼接URL请求参数
// 拼接URL请求参数
serialize(obj) {
const str = [];
for (let p in obj) {
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(this.isNothing(obj[p]) ? '' : obj[p]));
str.push(p + '=' + this.isNothing(obj[p]) ? '' : obj[p]);
}
}
return str.join('&');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment