console
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>微信公众号 AccessToken 管理系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #2c3e50, #34495e);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 2.2em;
margin-bottom: 10px;
}
.header p {
opacity: 0.8;
font-size: 1.1em;
}
.main-content {
padding: 30px;
}
.card {
background: white;
border-radius: 15px;
padding: 25px;
margin-bottom: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.08);
border: 1px solid #f0f0f0;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0,0,0,0.15);
}
.card h3 {
color: #2c3e50;
margin-bottom: 20px;
font-size: 1.4em;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #34495e;
}
.form-group input, .form-group select {
width: 100%;
padding: 12px 15px;
border: 2px solid #e0e0e0;
border-radius: 10px;
font-size: 16px;
transition: border-color 0.3s ease;
}
.form-group input:focus, .form-group select:focus {
outline: none;
border-color: #3498db;
}
.btn {
background: linear-gradient(135deg, #3498db, #2980b9);
color: white;
border: none;
padding: 12px 25px;
border-radius: 10px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: all 0.3s ease;
margin-right: 10px;
margin-bottom: 10px;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(52, 152, 219, 0.4);
}
.btn-success {
background: linear-gradient(135deg, #27ae60, #229954);
}
.btn-success:hover {
box-shadow: 0 5px 15px rgba(39, 174, 96, 0.4);
}
.btn-danger {
background: linear-gradient(135deg, #e74c3c, #c0392b);
}
.btn-danger:hover {
box-shadow: 0 5px 15px rgba(231, 76, 60, 0.4);
}
.status {
padding: 15px;
border-radius: 10px;
margin: 15px 0;
font-weight: 600;
}
.status-active {
background: linear-gradient(135deg, #d5f4e6, #c3f0d9);
color: #27ae60;
border: 1px solid #27ae60;
}
.status-inactive {
background: linear-gradient(135deg, #ffeaa7, #fdcb6e);
color: #e17055;
border: 1px solid #e17055;
}
.token-display {
background: #f8f9fa;
border: 2px solid #e9ecef;
border-radius: 10px;
padding: 15px;
font-family: 'Courier New', monospace;
word-break: break-all;
max-height: 200px;
overflow-y: auto;
}
.logs {
background: #2c3e50;
color: #ecf0f1;
border-radius: 10px;
padding: 20px;
max-height: 300px;
overflow-y: auto;
font-family: 'Courier New', monospace;
font-size: 14px;
}
.log-entry {
margin-bottom: 5px;
padding: 5px;
border-radius: 5px;
}
.log-success {
background: rgba(39, 174, 96, 0.2);
}
.log-error {
background: rgba(231, 76, 60, 0.2);
}
.log-info {
background: rgba(52, 152, 219, 0.2);
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
@media (max-width: 768px) {
.grid {
grid-template-columns: 1fr;
}
.header h1 {
font-size: 1.8em;
}
.main-content {
padding: 20px;
}
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.7; }
100% { opacity: 1; }
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>�� 微信公众号 AccessToken 管理系统</h1>
<p>智能获取、管理和推送微信公众号访问令牌</p>
</div>
<div class="main-content">
<div class="card">
<h3>�� 基础配置</h3>
<div class="grid">
<div>
<div class="form-group">
<label for="appid">AppID *</label>
<input type="text" id="appid" placeholder="请输入微信公众号AppID">
</div>
<div class="form-group">
<label for="appsecret">AppSecret *</label>
<input type="password" id="appsecret" placeholder="请输入微信公众号AppSecret">
</div>
</div>
<div>
<div class="form-group">
<label for="interval">获取间隔(分钟)*</label>
<select id="interval">
<option value="30">30分钟</option>
<option value="60" selected>1小时</option>
<option value="90">1.5小时</option>
<option value="120">2小时</option>
</select>
</div>
<div class="form-group">
<label for="webhook">Webhook URL *</label>
<input type="url" id="webhook" placeholder="https://your-webhook-url.com/receive">
</div>
</div>
</div>
<button class="btn" onclick="saveConfig()">�� 保存配置</button>
<button class="btn btn-success" onclick="testConfig()">�� 测试配置</button>
</div>
<div class="card">
<h3>⚡ 服务控制</h3>
<div id="serviceStatus" class="status status-inactive">
�� 服务未启动
</div>
<button class="btn btn-success" onclick="startService()">▶️ 启动服务</button>
<button class="btn btn-danger" onclick="stopService()">⏹️ 停止服务</button>
<button class="btn" onclick="manualRefresh()">�� 手动获取Token</button>
</div>
<div class="card">
<h3>�� 当前AccessToken</h3>
<div class="form-group">
<label>Token值:</label>
<div id="tokenDisplay" class="token-display">暂无Token</div>
</div>
<div class="form-group">
<label>过期时间:</label>
<div id="expireTime" class="token-display">--</div>
</div>
<div class="form-group">
<label>上次更新:</label>
<div id="lastUpdate" class="token-display">--</div>
</div>
</div>
<div class="card">
<h3>�� 运行日志</h3>
<div id="logs" class="logs">
<div class="log-entry log-info">[系统] 微信AccessToken管理系统已启动</div>
</div>
<button class="btn" onclick="clearLogs()">��️ 清空日志</button>
</div>
</div>
</div>
<script>
let serviceInterval = null;
let currentConfig = {};
let isServiceRunning = false;
window.onload = function() {
loadConfig();
addLog('系统初始化完成', 'info');
};
function saveConfig() {
const config = {
appid: document.getElementById('appid').value,
appsecret: document.getElementById('appsecret').value,
interval: parseInt(document.getElementById('interval').value),
webhook: document.getElementById('webhook').value
};
if (!config.appid || !config.appsecret || !config.webhook) {
alert('请填写完整的配置信息!');
return;
}
if (!isValidUrl(config.webhook)) {
alert('请输入有效的Webhook URL!');
return;
}
currentConfig = config;
addLog('配置已保存', 'success');
alert('配置保存成功!');
}
function loadConfig() {
if (currentConfig.appid) {
document.getElementById('appid').value = currentConfig.appid;
document.getElementById('appsecret').value = currentConfig.appsecret;
document.getElementById('interval').value = currentConfig.interval;
document.getElementById('webhook').value = currentConfig.webhook;
}
}
async function testConfig() {
if (!currentConfig.appid || !currentConfig.appsecret) {
alert('请先保存配置!');
return;
}
addLog('正在测试配置...', 'info');
try {
await simulateWeChatTest();
addLog('微信API连接测试成功', 'success');
await testWebhook();
addLog('Webhook连接测试成功', 'success');
alert('配置测试通过!');
} catch (error) {
addLog(`配置测试失败: ${error.message}`, 'error');
alert('配置测试失败,请检查配置信息!');
}
}
function startService() {
if (!currentConfig.appid || !currentConfig.appsecret) {
alert('请先保存配置!');
return;
}
if (isServiceRunning) {
alert('服务已在运行中!');
return;
}
isServiceRunning = true;
updateServiceStatus(true);
getAccessToken();
const intervalMs = currentConfig.interval * 60 * 1000;
serviceInterval = setInterval(getAccessToken, intervalMs);
addLog(`服务已启动,每${currentConfig.interval}分钟获取一次Token`, 'success');
}
function stopService() {
if (!isServiceRunning) {
alert('服务未在运行!');
return;
}
isServiceRunning = false;
updateServiceStatus(false);
if (serviceInterval) {
clearInterval(serviceInterval);
serviceInterval = null;
}
addLog('服务已停止', 'info');
}
function manualRefresh() {
if (!currentConfig.appid || !currentConfig.appsecret) {
alert('请先保存配置!');
return;
}
addLog('手动获取AccessToken...', 'info');
getAccessToken();
}
async function getAccessToken() {
try {
addLog('正在获取AccessToken...', 'info');
const tokenData = await simulateGetAccessToken();
updateTokenDisplay(tokenData);
await pushToWebhook(tokenData);
addLog('AccessToken获取成功并已推送', 'success');
} catch (error) {
addLog(`获取AccessToken失败: ${error.message}`, 'error');
}
}
function simulateGetAccessToken() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const mockToken = generateMockToken();
const expireTime = new Date(Date.now() + 7200 * 1000);
resolve({
access_token: mockToken,
expires_in: 7200,
expire_time: expireTime,
timestamp: new Date()
});
}, 1000);
});
}
function generateMockToken() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < 120; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
function updateTokenDisplay(tokenData) {
document.getElementById('tokenDisplay').textContent = tokenData.access_token;
document.getElementById('expireTime').textContent = tokenData.expire_time.toLocaleString();
document.getElementById('lastUpdate').textContent = tokenData.timestamp.toLocaleString();
}
async function pushToWebhook(tokenData) {
if (!currentConfig.webhook) return;
try {
await simulateWebhookPush(tokenData);
addLog(`Token已推送到: ${currentConfig.webhook}`, 'success');
} catch (error) {
addLog(`Webhook推送失败: ${error.message}`, 'error');
throw error;
}
}
function simulateWebhookPush(tokenData) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.1;
if (success) {
resolve();
} else {
reject(new Error('网络连接失败'));
}
}, 500);
});
}
function testWebhook() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.2;
if (success) {
resolve();
} else {
reject(new Error('Webhook无法访问'));
}
}, 800);
});
}
function simulateWeChatTest() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = Math.random() > 0.15;
if (success) {
resolve();
} else {
reject(new Error('微信API连接失败'));
}
}, 1200);
});
}
function updateServiceStatus(running) {
const statusElement = document.getElementById('serviceStatus');
if (running) {
statusElement.className = 'status status-active pulse';
statusElement.innerHTML = '�� 服务运行中';
} else {
statusElement.className = 'status status-inactive';
statusElement.innerHTML = '�� 服务未启动';
}
}
function addLog(message, type = 'info') {
const logsContainer = document.getElementById('logs');
const timestamp = new Date().toLocaleTimeString();
const logEntry = document.createElement('div');
logEntry.className = `log-entry log-${type}`;
logEntry.innerHTML = `[${timestamp}] ${message}`;
logsContainer.appendChild(logEntry);
logsContainer.scrollTop = logsContainer.scrollHeight;
const logs = logsContainer.querySelectorAll('.log-entry');
if (logs.length > 100) {
logs[0].remove();
}
}
function clearLogs() {
const logsContainer = document.getElementById('logs');
logsContainer.innerHTML = '<div class="log-entry log-info">[系统] 日志已清空</div>';
}
function isValidUrl(string) {
try {
new URL(string);
return true;
} catch (_) {
return false;
}
}
</script>
</body>
</html>