bboss-insights-collector

a plugin focused on the behavior research of Boss job seekers

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         bboss-insights-collector
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  a plugin focused on the behavior research of Boss job seekers
// @author       abin
// @match        https://www.zhipin.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @grant        GM_addStyle
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function() {
    'use strict';
const BISVERSION = "1.0.0"
const MAX_RECORDS = 1000; // 最大记录数量
const DELETE_COUNT = 50; // 超过最大记录数时删除的数据条数
const BEATNUM = 60000;
let proInerNet = "https://172.27.2.58:9097";
let proOutNet = "https://111.202.197.150:9099";
let timer;
let IS_BOSSPAGE = false;
let isTargetPage = false;
let data = { stuId: "", isDisconnectReconnect: 'N', pluginsContentList: [] };
let itemContent = { act_id: "", act_tool: "chrome" }
let bossName, jobName, isNodeList;

console.log(GM_getValue('isLoggedIn'),'isLoggedInisLoggedInisLoggedIn')
GM_setValue('proIP',proOutNet);
GM_setValue('proInerNet', proInerNet);
GM_setValue('proOutNet', proOutNet);
GM_setValue('operateStatus',false );

const formatTime = (
  time,
  fmt
) => {
  if (!time) { return ''; }
  const date = new Date(time);
  const o = {
    'M+': date.getMonth() + 1,
    'd+': date.getDate(),
    'H+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds(),
    'q+': Math.floor((date.getMonth() + 3) / 3),
    S: date.getMilliseconds(),
  };

  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(
      RegExp.$1,
      (date.getFullYear() + '').substr(4 - RegExp.$1.length)
    );
  }
  for (const k in o) {
    if (new RegExp('(' + k + ')').test(fmt)) {
      fmt = fmt.replace(
        RegExp.$1,
        // @ts-ignore: Unreachable code error
        RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
      );
    }
  }
  return fmt;
};


  const db = indexedDB.open('bossDatabase', 1); // 名称 + 版本
	db.onerror = (event) => {
		console.error('数据库打开失败:', event.target.error);
	};
	db.onsuccess = (event) => {
		const db = event.target.result;
		console.log('数据库已打开:', db.name);
	};
	db.onupgradeneeded = (event) => {
		const db = event.target.result;
		if (!db.objectStoreNames.contains('')) {
			const store = db.createObjectStore('action', { keyPath: 'id_', autoIncrement: true });
		}
	};

      // 检查用户是否已经登录
    function checkLoginStatus() {
         const isLoggedIn = GM_getValue('isLoggedIn');
        if (isLoggedIn) {
              const username = GM_getValue('username');
              const usernum = GM_getValue('usernum');
            // 显示学号和用户名
            document.querySelector(".boss-script-login")?.remove();
            displayUserInfo(username, usernum);
        } else {
             document.querySelector(".boss-script-info")?.remove();
            // 如果没有登录信息,显示登录表单
            console.log('1111111')
            displayLoginForm();
        }
    }

    // 显示用户信息(学号和用户名)
    function displayUserInfo(username, usernum) {
      const Html = `
      	<div id="floating-window" class="floating-window boss-script-info">
		<div class="floating-window-content">
			<button id="minimize-btn">-</button>
                <div style="text-align: right; margin-right: 20px;">
                    <button id="logout-btn">注销</button>
                </div>
                   <h3 style="margin-top:20px; font-size: 16px;">八维教育 ${BISVERSION}</h3>
                  <form id="loginForm">
                    <div class="layui-form-item">
                        <label class="layui-form-label">学号</label>
                        <div class="layui-input-block">
                        ${usernum}
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <label class="layui-form-label">姓名</label>
                        <div class="layui-input-block">
                        ${username}
                        </div>
                    </div>
                </form>
		</div>
	</div>
	<button id="show-btn" class="show-btn">BOSS</button>
        `;
        document.body.insertAdjacentHTML('beforeend', Html);
        const logoutBtn = document.getElementById('logout-btn');
        const floatingWindow = document.getElementById('floating-window');
		const minimizeBtn = document.getElementById('minimize-btn');
		const showBtn = document.getElementById('show-btn');
        console.log(minimizeBtn,'minimizeBtn')
		// 点击“-”按钮,浮窗逐渐变小,直到按钮大小并消失
		minimizeBtn.addEventListener('click', () => {
			// 设置缩小效果
			floatingWindow.style.width = '50px';
			floatingWindow.style.height = '50px';
			floatingWindow.style.opacity = '0';
			floatingWindow.style.right = '0'; // 确保它停在按钮的位置

			// 隐藏浮窗并显示按钮
			setTimeout(() => {
				floatingWindow.style.display = 'none';
				showBtn.style.display = 'block';
			}, 500); // 延迟,以便看到缩小的过程
		});

		// 点击右侧按钮,浮窗恢复原始状态并逐渐变大
		showBtn.addEventListener('click', () => {
			// 先设置为小尺寸和透明,确保没有显示
			floatingWindow.style.display = 'block';
			floatingWindow.style.width = '50px';
			floatingWindow.style.height = '50px';
			floatingWindow.style.opacity = '0';
			floatingWindow.style.right = '0';

			// 显示按钮隐藏
			showBtn.style.display = 'none';

			// 使用 setTimeout 延迟启动动画
			setTimeout(() => {
				// 恢复浮窗尺寸和透明度
				floatingWindow.style.transition = 'all 0.5s ease-out'; // 应用过渡效果
				floatingWindow.style.width = '220px';
                   floatingWindow.style.height = '190px';
				floatingWindow.style.opacity = '1'; // 恢复可见
				floatingWindow.style.right = '0'; // 确保浮窗停在右侧
			}, 10); // 延迟0.01s启动动画
		});




        if (!logoutBtn) return;
        logoutBtn.addEventListener('click', async function (e) {
            let proInerNet;
            // 读取本地存储的用户信息
            const token = GM_getValue('token');
            const userid = GM_getValue('userid');
            const proIP = GM_getValue('proIP');
            proInerNet = GM_getValue('proInerNet');
            try {
                const response = await fetch(`${proOutNet}/logout`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${token}` // 添加 Authorization 头
                    },
                });
                // 处理响应
                const data = await response.json();
                if (data.code == 200) {
                    let dta = {};
                    dta.act_type = "logout";
                    dta.act_id = userid;
                    dta.act_tool = "chrome";
                    dta.act_time = formatTime(new Date(), 'yyyy-MM-dd HH:mm:ss.S');
                    sendData(dta,'logout')
                    GM_deleteValue('username');
                    GM_deleteValue('token');
                    GM_deleteValue('usernum');
                    GM_setValue('isLoggedIn', false);
                // 跳转回登录页面
                 checkLoginStatus();
                } else {
                    throw new Error(data.msg || '退出登录失败');
                }
            } catch (error) {
                console.error(error, 'error');
                GM_setValue('proIP', proInerNet); // 恢复 proIP 值
                alert(error || '退出登录过程中发生错误');
            } finally {
                // 重置按钮状态
            }
        });
    }


 GM_addStyle(`
             #loginForm{
                 padding:15px 10px 0;
             }

            .layui-form-item {
                margin-bottom: 20px;
                display:flex;
                align-items:center
            }
            .layui-form-label{
               margin-right:10px;
               font-size:14px;
               width:43px;

            }

            .layui-input {
                border-radius: 5px;
                border: 1px solid #ddd;
                height: 25px;
                font-size: 14px;
                width:120px;
            }

            .layui-btn {
                width: 100%;
                height: 30px;
                background-color: #007bff;
                border-radius: 5px;
                color: white;
                font-size: 14px;
                border:none;
            }


            .layui-btn:hover {
                background-color: #409eff;
               cursor: pointer;
            }
            .form-btn{
            display:flex;
            justify-content:center;
                margin-bottom:10px;

            }
            ` )
// 动画效果:使用CSS过渡来平滑显示或隐藏浮窗
const style = document.createElement('style');
style.textContent = `
	   .floating-window {
            z-index:2000;
			position: fixed;
			right: 0;
			top: 148px;
			transform: translateY(-50%);
			width: 220px;
            height:190px;
			background-color: #fff;
			box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
			display: block;
			transition: all 0.5s ease-out; /* 应用过渡 */
			opacity: 1;
		}
        .floating-window h3 {
           margint-top:10px;
           text-align:center;
        }
        #logout-btn{
           color:#007bff;
           border:none;
           background:none;
           margin-right:10px;
        }
         #logout-btn:hover {
              color: #409eff;
              cursor: pointer;
         }
		.floating-window-content {
			padding: 10px;
		}
		#minimize-btn {
			font-size: 20px;
			border: none;
			color: #888;
			cursor: pointer;
			transition: transform 0.3s ease-in-out;
            width: 15px;
            height: 15px;
            border-radius: 50%;
            display:flex;
            justify-content:center;
            line-height: 13px;
            position: absolute;
            right: 15px;
            top: 15px;
		}
		#minimize-btn:hover {
			transform: scale(1.2); /* 按钮悬停时放大 */
		}
		.show-btn {
			position: fixed;
			right: 0;
			top: 200px;
			width: 50px;
			height: 50px;
			background-color: #007bff;
			color: white;
			font-size: 14px;
			border: none;
			border-radius: 50%;
			display: none;
			cursor: pointer;
            font-weight:600;
		}
`;

    // 显示登录表单
    function displayLoginForm() {
        const loginForm = `
        	<div id="floating-window" class="floating-window boss-script-login">
		<div class="floating-window-content">
			<button id="minimize-btn">-</button>
                <h3 style="margin: 0; font-size: 16px;">八维教育 ${BISVERSION}</h3>
                  <form id="loginForm">
                    <div class="layui-form-item">
                        <label class="layui-form-label">用户名</label>
                        <div class="layui-input-block">
                            <input type="text" autocomplete="username" id="loginName" name="username" required lay-verify="required" placeholder="请输入用户名" class="layui-input" />
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <label class="layui-form-label">密码</label>
                        <div class="layui-input-block">
                            <input type="password" autocomplete="current-password" name="password" id="loginPassword" required lay-verify="required" placeholder="请输入密码" class="layui-input" />
                        </div>
                    </div>
                    <div class="layui-form-item form-btn">
                          <button type="button" id="layui-btn" class="layui-btn">登录</button>
                    </div>
                </form>
		</div>
	</div>
	<button id="show-btn" class="show-btn">BOSS</button> `;
          document.body.insertAdjacentHTML('beforeend', loginForm);
        const floatingWindow = document.getElementById('floating-window');
		const minimizeBtn = document.getElementById('minimize-btn');
		const showBtn = document.getElementById('show-btn');
		// 点击“-”按钮,浮窗逐渐变小,直到按钮大小并消失
		minimizeBtn?.addEventListener('click', () => {
			// 设置缩小效果
			floatingWindow.style.width = '50px';
			floatingWindow.style.height = '50px';
			floatingWindow.style.opacity = '0';
			floatingWindow.style.right = '0'; // 确保它停在按钮的位置

			// 隐藏浮窗并显示按钮
			setTimeout(() => {
				floatingWindow.style.display = 'none';
				showBtn.style.display = 'block';
			}, 500); // 延迟,以便看到缩小的过程
		});

		// 点击右侧按钮,浮窗恢复原始状态并逐渐变大
		showBtn?.addEventListener('click', () => {
			// 先设置为小尺寸和透明,确保没有显示
			floatingWindow.style.display = 'block';
			floatingWindow.style.width = '50px';
			floatingWindow.style.height = '50px';
			floatingWindow.style.opacity = '0';
			floatingWindow.style.right = '0';
			// 显示按钮隐藏
			showBtn.style.display = 'none';
			// 使用 setTimeout 延迟启动动画
			setTimeout(() => {
				// 恢复浮窗尺寸和透明度
				floatingWindow.style.transition = 'all 0.5s ease-out'; // 应用过渡效果
				floatingWindow.style.width = '220px';
                floatingWindow.style.height = '190px';
				floatingWindow.style.opacity = '1'; // 恢复可见
				floatingWindow.style.right = '0'; // 确保浮窗停在右侧
			}, 10); // 延迟0.01s启动动画
		});

      document.getElementById('layui-btn').addEventListener('click', async (e)=> {
         e.preventDefault();
        // 获取表单数据
        const username = document.getElementById('loginName').value;
        const password = document.getElementById('loginPassword').value;
        // 简单的客户端验证
        if (!username || !password) {
            alert('请输入用户名和密码');
            return;
        }

        const loginBtn = document.querySelector('.login-btn button');
        let proInerNet;
        proInerNet = GM_getValue('proInerNet');
        const proIP = GM_getValue('proIP');

        try {
            // 发送登录请求
            const response = await fetch(`${proIP}/login`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    username,
                    password
                }),
            });
            if (!response) {
                // 登录失败
                alert('登录过程中发生错误');
                return;
            }
            const data = await response.json();
            if (data.code == 200) {
                let dta = {};
                dta.act_type = "login";
                dta.act_time = formatTime(new Date(), 'yyyy-MM-dd HH:mm:ss.S');
                dta.act_id = data.userid;
                dta.act_tool = "tampermonkey";

                // 存储登录数据
                GM_setValue('token', data.token);
                GM_setValue('usernum', data.usernum);
                GM_setValue('userid', data.userid);
                GM_setValue('username', data.username);
                GM_setValue('isLoggedIn', true);
                checkLoginStatus();
                // 登录成功
                  sendData(dta,'login')
            } else {
                // 登录失败
                alert(data.msg || '登录过程中发生错误');
            }
        } catch (error) {
            GM_setValue('proIP', proInerNet);
            alert(error || '登录过程中发生错误');
        } finally {
            // 重置按钮状态

        }
    });
   }



document.head.append(style);

    // 检查登录状态
    checkLoginStatus();

// 获取当前页面的完整 URL
const currentUrl = window.location.href;
// 判断当前页面的域名和路径
if (currentUrl.includes('https://www.zhipin.com/web/geek/recommend')) {
	isTargetPage = true;
	// 在这里执行你希望的操作
} else {
	isTargetPage = false;
}

window.addEventListener('beforeunload', function (event) {
	if (isTargetPage) updateData();
});
// 获取数据并更新存储
function updateData() {
	// 提取各项数据
	const communicationCount = document.querySelector('[ka="personal_top_added"] .count');
	const applyCount = document.querySelector('[ka="personal_top_submitted"] .count');
	const interviewCountDom = document.querySelector('[ka="personal_top_interview"] .count');
	// 获取 .resume-refresh-hwslider 下的 <svg> 元素
	const svg = document.querySelector('.resume-refresh-hwslider svg');
	// 获取最后一个 <g> 标签
	const lastGElement = svg && svg.querySelector('g:last-child');
	// 获取 .my-series 元素
	const exposureCountDom = lastGElement && lastGElement.querySelector('.my-series');
	let exposureCount;
	let interviewCount;
	let data = {};
	if (isTargetPage) {
		if (interviewCountDom) {
			interviewCount = parseInt(interviewCountDom.textContent);
		}
		if (exposureCountDom) {
			exposureCount = parseInt(exposureCountDom.textContent);
		}
        let userid = GM_getValue('userid') || '';
		data = [{ act_id: userid, act_tool: "chrome", act_type: 'active', act_time: formatTime(new Date(), 'yyyy-MM-dd HH:mm:ss.S'), act_value: exposureCount }, { act_id: userid, act_tool: "chrome", act_type: 'interview', act_time: formatTime(new Date(), 'yyyy-MM-dd HH:mm:ss.S'), act_value: interviewCount }]
        sendData(data,'active')
	}
}

// 监听“投递”按钮点击事件
function setupApplyButtonListener() {
	const applyButton = document.querySelector('.choose-resume-dialog .btn-confirm') || document.querySelector('.toolbar-btn-content .btn-v2.btn-sure-v2');
	if (applyButton) {
		applyButton.addEventListener('click', () => {
			// 检查按钮是否被禁用
			if (applyButton.disabled) {
                // 如果按钮被禁用,直接返回
				return;
			}
			let data = {};
            let userid = GM_getValue('userid') || '';
			const bossName = document.querySelectorAll('.base-info > *')[1]?.textContent;
			const jobName = document.querySelector('.position-name')?.textContent;
			data.act_type = "deliver";
			data.act_time = formatTime(new Date(), 'yyyy-MM-dd HH:mm:ss.S');
			data.act_value = "1";
			data.act_company_name = bossName;
			data.act_station_name = jobName;
			data.act_id = userid;
			data.act_tool = "chrome";
            sendData(data,'deliver')
		});
	}
}


// 监听“沟通”按钮点击事件
function setupButtonClickListener() {
	let dom = document.querySelector('.job-detail-header .op-btn.op-btn-chat') || document.querySelector('.btn-container .btn.btn-startchat') || document.querySelectorAll('button.btn.btn-startchat') ||document.querySelectorAll('a.btn.btn-startchat');
	// 移除已有的事件监听器,防止重复绑定
    if (dom instanceof NodeList) {
		isNodeList = true;
		dom.forEach(button => {
			button.addEventListener('click', handleButtonClick);
		});
	} else {
		isNodeList = false;
		jobName = document.querySelector('.job-detail-box .job-name')?.textContent || document.querySelector('.job-primary .info-primary .name h1')?.textContent;
		dom?.removeEventListener('click', handleButtonClick);
		// 绑定新的事件监听器
		dom?.addEventListener('click', handleButtonClick);
	}

}

// 在页面跳转前发送数据
function handleButtonClick(event) {
	if (isNodeList){
		jobName = event.target.closest('li').querySelector('.info-primary .name b').textContent || event.target.closest('li').querySelector('.info-primary .name .job-title').textContent;
	}
	bossName = document.querySelector('.job-card-wrap.active .boss-name')?.textContent || document.querySelector('.info-primary .info h1')?.childNodes[0].textContent.trim() || document.querySelector('.sider-company .company-info a')?.title;;
	let data = {};
      let userid = GM_getValue('userid') || '';
	data.act_type = "communication";
	data.act_time = formatTime(new Date(), 'yyyy-MM-dd HH:mm:ss.S');
	data.act_value = "1";
	data.act_company_name = bossName;
	data.act_station_name = jobName;
	data.act_id = userid;
	data.act_tool = "chrome";
    sendData(data,'communication')
}

    // 初始化
    function init() {
         startTimer();
    }
    // 等待页面加载完毕后初始化
    window.addEventListener('load', init);

// 设置 MutationObserver 来确保页面加载后添加监听器
let tempTimer = null;
function setupObserver() {
	const observer = new MutationObserver(() => {
		// 检查监听器是否已经设置,如果没有设置,就设置它们
		// 每次 MutationObserver 回调触发时,清除之前的定时器
		clearTimeout(tempTimer);
		// 设置新的定时器,延迟 200 毫秒后执行某些逻辑
		tempTimer = setTimeout(() => {
			// 如果有多个变动,只有最后一次触发时才执行这里的逻辑
			setupApplyButtonListener();
			setupButtonClickListener();
		}, 0); // 延迟时间可以根据实际需要调整
	});
	const targetNode = document.body; // 观察整个页面
	if (targetNode) {
		observer.observe(targetNode, {
			childList: true,
			subtree: true
		});
	}
}
// 启动 MutationObserver 以确保动态加载的按钮也能正确绑定事件
setupObserver();
    // 发送数据
    async function request(info, isReconnect, type,isold) {
        try {
            const token = GM_getValue('token');
            const userid = GM_getValue('userid');
            const proIP = GM_getValue('proIP');
            let data = {};
            if (userid) {
                data.stuId = userid;
                data.isDisconnectReconnect = isReconnect;
                if (info instanceof Array) {
                    info.forEach(item => {
                        item.act_id = userid;
                    });
                    data.pluginsContentList = info;
                } else {
                    data.pluginsContentList = [{ ...info }];
                }
                // 发送请求
                const response = await fetch(`${proIP}/boos/plugins/saveJsonContent`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${token}` // 添加 Authorization 头
                    },
                    body: JSON.stringify(data),
                });

                const res = await response.json();
                if (res.code === 200) {
                    GM_setValue('operateStatus', true);
                    if (type === 'login') {
                        startTimer();
                    }
                    if (type === 'logout') {
                        GM_setValue('token', null);
                        GM_setValue('usernum', '');
                        GM_setValue('userid', null);
                        GM_setValue('username', null);
                        GM_setValue('isLoggedIn', false);
                    }
                    if(isold){
                        // 打开数据库
                    const dbRequest = indexedDB.open('bossDatabase', 1);
                    dbRequest.onsuccess = (event) => {
                        const db = event.target.result;
                        const transaction = db.transaction('action', 'readwrite');
                        const store = transaction.objectStore('action');
                        const clearRequest = store.clear();
                        clearRequest.onsuccess = () => {
                        };
                        clearRequest.onerror = (event) => {
                        };
                      };
                    }

                }
            } else {
            }
        } catch (error) {
             GM_setValue('proIP', proInerNet);
        }
    }

     // 发送数据前处理
    async function sendData(data,type) {
        // 获取存储的 token 和 userid
        const token = GM_getValue('token');
        const userid = GM_getValue('userid');
        if (userid) {
            // 使用 request 替换为你自己的请求函数
            await request(data, 'N', type);
            if (type === 'login') {
                data.isDisconnectReconnect = 'Y';
                const dbRequest = indexedDB.open('bossDatabase', 1);
                dbRequest.onsuccess = async (event) => {
                    const db = event.target.result;
                    await getAllUsers(db);
                };
            }
        } else {
            const dbRequest = indexedDB.open('bossDatabase', 1);
            dbRequest.onsuccess = (event) => {
                const db = event.target.result;
                addAction(db, data);
            };
        }
        return true;
    }


    // 添加数据到 IndexedDB
    function addAction(db, data) {
        const transaction = db.transaction('action', 'readwrite');
        const store = transaction.objectStore('action');
        const countRequest = store.count();
        countRequest.onsuccess = () => {
            const currentCount = countRequest.result;
            if (currentCount >= MAX_RECORDS) {
                deleteOldRecords(store, DELETE_COUNT, () => {
                    insertData(store, data);
                });
            } else {
                insertData(store, data);
            }
        };
        countRequest.onerror = (event) => {
            console.error('获取记录数失败:', event.target.error);
        };
    }

    // 删除旧记录
    function deleteOldRecords(store, deleteCount) {
        const cursorRequest = store.openCursor();
        let deletedCount = 0;
        cursorRequest.onsuccess = (event) => {
            const cursor = event.target.result;
            if (cursor && deletedCount < deleteCount) {
                cursor.delete();
                deletedCount++;
                cursor.continue();
            }
        };
        cursorRequest.onerror = (event) => {
            console.error('删除记录失败:', event.target.error);
        };
    }

    // 插入新数据
    function insertData(store, data) {
        if (Array.isArray(data)) {
            data.forEach(item => store.add(item));
        } else {
            const addRequest = store.add(data);
            addRequest.onsuccess = () => {
                  const getAllRequest = store.getAll();
            getAllRequest.onsuccess = () => {
            };
            getAllRequest.onerror = (event) => {
                console.error('获取数据失败:', event.target.error);
            };
           };
            addRequest.onerror = (event) => {
                console.error('插入数据失败:', event.target.error);
            };
        }
    }
    // 获取所有离线数据
    async function getAllUsers(db) {
       const transaction = db.transaction('action', 'readonly');
	   const store = transaction.objectStore('action');
	   const arr = store.getAll();
	   arr.onsuccess = async (event) => {
		  const cursor = event.target.result;
		if (cursor.length) {
			await request(cursor, 'Y', 'login','old')
		 }
	  };
	  arr.onerror = function (event) {
	  	console.error('获取数据失败:', event.target.error);
	  };
    }
     //心跳
     function startTimer() {
        if (!timer) {
           timer = setInterval(async () => {
                 // 获取存储的值
                 const active = GM_getValue('active');
                 const token = GM_getValue('token');
                 const userid = GM_getValue('userid');
                 const proIP = GM_getValue('proIP');
                 const operateStatus = GM_getValue('operateStatus');

                 if (userid) {
                     try {
                         if (token) {
                             // 使用 fetch 发送心跳请求
                             const response = await fetch(`${proIP}/boos/plugins/heartbeat`, {
                                 method: 'POST',
                                 headers: {
                                     'Content-Type': 'application/json',
                                     'Authorization': `Bearer ${token}` // 添加 Authorization 头
                                 },
                                 body: JSON.stringify({
                                     stuId: userid,
                                     operateStatus
                                 })
                             });
                             if (response.code==200) {
                                 // 请求成功后的处理
                                 GM_setValue('operateStatus', false); // 将 operateStatus 设置为 false
                             }
                         }
                     } catch (error) {
                          GM_setValue('proIP', proInerNet);
                   }
               }

           }, BEATNUM);

        }
    }


})();