Compare versions and calculate percent sum
// ==UserScript==
// @name Version Comparison Script
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Compare versions and calculate percent sum
// @author Your Name
// @match raptor.mws.sankuai.com/client/metrics/*
// @grant none
// @run-at document-body
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// 创建用于显示toast的div元素
const toastDiv = document.createElement('div');
toastDiv.style.position = 'fixed';
toastDiv.style.top = '10px';
toastDiv.style.right = '10px';
toastDiv.style.padding = '10px 20px';
toastDiv.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
toastDiv.style.color = '#fff';
toastDiv.style.borderRadius = '4px';
toastDiv.style.zIndex = '1001';
toastDiv.style.display = 'none'; // 初始隐藏
// 创建下拉框元素(原来是输入框,现在改为下拉框)
const versionInput = document.createElement('select');
const jsonInput = document.createElement('textarea');
const button = document.createElement('button');
// 保存原始的open和send方法
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
// 重写open方法
XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
this._customUrl = url; // 记录当前请求的URL,方便后续在send方法中使用
return originalOpen.apply(this, arguments);
};
// 重写send方法
XMLHttpRequest.prototype.send = function (data) {
const xhr = this;
xhr.addEventListener('load', function () {
const url = xhr._customUrl; // 获取之前记录的URL
if (url && url.indexOf("/common/decorator/distribution")!== -1) {
console.log("Response status:", xhr.status);
console.log("Response text:", xhr.responseText);
// 这里也可以根据需求进一步处理响应结果,比如将结果存储到某个变量等
// 例如可以定义一个全局变量来保存这个特定请求的结果
const res = JSON.parse(xhr.responseText);
const versionMap = res.data;
jsonInput.value = JSON.stringify(res.data);
// 清空原有下拉选项(防止重复添加)
while (versionInput.options.length > 0) {
versionInput.remove(0);
}
// 遍历版本数据,添加版本号到下拉框选项中
for (const version in versionMap) {
const option = document.createElement('option');
option.value = version;
option.text = version;
versionInput.add(option);
}
}
});
return originalSend.apply(this, arguments);
};
// 创建容器div
const container = document.createElement('div');
container.style.position = 'fixed';
container.style.top = '50%';
container.style.right = '10px';
container.style.transform = 'translateY(-50%)';
container.style.backgroundColor = '#67de7b';
container.style.padding = '20px';
container.style.borderRadius = '8px';
container.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)';
container.style.zIndex = '1000';
container.style.fontFamily = 'Arial, sans-serif';
container.style.width = '320px';
versionInput.placeholder = "选择版本号";
jsonInput.placeholder = "输入所有版本数据及其百分比";
button.textContent = "计算结果";
// 样式设置
versionInput.style.width = "100%";
versionInput.style.marginBottom = "10px";
versionInput.style.padding = "10px";
versionInput.style.border = "1px solid #d9d9d9";
versionInput.style.borderRadius = "4px";
versionInput.style.fontSize = "14px";
jsonInput.style.width = "100%";
jsonInput.style.height = "100px";
jsonInput.style.marginBottom = "10px";
jsonInput.style.padding = "10px";
jsonInput.style.border = "1px solid #d9d9d9";
jsonInput.style.borderRadius = "4px";
jsonInput.style.fontSize = "14px";
jsonInput.style.resize = "none";
button.style.width = "100%";
button.style.padding = "10px";
button.style.backgroundColor = "#1890ff";
button.style.color = "#fff";
button.style.border = "none";
button.style.borderRadius = "4px";
button.style.fontSize = "16px";
button.style.cursor = "pointer";
button.style.transition = "background-color 0.3s";
button.addEventListener('mouseover', () => {
button.style.backgroundColor = "#40a9ff";
});
button.addEventListener('mouseout', () => {
button.style.backgroundColor = "#1890ff";
});
// 将输入框等元素添加到容器
container.appendChild(versionInput);
container.appendChild(jsonInput);
container.appendChild(button);
// 将容器添加到页面body
document.body.appendChild(container);
// 将toast div添加到页面body
document.body.appendChild(toastDiv);
// 版本比较函数
function compareVersions(v1, v2) {
const v1parts = v1.split('.').map(Number);
const v2parts = v2.split('.').map(Number);
for (let i = 0; i < Math.max(v1parts.length, v2parts.length); i++) {
const num1 = v1parts[i] || 0;
const num2 = v2parts[i] || 0;
if (num1 > num2) return 1;
if (num1 < num2) return -1;
}
return 0;
}
// 精确小数加法函数
function addDecimal(a, b) {
const aParts = `${a}`.split('.');
const bParts = `${b}`.split('.');
const aInt = aParts[0] || '0';
const bInt = bParts[0] || '0';
const aDec = aParts[1] || '';
const bDec = bParts[1] || '';
const maxDecLength = Math.max(aDec.length, bDec.length);
const aDecPadded = aDec.padEnd(maxDecLength, '0');
const bDecPadded = bDec.padEnd(maxDecLength, '0');
const aBigInt = BigInt(aInt + aDecPadded);
const bBigInt = BigInt(bInt + bDecPadded);
const sumBigInt = aBigInt + bBigInt;
const sumStr = sumBigInt.toString();
const integerPart = sumStr.slice(0, -maxDecLength) || '0';
const decimalPart = sumStr.slice(-maxDecLength).padStart(maxDecLength, '0');
return `${integerPart}.${decimalPart}`;
}
// 按钮点击事件处理函数
button.addEventListener('click', () => {
const jsonData = JSON.parse(jsonInput.value || '{}');
const version = versionInput.value || "12.21.400";
const result = getVersionGTE(jsonData, version);
if (result!== undefined) {
showToast(`大于 ${version} 的版本的 percent 总和是: ${result}`);
}
});
// 用于比较版本并计算百分比总和的函数
function getVersionGTE(obj = {}, v = "12.21.400") {
if (!obj || Object.keys(obj)?.length === 0) {
console.log(`数据源为空,请前往 https://perf.sankuai.com/#/metrics/overview/2/Android?env=prod 进行观测`);
console.log(`对应接口为: /common/decorator/distribution`);
return;
}
// 筛选出大于等于指定版本的版本数据
const targetVersions = Object.values(obj)
.filter(version => compareVersions(version.tagValue, v) >= 0);
const result = targetVersions.reduce((sum, version) => {
sum = addDecimal(sum, version.percent);
return sum;
}, '0');
return result;
}
// 显示toast的函数
function showToast(message) {
toastDiv.textContent = message;
toastDiv.style.display = 'block';
// 设定一定时间后隐藏toast,这里设置为3秒后隐藏
setTimeout(() => {
toastDiv.style.display = 'none';
}, 5000);
}
})();