// ==UserScript==
// @name 联通内网 - 智慧法治平台
// @namespace http://unicom.studio/
// @version 2025-04-08
// @description 创新与灵感的交汇处
// @author easterNday
// @match https://lawplatform.chinaunicom.cn/*
// @icon https://unicom.studio/Unicom.svg
// @grant GM_xmlhttpRequest
// @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js
// @require data:application/javascript,window.LAYUI_GLOBAL=%7Bdir:'https://unpkg.com/[email protected]/dist/'%7D
// @require https://unpkg.com/[email protected]/dist/layui.js
// @run-at document-start
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const PASSWORD = 'lawtalkwebfront_';
const CONCURRENCY_LIMIT = 5;
// 状态管理对象
const state = {
authorization: localStorage.getItem('law_auth') || '', // 持久化存储[5](@ref)
isRunning: false,
currentPage: 1,
ready: false,
};
// 用户信息
const userInfo = {
name: '',
pageSize: localStorage.getItem('law_page_size') || 50,
};
const articleStore = {
list: new Map(),
total: 0,
get count() {
return this.list.size;
}
};
// 请求拦截
(function setupRequestInterceptor() {
const originalOpen = XMLHttpRequest.prototype.open;
const originalSetHeader = XMLHttpRequest.prototype.setRequestHeader;
// 请求头拦截
XMLHttpRequest.prototype.requestHeaders = {};
XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
this.requestHeaders[name] = value;
originalSetHeader.apply(this, arguments);
};
// 核心拦截逻辑
XMLHttpRequest.prototype.open = function (method, url) {
this.addEventListener('load', function () {
// 仅捕获Authorization字段
if (this.requestHeaders) {
const authHeader = this.requestHeaders['Authorization'] || this.requestHeaders['authorization'];
if (authHeader && authHeader !== state.authorization) {
state.authorization = authHeader;
localStorage.setItem('law_auth', authHeader);
}
}
// TODO: 此处还需要增加用户信息获取功能
// userName = decodeURIComponent(this.responseURL.split('mm=')[1].split('&')[0])
// userName = cryptoUtils.decryptECB(atob(userName), PASSWORD)
// if (userName) {
// isGetUserName = true;
// }
// TODO: 下面是第一版的实现
if (url.includes('readPurview') && !userInfo.name) {
const mm = new URL(url).searchParams.get('mm');
userInfo.name = cryptoUtils.decryptECB(atob(mm));
}
});
originalOpen.apply(this, arguments);
};
})();
// 加密模块
const cryptoUtils = {
encryptECB: (text) => {
const key = CryptoJS.enc.Utf8.parse(PASSWORD.padEnd(16, "\0"));
return CryptoJS.AES.encrypt(text, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).toString();
},
decryptECB: (cipherText) => {
const key = CryptoJS.enc.Utf8.parse(PASSWORD.padEnd(16, "\0"));
return CryptoJS.AES.decrypt(cipherText, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).toString(CryptoJS.enc.Utf8);
}
};
// 请求队列系统
const requestQueue = {
queue: [],
active: 0,
async add(task) {
this.queue.push(task);
return this.process();
},
async process() {
return new Promise((resolve) => {
const runner = async () => {
while (this.active < CONCURRENCY_LIMIT && this.queue.length) {
const task = this.queue.shift();
this.active++;
try {
await task();
} catch (error) {
console.error('Task failed:', error);
} finally {
this.active--;
runner();
}
}
if (this.queue.length === 0 && this.active === 0) {
resolve();
}
};
runner();
});
}
};
// 数据获取逻辑
const dataService = {
async getTotal() {
const params = this.buildParams(1);
const { total } = await this.fetchData(params);
articleStore.total = total;
return total;
},
async fetchPage(pageNum) {
const params = this.buildParams(pageNum);
const { rows } = await this.fetchData(params);
rows.forEach(article => {
articleStore.list.set(article.articleId, article);
});
return rows;
},
buildParams(pageNum) {
return {
pageNum,
pageSize: userInfo.pageSize,
sortType: 0,
columnOne: null,
columnTwo: null,
readPurviewValueList: [
"52afcdceba40421e85ae255d84611ce1",
"c9f4122a68904299b737555e2c13858d",
"fb9a92b14df74671840dc9c5bce47b22",
"42",
"4211",
"421121998"
],
readPurviewRoleTypeList: [],
m_m: userInfo.name
};
},
async fetchData(params) {
const mm = cryptoUtils.encryptECB(JSON.stringify(params));
const response = await fetch(
`https://lawplatform.chinaunicom.cn/api/legal/more/article/list?pageSize=${userInfo.pageSize}&pageNum=${params.pageNum}`,
{
method: 'POST',
headers: {
authorization: state.authorization,
'content-type': 'application/json'
},
body: JSON.stringify({ mm })
}
);
return response.json();
}
};
// 文章处理
async function processArticles() {
try {
const totalPages = Math.ceil(articleStore.total / userInfo.pageSize);
const tasks = Array.from({ length: totalPages }, (_, i) =>
() => dataService.fetchPage(i + 1)
);
// 清空队列并添加任务
requestQueue.queue = tasks;
// ✅ 等待所有分页完成
await requestQueue.process();
layer.msg(`已加载 ${articleStore.count} 篇文章`, { icon: 1 });
// 创建文章处理任务
const articleTasks = Array.from(articleStore.list.values(), article =>
() => Promise.allSettled([
processRead(article),
processLike(article)
])
);
// 添加文章处理任务
requestQueue.queue = articleTasks;
await requestQueue.process();
layer.msg('所有操作已完成', { icon: 1 });
} catch (error) {
layer.msg('处理失败: ' + error.message, { icon: 2 });
}
}
// 阅读处理
async function processRead(article) {
// if (article.isBrowse) return;
const timestamp = cryptoUtils.encryptECB(Date.now().toString());
const mm = cryptoUtils.encryptECB(JSON.stringify({
businessId: article.articleId,
m_m: userInfo.name
}));
await Promise.all([
fetch(`https://lawplatform.chinaunicom.cn/api/legal-console/integral/checkIntegralForBrowser`, {
method: 'POST',
headers: {
authorization: state.authorization,
'content-type': 'application/json'
},
body: JSON.stringify({ mm })
}),
// fetch(`https://lawplatform.chinaunicom.cn/api/legal/article/details/readPurview?timeStamp=${btoa(timestamp)}&mm=${btoa(mm)}`, {
// headers: { authorization: state.authorization }
// }),
fetch(`https://lawplatform.chinaunicom.cn/api/legal/article/details/add-browse-num/${article.articleId}`, {
headers: { authorization: state.authorization }
})
]);
}
// 点赞处理
async function processLike(article) {
// if (article.isLike) return;
const mm = cryptoUtils.encryptECB(JSON.stringify({
articleId: article.articleId,
whetherLike: 1,
m_m: userInfo.name
}));
await fetch("https://lawplatform.chinaunicom.cn/api/legal/like-detail", {
method: 'POST',
headers: {
authorization: state.authorization,
'content-type': 'application/json'
},
body: JSON.stringify({ mm })
});
}
// 初始化入口
async function init() {
// setupRequestInterceptor();
await waitForUserInfo().then(() => { layer.msg('基础信息获取完成', { time: 7777, icon: 1 }); });
await dataService.getTotal();
await createUI();
}
async function waitForUserInfo() {
return new Promise(resolve => {
const check = () => {
if (userInfo.name && state.authorization) {
state.ready = true;
resolve();
} else {
setTimeout(check, 500);
}
};
check();
});
}
// 创建UI
async function createUI() {
// TODO: 完善UI
let buttonSets = document.createElement("div");
document.body.appendChild(buttonSets);
buttonSets.id = "buttonSets";
buttonSets.style.padding = "8px";
buttonSets.style.display = "flex";
buttonSets.style.flexDirection = "row";
buttonSets.style.justifyContent = "center";
buttonSets.style.alignItems = "center";
buttonSets.style.position = "fixed";
buttonSets.style.bottom = "20px";
buttonSets.style.right = "20px";
buttonSets.style.zIndex = "1000";
buttonSets.innerHTML = `
<link href="//unpkg.com/[email protected]/dist/css/layui.css" rel="stylesheet">
<button id="controlPanelButton" type="button" class="layui-btn layui-btn-lg layui-btn-primary layui-btn-radius layui-bg-red">控制面板</button>
`
let controlPanelButton = document.getElementById("controlPanelButton")
controlPanelButton.onclick = () => {
layer.open({
type: 1,
id: "settingsPanel",
title: "智慧法治平台配置",
area: ["320px", "100%"],
offset: 'l',
anim: 'slideRight', // 从左往右
shade: 0.1,
// skin: 'layui-layer-win10',
icon: 6,
resize: false,
move: false,
content: `
<div class="layui-input-group">
<div class="layui-input-prefix">用户昵称</div>
<input type="text" name="username" value="" lay-verify="required" placeholder="${userInfo.name}" class="layui-input" disabled>
</div>
<div class="layui-input-group">
<div class="layui-input-prefix">文章总数</div>
<input type="text" name="username" value="" lay-verify="required" placeholder="${articleStore.total}" class="layui-input" disabled>
</div>
<div class="layui-input-group">
<div class="layui-input-prefix">单次请求数目</div>
<input id="pageSize" type="number" lay-affix="number" value="${userInfo.pageSize}" step="10" min="10" max="50" class="layui-input">
<!-- <div class="layui-input-suffix">单次请求数目</div> -->
</div>
`,
btn: ['保存设置', '开始运行'],
btn1: function (index, layero, that) {
localStorage.setItem('law_page_size', document.getElementById("pageSize").value);
userInfo.pageSize = document.getElementById("pageSize").value;
return false
},
btn2: function (index, layero, that) {
processArticles();
return true
}
});
}
}
window.addEventListener('load', init);
})();