@xmini/x-mini
Version:
封装小程序
476 lines (394 loc) • 17.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _pluginBase = _interopRequireDefault(require("../../core/plugin-base"));
var _index = _interopRequireDefault(require("../../index"));
var _index2 = require("../../utils/index");
var _regionMap = require("./regionMap");
var _storage = require("../../core/storage");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var storagePiwik = new _storage.Storage('piwik', 31536000);
/**
* 公共参数 idsite、rec、r、url、urlref、h、m、s、send_image、cookie、gt_ms、_ref、pv_id、country、region、city、lat、long、cdt
* 访客属性 _id、uid、_idts、_idvc、_idn、_refts、_viewts、res、cvar、_cvar
* 事件参数:action_name
*
* _hmt.push(['_trackPageview', pageURL]);
* _hmt.push(['_trackEvent', category, action, opt_label, opt_value]);
* _hmt.push(['_setCustomVar', index, name, value, opt_scope]);
* _hmt.push(['_setAccount', siteId);
* _hmt.push(['_setAutoPageview', false]);
*/
/**
* piwik 数据统计(负责实现数据上报)
* 支持配置必备业务参数透传
* 参考 https://p-2q9b.tower.im/p/2fl2
* https://developer.matomo.org/guides/tracking-api-clients
* 基于piwik接口的统计方案 https://p-2q9b.tower.im/p/ccuo
* 百度统计 http://tongji.baidu.com/open/api/more?p=ref_setCustomVar
* 暴露三个方法
* - piwikInit 配置接口(受限)
* - piwikUpdate 更新数据接口(受限)
* - piwikLog 接收log,之后合并公共数据,然后 push 到数组中,并触发log上报
* - 内部方法 pushLog piwikReport send
* https://developer.matomo.org/api-reference/tracking-api
*
* @class Plugin
* @extends {PluginBase}
*/
var Plugin =
/*#__PURE__*/
function (_PluginBase) {
_inherits(Plugin, _PluginBase);
function Plugin(config) {
var _this;
_classCallCheck(this, Plugin);
_this = _possibleConstructorReturn(this, _getPrototypeOf(Plugin).call(this, {}));
_defineProperty(_assertThisInitialized(_this), "name", 'piwik');
_defineProperty(_assertThisInitialized(_this), "events", {});
_defineProperty(_assertThisInitialized(_this), "requestCount", 0);
_defineProperty(_assertThisInitialized(_this), "methods", {
piwikInit: 'piwikInit',
piwikUpdate: 'piwikUpdate',
piwikEvent: 'piwikEvent'
});
_defineProperty(_assertThisInitialized(_this), "_data", {});
_defineProperty(_assertThisInitialized(_this), "_logs", []);
_this.piwikInit(config);
_this._logs = (storagePiwik.get('piwikLog') || []).concat(_this._logs);
return _this;
}
_createClass(Plugin, [{
key: "install",
value: function install() {
_index2.emitter.on('stat_data', this.setData, this);
_index2.emitter.on('stat_log', this.statLog, this);
}
}, {
key: "setData",
value: function setData(config) {
if (!config) return;
Object.assign(this._data, config);
}
}, {
key: "getData",
value: function getData(key) {
return key ? this._data[key] : _objectSpread({}, this._data);
} // 不需要这个,因为直接 new 时,就可以配置好了
}, {
key: "piwikInit",
value: function piwikInit() {
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
if (!opts.reportURI || !opts.siteId || !opts.authToken) {
console.error("\u8BF7\u68C0\u67E5 plugin ".concat(this.name, " \u7684\u8BBE\u7F6E\uFF0C\u521D\u59CB\u5316\u5FC5\u987B\u914D\u7F6E reportURI, siteId, authToken"));
return;
} // 只允许设置以下值
var whiteList = {
size: 1,
siteId: 1,
category: 1,
reportURI: 1,
authToken: 1
}; // 这里做过滤,无效的删除,非白名单的删除
var temp = (0, _index2.filterObj)(opts, whiteList);
var config = {
size: temp.size || 5,
idsite: temp.siteId,
category: temp.category,
reportURI: temp.reportURI,
token_auth: temp.authToken,
rec: 1
};
this.setConfig((0, _index2.compactObject)(config));
}
}, {
key: "piwikUpdate",
value: function piwikUpdate() {
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
// 只允许更新以下值
if (opts.userId) opts.userId = opts.userId.toString();
var whiteList = {
openId: 1,
location: 1,
userId: 1,
screen: 1,
path: 1,
refer: 1,
spm: 1,
channel: 1,
preSpm: 1 // ...xmini.getChannelFilter(),
}; // ['screen', 'userId', 'openId', 'location', 'cityName', 'path', 'refer', 'channel', 'spm']
// console.warn(opts);
var config = (0, _index2.filterObj)(opts, whiteList); // console.warn('更新用户信息');
this.setData(config);
}
}, {
key: "piwikPageView",
value: function piwikPageView(pagePath, referer) {
// console.warn('pv');
// pv 统计页面 url 以及页面名称
// const { pageName, pagePath, referer = '' } = xmini.me.$getPageInfo();
var url = pagePath;
if (!/^http/.test(pagePath)) {
url = 'http://' + pagePath;
} // const { lastPage } = this.getData();
// pv 信息都应该从 pageInfo 上取
var data = {
url: url,
action_name: pagePath,
urlref: referer || 'istoppage',
_ref: referer
};
this.setData({
url: url,
urlref: referer || 'istoppage',
_ref: referer
});
this.pushLog(data);
} // cvar 暂时只有固定的数量,通过 stat_update/piwikUpdate 更新
// piwikCustomVar(index, name, value) { }
// 上报自定义事件
}, {
key: "piwikEvent",
value: function piwikEvent(action) {
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
var category = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
// console.warn('event');
// 上报自定义事件
if (!action) return;
if (value && typeof value !== 'string') {
value = JSON.stringify(value);
} // 系统生命周期的事件,别用 action 直接用,category=lifecycle
// 用户交互事件,使用 action 区别行为
// 曝光事件,使用 action 以 ex_ 开头, category=exposure
category = category || this.getConfig('category') || 'miniapp';
var temp = {
action_name: action,
// 页面路径
e_c: category,
e_a: action,
e_n: value
};
var immediately = false;
if (action === 'app') {
if (value === 'hide' || value === 'unlaunch') {
immediately = true;
}
}
this.pushLog(temp, immediately);
} // 暂时只支持两种log:event pv
}, {
key: "statLog",
value: function statLog(data) {
if (!(0, _index2.isObject)(data) || !data.type) {
console.error('statLog 上报数据格式必须为对象,且必须指定type 和 action');
return;
}
switch (data.type) {
case 'event':
this.piwikEvent(data.action, data.value, data.category);
break;
case 'pv':
this.piwikPageView(data.action, data.value, data.category);
break;
default:
// doNothing
break;
}
}
}, {
key: "piwikLog",
value: function piwikLog(data) {
// 通用结构
if (!(0, _index2.isObject)(data) && !data.action_name) {
console.error('piwikLog 上报数据格式必须为对象,且必须指定 action_name 和 action');
return;
}
this.pushLog(data);
}
}, {
key: "pushLog",
value: function pushLog(data, immediately) {
var log = (0, _index2.merge)(this.getCommon(), data); // console.warn(log);
this._logs.push("?".concat((0, _index2.stringify)(log)));
this.checkLog();
}
}, {
key: "checkLog",
value: function checkLog(immediately) {
if (this.reporting) return;
if (!this._logs.length) return;
this.piwikReport(immediately);
}
}, {
key: "piwikReport",
value: function piwikReport(immediately) {
var _this2 = this;
// "?urlref=istoppage&_ref=istoppage&action_name=index&url=http%3A%2F%2Fpages%2Findex%2Findex%3Fspm%3Daliapp%26channel_id%3Daliapp%26ide_internal_page%3Dpages%252Findex%252Findex%26port%3D60154&idsite=5&rec=1&_id=c3b80d787042a4c8&uid=&res=375x667&r=366229&h=17&m=25&s=17&send_image=0&cvar=%7B%221%22%3A%5B%22channel%22%2C%22aliapp%22%5D%2C%222%22%3A%5B%22city_name%22%2Cnull%5D%2C%223%22%3A%5B%22spm%22%2C%22aliapp%22%5D%2C%224%22%3A%5B%22user_id%22%2C%22%22%5D%7D&_cvar=%7B%221%22%3A%5B%22spm%22%2C%22aliapp%22%5D%2C%222%22%3A%5B%22openid%22%2Cnull%5D%2C%223%22%3A%5B%22city_name%22%2Cnull%5D%2C%224%22%3A%5B%22user_id%22%2C%22%22%5D%7D&cdt=1547803517"
if (this.reporting && !immediately) return;
var logs = this._logs.splice(0);
var retryTimes = 0;
var _this$getConfig = this.getConfig(),
reportURI = _this$getConfig.reportURI,
token_auth = _this$getConfig.token_auth;
this.reporting = true;
var reportData = function reportData() {
// logs = logs.map(item => {
// return item
// .replace(/rq_c=(\d+)/g, (match, $1) => {
// const count = $1 > this.requestCount ? $1 : this.requestCount;
// return `rq_c=${count}`;
// })
// .replace(/retryTimes=(\d+)/g, (match, $1) => {
// const count = $1 > retryTimes ? $1 : retryTimes;
// return `retryTimes=${count}`;
// });
// });
// console.log('report 上报数据', JSON.stringify(logs));
var data = {
token_auth: token_auth,
requests: logs
}; // data['rq_c'] = this.requestCount;
_this2.requestCount++;
_index.default.me.request({
url: "".concat(reportURI),
method: 'POST',
// 默认就是这个
// header: {
// 'content-type': 'application/json',
// },
// headers: {
// 'content-type': 'application/json',
// },
data: JSON.stringify(data),
dataType: 'json',
success: function success(res) {
// 成功就销毁数据,失败就多尝试两次,还失败就暂存
// 成功后检查是否有数据
_this2.reporting = false;
_this2.checkLog();
},
fail: function fail(err) {
if (retryTimes < 2) {
retryTimes++; // data['retryTimes'] = retryTimes;
setTimeout(function () {
reportData();
}, 300);
} else {
_this2.reporting = false; // 把数据存起来,留待后面再上报使用
_this2.save(logs);
}
}
});
};
reportData();
}
}, {
key: "save",
value: function save(logs) {
// 未上报成功的数据存储下来
storagePiwik.set('piwikLog', logs);
}
}, {
key: "random",
value: function random(size) {
var temp = '';
for (var i = 0; i < size; i++) {
temp += Math.floor(Math.random() * 10);
}
return temp;
}
}, {
key: "getCommon",
value: function getCommon() {
var date = new Date();
var config = this.getConfig();
var data = this.getData();
var devId = (0, _index2.hexMD5)(data.uuid + config.idsite).substr(8, 16);
var screenWidth = data.screenWidth,
screenHeight = data.screenHeight; // const channelParams = xmini.getChannel();
var _ref = data.showOptions || {},
_ref$scene = _ref.scene,
scene = _ref$scene === void 0 ? '' : _ref$scene; // 支付宝版本低时,取不到省市相关信息
var regionId = this.getRegionId(data.province);
var locate = {};
var _data$location = data.location,
location = _data$location === void 0 ? {} : _data$location;
Object.assign(locate, {
country: 'cn',
lat: location.latitude || 0,
long: location.longitude || 0
});
if (regionId) {
Object.assign(locate, {
region: regionId,
city: location.province
});
}
var temp = _objectSpread({
idsite: config.idsite,
rec: 1,
_id: devId,
// 支付宝小程序使用 user_id + idsite 前端生成ID,微信小程序使用 openid + idsite 前端生成ID
uid: '',
// 这里不加
res: screenWidth ? "".concat(screenWidth, "x").concat(screenHeight) : '',
r: this.random(6),
h: date.getHours(),
m: date.getMinutes(),
s: date.getSeconds(),
send_image: 0,
url: data.url,
urlref: data.urlref,
_ref: data._ref,
action_name: '',
// 事件名称,用户上报PV、UV时的页面路径
// 额外参数 上报次数,以及错误重试次数
// rq_c: 0,
// retryTimes: 0,
// 当前页面访问时的数据
cvar: JSON.stringify({
1: ['channel', data.channel],
2: ['pre_spm', data.preSpm],
3: ['spm', data.spm],
4: ['user_id', data.userId || '']
}),
// 记录访问最后一个页面的数据(每次上报,只是 piwik 数据库中覆盖式记录只存在一条记录)
// https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/scene.html
_cvar: JSON.stringify({
1: ['spm', data.spm],
2: ['openid', data.openId || null],
3: ['pre_spm', data.preSpm || null],
4: ['user_id', data.userId || ''],
5: ['scene', scene || ''] // 场景值
}),
cdt: parseInt(date / 1000)
}, locate);
return temp;
}
}, {
key: "getRegionId",
value: function getRegionId(province) {
if (!province) return '';
return _regionMap.regionMap[province] || '';
}
}]);
return Plugin;
}(_pluginBase.default);
var _default = Plugin;
exports.default = _default;
module.exports = exports.default;