Version Comparison Script

Compare versions and calculate percent sum

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

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