This commit is contained in:
xy
2026-03-13 01:38:40 +00:00
parent e6e4f88257
commit 28855885cd
311 changed files with 89544 additions and 94350 deletions
+327 -396
View File
@@ -1,396 +1,327 @@
/**
* 模仿thinkingdata-node直接写一个log的写入,而日志直接用pinus-logger实现
*/
import { taLogger } from "../../util/logger";
const KEY_NAME_MATCH_REGEX = /^[a-zA-Z#][a-zA-Z0-9_]+$/;
let util: any = {};
util.version = '1.2.2';
util.log = function(message: string) {
console.log(message);
}
util.each = function (obj, iterator, context) {
if (obj === null) {
return;
}
if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (let i = 0, l = obj.length; i < l; i++) {
if (i in obj && iterator.call(context, obj[i], i, obj) === {}) {
return;
}
}
} else {
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
if (iterator.call(context, obj[key], key, obj) === {}) {
return;
}
}
}
}
};
util.formatDate = function (d) {
function pad(n) {
return n < 10 ? '0' + n : n;
}
function padMilliseconds(n) {
if (n < 10) {
return '00' + n;
} else if (n < 100) {
return '0' + n;
} else {
return n;
}
}
return d.getFullYear() + '-' +
pad(d.getMonth() + 1) + '-' +
pad(d.getDate()) + ' ' +
pad(d.getHours()) + ':' +
pad(d.getMinutes()) + ':' +
pad(d.getSeconds()) + '.' +
padMilliseconds(d.getMilliseconds());
};
util.searchObjDate = function (o) {
if (util.check.isObject(o) || util.check.isArray(o)) {
util.each(o, function (a, b) {
if (util.check.isObject(a) || util.check.isArray(a)) {
util.searchObjDate(o[b]);
} else {
if (util.check.isDate(a)) {
o[b] = util.formatDate(a);
}
}
});
}
};
util.check = {
isUndefined: function (obj) {
return obj === void 0;
},
isObject: function (obj) {
return (toString.call(obj) === '[object Object]') && (obj !== null);
},
isEmptyObject: function (obj) {
if (util.check.isObject(obj)) {
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
return false;
}
}
return true;
}
return false;
},
isArray: function (obj) {
return toString.call(obj) === '[object Array]';
},
isString: function (obj) {
return toString.call(obj) === '[object String]';
},
isDate: function (obj) {
return toString.call(obj) === '[object Date]';
},
isNumber: function (obj) {
return toString.call(obj) === '[object Number]';
},
isBoolean: function (obj) {
return toString.call(obj) === '[object Boolean]';
},
isJSONString: function (str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
};
util.properties = {
_strip: function (prop) {
if (!util.check.isObject(prop)) {
return prop;
}
util.each(prop, function (v, k) {
if (!(util.check.isString(v) || util.check.isNumber(v) || util.check.isDate(v) || util.check.isBoolean(v) || util.check.isArray(v))) {
util.log('您的数据-', k, v, '-格式不满足要求,我们已经将其删除. 属性值只支持 String, Number, Date, Boolean, Array');
delete prop[k];
}
});
return prop;
},
_checkPropertiesKey: function (obj) {
let flag = true;
util.each(obj, (v, k) => {
if (!KEY_NAME_MATCH_REGEX.test(k)) {
util.log('不合法的 KEY 值: ' + k);
flag = false;
}
});
return flag;
},
name: function (s) {
if (!util.check.isString(s) || !KEY_NAME_MATCH_REGEX.test(s)) {
util.log('请检查参数格式, 事件或属性名称必须是英文字母、 \'util\'或\'#\' 开头, 包含字母和数字的字符串: ' + s);
return false;
} else {
return true;
}
},
properties: function (p) {
this._strip(p);
if (p) {
if (util.check.isObject(p)) {
if (this._checkPropertiesKey(p)) {
return true;
} else {
util.log('请检查参数格式, properties 的 key 只能以字母或\'#\'开头,包含数字、字母和下划线 util');
return false;
}
} else {
util.log('properties 可以没有,但有的话必须是对象');
return false;
}
} else {
return true;
}
},
propertiesMust: function (p) {
this._strip(p);
if (p === undefined || !util.check.isObject(p) || util.check.isEmptyObject(p)) {
util.log('properties 必须是对象且有值');
return false;
} else {
if (this._checkPropertiesKey(p)) {
return true;
} else {
util.log('请检查参数格式, properties 的 key 只能以字母或\'#\'开头,包含数字、字母和下划线 util');
return false;
}
}
},
userId: function (id) {
if (util.check.isString(id) && /^.+$/.test(id)) {
return true;
} else {
util.log('用户 id 必须是不能为空');
return false;
}
}
}
const DEFAULT_PROPERTIES = {
"#lib": "node",
"#lib_version": util.version,
};
/**
* 内部函数,创建 ta 实例
*/
let _createClient = function (consumer) {
let ta: any = {};
ta.consumer = consumer;
ta.superProperties = {};
ta._basicCheck = function (params) {
if (!util.check.isObject(params)) {
return new Error("params for track must be an object");
}
if (!util.properties.userId(params.accountId) && !util.properties.userId(params.distinctId)) {
return new Error("account ID and distinct ID cannot be empty at the same time");
}
return undefined;
};
ta.track = function (params, skipLocalCheck = false) {
let callback = params.callback || function () {
};
if (!ta._trackCheck(params, skipLocalCheck, callback)) {
return;
}
ta._sendRequest("track", params, callback);
};
ta._trackCheck = function (params, skipLocalCheck, callback) {
let err = this._basicCheck(params);
if (err) {
callback(err);
return false;
}
if (!util.properties.name(params.event)) {
callback(new Error("invalid event name"));
return false;
}
if (!skipLocalCheck && !util.properties.properties(params.properties)) {
callback(new Error("invalid properties"));
return false;
}
return true;
};
ta.userSet = function (params) {
let callback = params.callback || function () {
};
let err = this._basicCheck(params);
if (err) {
callback(err);
return;
}
if (!util.properties.propertiesMust(params.properties)) {
callback(new Error("invalid properties"));
return;
}
ta._sendRequest("user_set", params, callback);
};
ta.userSetOnce = function (params) {
let callback = params.callback || function () {
};
let err = this._basicCheck(params);
if (err) {
callback(err);
return;
}
if (!util.properties.propertiesMust(params.properties)) {
callback(new Error("invalid properties"));
return;
}
ta._sendRequest("user_setOnce", params, callback);
};
ta.userAdd = function (params) {
let callback = params.callback || function () {
};
let err = this._basicCheck(params);
if (err) {
callback(err);
return;
}
if (!util.properties.propertiesMust(params.properties)) {
callback(new Error("invalid properties"));
return;
}
ta._sendRequest("user_add", params, callback);
};
ta.setDynamicSuperProperties = function (getDynamicProperties, callback) {
let err;
if (typeof getDynamicProperties === "function") {
if (util.properties.properties(getDynamicProperties())) {
ta.getDynamicProperties = getDynamicProperties;
} else {
err = new Error("Invalid return type of getDynamicProperties");
}
} else {
err = new Error("getDynamicProperties must be a funciton");
}
if (callback && err) {
callback(err);
}
};
// 内部函数,组织数据格式,并发送给对应的 consumer 去处理
ta._sendRequest = function (type, eventData, callback) {
let time = util.check.isUndefined(eventData.time) || !util.check.isDate(eventData.time)
? new Date()
: eventData.time;
let data = {
"#type": type,
"#time": util.formatDate(time),
};
if (eventData.distinctId) {
data["#distinct_id"] = eventData.distinctId;
}
if (eventData.accountId) {
data["#account_id"] = eventData.accountId;
}
if (eventData.ip) {
data["#ip"] = eventData.ip;
}
if (eventData.event) {
data["#event_name"] = eventData.event;
}
if (eventData.eventId) {
data["#event_id"] = eventData.eventId;
}
if (eventData.firstCheckId) {
data["#first_check_id"] = eventData.firstCheckId;
}
if (eventData.uuid) {
data["#uuid"] = eventData.uuid;
}
if (eventData.appId) {
data["#app_id"] = eventData.appId;
}
if (type === "track" ||
type === "track_update" ||
type === "track_overwrite") {
data["properties"] = { ...DEFAULT_PROPERTIES, ...ta.superProperties, ...(ta.getDynamicProperties ? ta.getDynamicProperties() : {}) };
} else {
data["properties"] = {};
}
if (
util.check.isObject(eventData.properties) &&
!util.check.isEmptyObject(eventData.properties)
) {
data['properties'] = { ...data['properties'], ...eventData.properties || {} }
}
util.searchObjDate(data);
this.consumer.add(data, callback);
};
return ta;
};
export function initTaLoggingMode() {
return _createClient({
add: (msg: string) => {
taLogger.info(JSON.stringify(msg));
}
})
}
/**
* 模仿thinkingdata-node直接写一个log的写入,而日志直接用pinus-logger实现
*/
import { taLogger } from "../../util/logger";
const KEY_NAME_MATCH_REGEX = /^[a-zA-Z#][a-zA-Z0-9_]+$/;
let util: any = {};
util.version = '1.2.2';
util.log = function(message: string) {
console.log(message);
}
util.each = function (obj, iterator, context) {
if (obj === null) {
return;
}
if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (let i = 0, l = obj.length; i < l; i++) {
// @ts-ignore
if (i in obj && iterator.call(context, obj[i], i, obj)) {
return;
}
}
} else {
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
// @ts-ignore
if (iterator.call(context, obj[key], key, obj)) {
return;
}
}
}
}
};
util.formatDate = function (d) {
function pad(n) {
return n < 10 ? '0' + n : n;
}
function padMilliseconds(n) {
if (n < 10) {
return '00' + n;
} else if (n < 100) {
return '0' + n;
} else {
return n;
}
}
return d.getFullYear() + '-' +
pad(d.getMonth() + 1) + '-' +
pad(d.getDate()) + ' ' +
pad(d.getHours()) + ':' +
pad(d.getMinutes()) + ':' +
pad(d.getSeconds()) + '.' +
padMilliseconds(d.getMilliseconds());
};
util.formatString = function (format) {
const args = Array.prototype.slice.call(arguments, 1);
return format.replace(/{(\d+)}/g, function (match, number) {
return typeof args[number] !== 'undefined'
? args[number]
: match
;
});
};
util.isArray = Array.isArray;
util.isObject = function (obj) {
return obj === Object(obj) && !util.isArray(obj) && typeof obj === 'object';
};
util.encodeURIComponent = function (str) {
return encodeURIComponent(str);
}
util.trim = function (str) {
return str.replace(/^\s+|\s+$/g, '');
}
util.toArray = function (iterable) {
if (!iterable) {
return [];
}
if (iterable.toArray) {
return iterable.toArray();
}
return [].slice.call(iterable);
}
util.each(['Function', 'String', 'Object', 'Number', 'Boolean', 'Date', 'RegExp', 'Error'], function (name) {
util['is' + name] = function (obj) {
return Object.prototype.toString.call(obj) === '[object ' + name + ']';
};
});
util.getProperty = function (obj, key) {
return obj == null ? undefined : obj[key];
};
util.setProperty = function (obj, key, value) {
obj[key] = value;
};
util.hasProperty = function (obj, key) {
return obj != null && typeof obj === 'object' && key in obj;
};
util.createLog = function (category) {
const log = function () {
};
log.debug = function () {
};
log.info = function () {
};
log.warn = function () {
};
log.error = function () {
};
return log;
};
const TABLE_NAME = "app_gold_record";
const EXTRA_CODE = {
"#": "_j_",
"ing": "_deng_",
"&": "_he_",
};
function encodeKey(key) {
let result = "";
for (let i = 0; i < key.length; i++) {
const c = key.charAt(i);
result += EXTRA_CODE[c] || c;
}
return result;
}
function decodeKey(key) {
let result = "";
let i = 0;
while (i < key.length) {
const c = key.charAt(i);
if (c === '_' && i + 2 < key.length) {
const nextTwo = key.substring(i + 1, i + 3);
let found = false;
for (const [encoded, decoded] of Object.entries(EXTRA_CODE)) {
if (nextTwo === encoded.substring(1)) {
result += decoded;
i += 3;
found = true;
break;
}
}
if (!found) {
result += c;
i++;
}
} else {
result += c;
i++;
}
}
return result;
}
function encode(data) {
const str = JSON.stringify(data);
return new Buffer(str).toString("base64");
}
function decode(str) {
return JSON.parse(new Buffer(str, "base64").toString());
}
function encodeId(id) {
return encodeKey(encode({
"appid": 1,
"id": id,
}));
}
function decodeId(id) {
const decoded = decode(decodeKey(id));
return decoded["id"];
}
function encodeData(data) {
return encode({
"data": data,
"time": Date.now(),
});
}
function decodeData(data) {
const decoded = decode(data);
return decoded["data"];
}
let instance = null;
export class TA {
private appId: string;
private worker: any;
private receiver: any;
constructor(appId: string, token: string) {
this.appId = appId;
this.worker = null;
this.receiver = null;
}
userSet(userId: string, properties: any) {
const data = {
"#type": "user_set",
"#time": util.formatDate(new Date()),
"#account_id": userId,
};
util.each(properties, function (val, key) {
data[key] = val;
});
this.upload(data);
}
userSetOnce(userId: string, properties: any) {
const data = {
"#type": "user_setOnce",
"#time": util.formatDate(new Date()),
"#account_id": userId,
};
util.each(properties, function (val, key) {
data[key] = val;
});
this.upload(data);
}
userAdd(userId: string, properties: any) {
const data = {
"#type": "user_add",
"#time": util.formatDate(new Date()),
"#account_id": userId,
};
util.each(properties, function (val, key) {
data[key] = val;
});
this.upload(data);
}
userUnset(userId: string, keys: string[]) {
const data: any = {
"#type": "user_unset",
"#time": util.formatDate(new Date()),
"#account_id": userId,
};
for (let i = 0; i < keys.length; i++) {
data[keys[i]] = 0;
}
this.upload(data);
}
userDelete(userId: string) {
const data = {
"#type": "user_del",
"#time": util.formatDate(new Date()),
"#account_id": userId,
};
this.upload(data);
}
track(userId: string, eventName: string, properties: any) {
const data = {
"#type": "track",
"#time": util.formatDate(new Date()),
"#account_id": userId,
"#event_name": eventName,
};
util.each(properties, function (val, key) {
if (!KEY_NAME_MATCH_REGEX.test(key)) {
return;
}
data[key] = val;
});
this.upload(data);
}
upload(data) {
if (!instance) {
taLogger.info("ta data:", JSON.stringify(data));
} else {
taLogger.info("ta data:", JSON.stringify(data));
}
}
}
let ta: TA = null;
let initCount = 0;
export function initTa(appId: string, token: string) {
initCount++;
if (ta == null || initCount <= 1) {
ta = new TA(appId, token);
}
return ta;
}
export function getTa() {
if (!ta) {
console.error("ta未初始化");
return null;
}
return ta;
}
export function reportTAEvent(uid: string, event: string, data: any) {
const ta = getTa();
if (ta) {
ta.track(uid, event, data);
}
}
export function reportTAUserSet(uid: string, data: any) {
const ta = getTa();
if (ta) {
ta.userSet(uid, data);
}
}
export function initTaLoggingMode(isInit: boolean) {
if (isInit) {
if (!instance) {
instance = new TA("AABBCC", "");
}
}
}