淘宝网络请求拦截器

拦截并修改 queryBillHomeAgg 接口响应数据

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         淘宝网络请求拦截器
// @namespace    http://tampermonkey.net/
// @version      1.0.9
// @description  拦截并修改 queryBillHomeAgg 接口响应数据
// @author       You
// @match        https://tgc.tmall.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

;(function () {
  "use strict"

  // ==================== 配置区域 ====================
  // 在这里配置需要拦截的 URL 和修改规则
  const interceptRules = [
    {
      // 匹配的 URL(支持正则表达式)
      urlPattern: /fbi\/bill\/queryBillHomeAgg/,
      // 修改响应数据的函数
      modifyResponse: (originalData) => {
        if (originalData.data) {
          let incomeMultiplier = 1.9 // 收入倍数,根据实际需求修改
          let costAmountMultiplier = 0.3 // 成本倍数,根据实际需求修改
          let incomeAmount = originalData.data.incomeAmount || 0
          originalData.data.incomeAmount = Math.round(incomeAmount * incomeMultiplier * 100) / 100
          originalData.data.costAmount = Math.round(originalData.data.incomeAmount * costAmountMultiplier * 100) / 100
          originalData.data.settleAmount = originalData.data.incomeAmount - originalData.data.costAmount
          return originalData
        }
      },
    },
  ]
  // ==================== 配置区域结束 ====================

  const originalXHROpen = XMLHttpRequest.prototype.open
  const originalXHRSend = XMLHttpRequest.prototype.send
  const originalFetch = window.fetch

  // ==================== 拦截 fetch 请求 ====================
  window.fetch = async function (url, options = {}) {
    const urlStr = typeof url === "string" ? url : url.url || url.href

    // 检查是否需要拦截这个请求
    const matchedRule = interceptRules.find((rule) => {
      if (rule.urlPattern instanceof RegExp) {
        return rule.urlPattern.test(urlStr)
      }
      return urlStr.includes(rule.urlPattern)
    })

    if (matchedRule) {
      try {
        const response = await originalFetch.call(this, url, options)
        const clonedResponse = response.clone()
        const originalData = await clonedResponse.json()

        const modifiedData = matchedRule.modifyResponse(originalData)

        // 返回修改后的响应
        return new Response(JSON.stringify(modifiedData), {
          status: response.status,
          statusText: response.statusText,
          headers: response.headers,
        })
      } catch (error) {
        return originalFetch.call(this, url, options)
      }
    }

    return originalFetch.call(this, url, options)
  }

  // ==================== 拦截 XMLHttpRequest 请求 ====================
  XMLHttpRequest.prototype.open = function (method, url, ...rest) {
    this._url = url
    this._method = method
    return originalXHROpen.apply(this, [method, url, ...rest])
  }

  XMLHttpRequest.prototype.send = function (...args) {
    // 检查是否需要拦截这个请求
    const matchedRule = interceptRules.find((rule) => {
      if (rule.urlPattern instanceof RegExp) {
        return rule.urlPattern.test(this._url)
      }
      return this._url.includes(rule.urlPattern)
    })

    if (matchedRule) {
      // 保存原始的 onreadystatechange 和 onload
      const originalOnReadyStateChange = this.onreadystatechange
      const originalOnLoad = this.onload

      // 重写 onreadystatechange
      this.onreadystatechange = function () {
        if (this.readyState === 4 && this.status === 200) {
          try {
            // 获取原始响应
            const originalResponse = this.responseText
            let originalData = JSON.parse(originalResponse)

            // 应用修改规则
            const modifiedData = matchedRule.modifyResponse(originalData)

            // 重写响应属性
            Object.defineProperty(this, "responseText", {
              writable: true,
              value: JSON.stringify(modifiedData),
            })
            Object.defineProperty(this, "response", {
              writable: true,
              value: JSON.stringify(modifiedData),
            })
          } catch (error) {
            console.error("[XHR 拦截] 处理响应时出错:", error)
          }
        }

        if (originalOnReadyStateChange) {
          originalOnReadyStateChange.apply(this, arguments)
        }
      }

      // 重写 onload
      this.onload = function () {
        if (this.status === 200) {
          try {
            const originalResponse = this.responseText
            let originalData = JSON.parse(originalResponse)

            const modifiedData = matchedRule.modifyResponse(originalData)

            Object.defineProperty(this, "responseText", {
              writable: true,
              value: JSON.stringify(modifiedData),
            })
            Object.defineProperty(this, "response", {
              writable: true,
              value: JSON.stringify(modifiedData),
            })
          } catch (error) {
            console.error("[XHR 拦截 onload] 处理响应时出错:", error)
          }
        }

        if (originalOnLoad) {
          originalOnLoad.apply(this, arguments)
        }
      }
    }

    return originalXHRSend.apply(this, args)
  }

  // ==================== 尝试拦截 axios ====================
  setTimeout(() => {
    if (window.axios && window.axios.interceptors) {
      window.axios.interceptors.response.use(
        (response) => {
          const url = response.config.url

          const matchedRule = interceptRules.find((rule) => {
            if (rule.urlPattern instanceof RegExp) {
              return rule.urlPattern.test(url)
            }
            return url.includes(rule.urlPattern)
          })

          if (matchedRule && response.data) {
            response.data = matchedRule.modifyResponse(response.data)
          }

          return response
        },
        (error) => Promise.reject(error),
      )
    } else {
    }
  }, 100)
  console.log(1111)
  interceptRules.forEach((rule, index) => {
    console.log(`  ${index + 1}. ${rule.urlPattern}`)
  })

  // ==================== 修改页面文案 ====================
  function modifyTextContent() {
    const elements = document.querySelectorAll(".bill-agg-bar-item-title")
    elements.forEach((element) => {
      if (element.textContent.trim() === "支出金额 (元)") {
        element.textContent = "退款金额 (元)"
        console.log('[文案修改] 已将"支出金额"修改为"退款金额"')
      }
    })
  }

  // 监听 DOM 变化
  const observer = new MutationObserver((mutations) => {
    modifyTextContent()
  })

  // 在 DOM 加载完成后开始监听
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", () => {
      modifyTextContent()
      observer.observe(document.body, {
        childList: true,
        subtree: true,
      })
    })
  } else {
    modifyTextContent()
    observer.observe(document.body, {
      childList: true,
      subtree: true,
    })
  }
})()