Version Comparison Script

Compare versions and calculate percent sum

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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);
    }
})();