清理项目多余文件

删除以下文件:
- 8个修复脚本(fix_*.py)
- 5个app.py备份文件
- 7个templates备份文件
- 1个重复的admin.html文件

共删除21个临时文件和备份文件,保持项目整洁。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-12-10 15:57:39 +08:00
parent f9aa511806
commit 97d1406e11
21 changed files with 0 additions and 28781 deletions

2213
admin.html

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,65 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""修复浏览器池初始化 - 在socketio启动前同步初始化"""
import re
with open('/www/wwwroot/zsglpt/app.py', 'r', encoding='utf-8') as f:
content = f.read()
# 1. 删除模块级别的后台初始化线程启动代码
# 这行代码在 if __name__ == '__main__': 之前会在eventlet接管后执行
old_init_code = '''def init_pool_background():
import time
time.sleep(5) # 等待应用启动
try:
config = database.get_system_config()
pool_size = config.get('max_screenshot_concurrent', 3)
print(f"[浏览器池] 开始预热 {pool_size} 个浏览器...")
init_browser_pool(pool_size=pool_size)
except Exception as e:
print(f"[浏览器池] 初始化失败: {e}")
threading.Thread(target=init_pool_background, daemon=True).start()
if __name__ == '__main__':'''
# 替换为新的代码 - 移除后台线程
new_init_code = '''if __name__ == '__main__':'''
if old_init_code in content:
content = content.replace(old_init_code, new_init_code)
print("OK - 已移除后台初始化线程")
else:
print("WARNING - 未找到后台初始化代码,尝试其他模式")
# 2. 在 socketio.run 之前添加同步初始化
# 查找 socketio.run 位置,在其之前添加初始化代码
old_socketio_run = ''' print("=" * 60 + "\\n")
socketio.run(app, host=config.SERVER_HOST, port=config.SERVER_PORT, debug=config.DEBUG)'''
new_socketio_run = ''' print("=" * 60 + "\\n")
# 同步初始化浏览器池必须在socketio.run之前否则eventlet会导致asyncio冲突
try:
system_cfg = database.get_system_config()
pool_size = system_cfg.get('max_screenshot_concurrent', 3) if system_cfg else 3
print(f"正在预热 {pool_size} 个浏览器实例(截图加速)...")
init_browser_pool(pool_size=pool_size)
print("✓ 浏览器池初始化完成")
except Exception as e:
print(f"警告: 浏览器池初始化失败: {e}")
socketio.run(app, host=config.SERVER_HOST, port=config.SERVER_PORT, debug=config.DEBUG)'''
if old_socketio_run in content:
content = content.replace(old_socketio_run, new_socketio_run)
print("OK - 已添加同步初始化代码")
else:
print("WARNING - 未找到socketio.run位置")
with open('/www/wwwroot/zsglpt/app.py', 'w', encoding='utf-8') as f:
f.write(content)
print("完成浏览器池将在socketio启动前同步初始化")

View File

@@ -1,92 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""修复quick_login - 使用池中浏览器时直接登录不尝试加载cookies"""
import re
with open('/www/wwwroot/zsglpt/playwright_automation.py', 'r', encoding='utf-8') as f:
content = f.read()
# 修复 quick_login 方法 - 当已有浏览器时直接登录
old_quick_login = ''' def quick_login(self, username: str, password: str, remember: bool = True):
"""快速登录 - 优先使用cookies失败则正常登录"""
# 尝试使用cookies
if self.load_cookies(username):
self.log(f"尝试使用已保存的登录态...")
if self.check_login_state():
self.log(f"✓ 登录态有效,跳过登录")
return {"success": True, "message": "使用已保存的登录态", "used_cookies": True}
else:
self.log(f"登录态已失效,重新登录")
# 关闭当前context重新登录
try:
if self.context:
self.context.close()
if self.browser:
self.browser.close()
if self.playwright:
self.playwright.stop()
except:
pass
# 正常登录
result = self.login(username, password, remember)
# 登录成功后保存cookies
if result.get('success'):
self.save_cookies(username)
result['used_cookies'] = False
return result'''
new_quick_login = ''' def quick_login(self, username: str, password: str, remember: bool = True):
"""快速登录 - 使用池中浏览器时直接登录否则尝试cookies"""
# 如果已有浏览器实例(从池中获取),直接使用该浏览器登录
# 不尝试加载cookies因为load_cookies会创建新浏览器覆盖池中的
if self.browser and self.browser.is_connected():
self.log("使用池中浏览器,直接登录")
result = self.login(username, password, remember)
if result.get('success'):
self.save_cookies(username)
result['used_cookies'] = False
return result
# 无现有浏览器时尝试使用cookies
if self.load_cookies(username):
self.log(f"尝试使用已保存的登录态...")
if self.check_login_state():
self.log(f"✓ 登录态有效,跳过登录")
return {"success": True, "message": "使用已保存的登录态", "used_cookies": True}
else:
self.log(f"登录态已失效,重新登录")
# <20><>闭当前context重新登录
try:
if self.context:
self.context.close()
if self.browser:
self.browser.close()
if self.playwright:
self.playwright.stop()
except:
pass
# 正常登录
result = self.login(username, password, remember)
# 登录成功后保存cookies
if result.get('success'):
self.save_cookies(username)
result['used_cookies'] = False
return result'''
if old_quick_login in content:
content = content.replace(old_quick_login, new_quick_login)
print("OK - quick_login已修复")
else:
print("WARNING - 未找到old_quick_login")
with open('/www/wwwroot/zsglpt/playwright_automation.py', 'w', encoding='utf-8') as f:
f.write(content)
print("完成")

View File

@@ -1,43 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""修复quick_login - 使用池中浏览器时直接登录"""
with open('/www/wwwroot/zsglpt/playwright_automation.py', 'r', encoding='utf-8') as f:
content = f.read()
# 找到quick_login方法并替换
old = ''' def quick_login(self, username: str, password: str, remember: bool = True):
"""快速登录 - 优先使用cookies失败则正常登录"""
# 尝试使用cookies
if self.load_cookies(username):'''
new = ''' def quick_login(self, username: str, password: str, remember: bool = True):
"""快速登录 - 使用池中浏览器时直接登录否则尝试cookies"""
# 如果已有浏览器实例(从池中获取),直接使用该浏览器登录
# 不尝试加载cookies因为load_cookies会创建新浏览器覆盖池中的
if self.browser and self.browser.is_connected():
self.log("使用池中浏览器,直接登录")
result = self.login(username, password, remember)
if result.get('success'):
self.save_cookies(username)
result['used_cookies'] = False
return result
# 无现有浏览器时尝试使用cookies
if self.load_cookies(username):'''
if old in content:
content = content.replace(old, new)
print("OK - quick_login已修复")
else:
print("WARNING - 未找到匹配内容,显示实际内容进行对比")
import re
match = re.search(r'def quick_login.*?(?=\n def |\n\nclass |\Z)', content, re.DOTALL)
if match:
print("实际内容前200字符:")
print(repr(match.group(0)[:200]))
with open('/www/wwwroot/zsglpt/playwright_automation.py', 'w', encoding='utf-8') as f:
f.write(content)
print("完成")

View File

@@ -1,403 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""修复定时任务并添加执行日志功能"""
def add_schedule_logs_table(database_content):
"""在database.py中添加定时任务执行日志表"""
# 在init_database函数中添加表创建代码
insert_position = ''' print(" ✓ 创建 user_schedules 表 (用户定时任务)")'''
new_table_code = ''' print(" ✓ 创建 user_schedules 表 (用户定时任务)")
# 定时任务执行日志表
cursor.execute(\'''
CREATE TABLE IF NOT EXISTS schedule_execution_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
schedule_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
schedule_name TEXT,
execute_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
total_accounts INTEGER DEFAULT 0,
success_accounts INTEGER DEFAULT 0,
failed_accounts INTEGER DEFAULT 0,
total_items INTEGER DEFAULT 0,
total_attachments INTEGER DEFAULT 0,
total_screenshots INTEGER DEFAULT 0,
duration_seconds INTEGER DEFAULT 0,
status TEXT DEFAULT 'running',
error_message TEXT,
FOREIGN KEY (schedule_id) REFERENCES user_schedules (id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
)
\''')
print(" ✓ 创建 schedule_execution_logs 表 (定时任务执行日志)")'''
if insert_position in database_content:
database_content = database_content.replace(insert_position, new_table_code)
print("✓ 已添加schedule_execution_logs表创建代码")
else:
print("❌ 未找到插入位置")
return database_content
# 添加数据库操作函数
functions_code = '''
# ==================== 定时任务执行日志 ====================
def create_schedule_execution_log(schedule_id, user_id, schedule_name):
"""创建定时任务执行日志"""
with db_pool.get_db() as conn:
cursor = conn.cursor()
cst_tz = pytz.timezone("Asia/Shanghai")
execute_time = datetime.now(cst_tz).strftime("%Y-%m-%d %H:%M:%S")
cursor.execute(\'''
INSERT INTO schedule_execution_logs (
schedule_id, user_id, schedule_name, execute_time, status
) VALUES (?, ?, ?, ?, 'running')
\''', (schedule_id, user_id, schedule_name, execute_time))
conn.commit()
return cursor.lastrowid
def update_schedule_execution_log(log_id, **kwargs):
"""更新定时任务执行日志"""
with db_pool.get_db() as conn:
cursor = conn.cursor()
updates = []
params = []
allowed_fields = ['total_accounts', 'success_accounts', 'failed_accounts',
'total_items', 'total_attachments', 'total_screenshots',
'duration_seconds', 'status', 'error_message']
for field in allowed_fields:
if field in kwargs:
updates.append(f'{field} = ?')
params.append(kwargs[field])
if not updates:
return False
params.append(log_id)
sql = f"UPDATE schedule_execution_logs SET {', '.join(updates)} WHERE id = ?"
cursor.execute(sql, params)
conn.commit()
return cursor.rowcount > 0
def get_schedule_execution_logs(schedule_id, limit=10):
"""获取定时任务执行日志"""
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute(\'''
SELECT * FROM schedule_execution_logs
WHERE schedule_id = ?
ORDER BY execute_time DESC
LIMIT ?
\''', (schedule_id, limit))
return [dict(row) for row in cursor.fetchall()]
def get_user_all_schedule_logs(user_id, limit=50):
"""获取用户所有定时任务的执行日志"""
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute(\'''
SELECT * FROM schedule_execution_logs
WHERE user_id = ?
ORDER BY execute_time DESC
LIMIT ?
\''', (user_id, limit))
return [dict(row) for row in cursor.fetchall()]
'''
# 在文件末尾添加这些函数
database_content += functions_code
print("✓ 已添加定时任务日志操作函数")
return database_content
def add_schedule_log_tracking(app_content):
"""在app.py中添加定时任务执行日志记录"""
# 修改check_user_schedules函数添加日志记录
old_code = ''' print(f"[用户定时任务] 用户 {schedule_config.get('user_username', user_id)} 的任务 '{schedule_config.get('name', '')}' 开始执行")
started_count = 0
for account_id in account_ids:'''
new_code = ''' print(f"[用户定时任务] 用户 {schedule_config.get('user_username', user_id)} 的任务 '{schedule_config.get('name', '')}' 开始执行")
# 创建执行日志
import time as time_mod
execution_start_time = time_mod.time()
log_id = database.create_schedule_execution_log(
schedule_id=schedule_id,
user_id=user_id,
schedule_name=schedule_config.get('name', '未命名任务')
)
started_count = 0
for account_id in account_ids:'''
if old_code in app_content:
app_content = app_content.replace(old_code, new_code)
print("✓ 已添加执行日志创建代码")
else:
print("⚠ 未找到执行日志创建位置")
# 添加日志更新代码(在任务执行完成后)
old_code2 = ''' # 更新最后执行时间
database.update_schedule_last_run(schedule_id)
print(f"[用户定时任务] 已启动 {started_count} 个账号")'''
new_code2 = ''' # 更新最后执行时间
database.update_schedule_last_run(schedule_id)
# 更新执行日志
execution_duration = int(time_mod.time() - execution_start_time)
database.update_schedule_execution_log(
log_id,
total_accounts=len(account_ids),
success_accounts=started_count,
failed_accounts=len(account_ids) - started_count,
duration_seconds=execution_duration,
status='completed'
)
print(f"[用户定时任务] 已启动 {started_count} 个账号")'''
if old_code2 in app_content:
app_content = app_content.replace(old_code2, new_code2)
print("✓ 已添加执行日志更新代码")
else:
print("⚠ 未找到执行日志更新位置")
# 添加日志查询API
api_code = '''
# ==================== 定时任务执行日志API ====================
@app.route('/api/schedules/<int:schedule_id>/logs', methods=['GET'])
@login_required
def get_schedule_logs_api(schedule_id):
"""获取定时任务执行日志"""
schedule = database.get_schedule_by_id(schedule_id)
if not schedule:
return jsonify({"error": "定时任务不存在"}), 404
if schedule['user_id'] != current_user.id:
return jsonify({"error": "无权访问"}), 403
limit = request.args.get('limit', 10, type=int)
logs = database.get_schedule_execution_logs(schedule_id, limit)
return jsonify(logs)
'''
# 在批量操作API之前插入
insert_marker = '# ==================== 批量操作API ===================='
if insert_marker in app_content:
app_content = app_content.replace(insert_marker, api_code + insert_marker)
print("✓ 已添加日志查询API")
else:
print("⚠ 未找到API插入位置")
return app_content
def add_frontend_log_button(html_content):
"""在前端添加日志按钮"""
# 修改定时任务卡片,添加日志按钮
old_html = ''' '<button class="btn btn-text btn-small" onclick="editSchedule(' + s.id + ')">编辑</button>' +
'<button class="btn btn-text btn-small" style="color: var(--md-error);" onclick="deleteSchedule(' + s.id + ')">删除</button>' +'''
new_html = ''' '<button class="btn btn-text btn-small" onclick="editSchedule(' + s.id + ')">编辑</button>' +
'<button class="btn btn-text btn-small" onclick="viewScheduleLogs(' + s.id + ')">日志</button>' +
'<button class="btn btn-text btn-small" style="color: var(--md-error);" onclick="deleteSchedule(' + s.id + ')">删除</button>' +'''
if old_html in html_content:
html_content = html_content.replace(old_html, new_html)
print("✓ 已添加日志按钮HTML")
else:
print("⚠ 未找到日志按钮插入位置")
# 添加日志弹窗HTML
modal_html = ''' <!-- 定时任务执行日志弹窗 -->
<div class="modal-overlay" id="scheduleLogsModal">
<div class="modal" style="max-width: 800px;">
<div class="modal-header"><h3 class="modal-title" id="scheduleLogsTitle">执行日志</h3></div>
<div class="modal-body">
<div id="scheduleLogsList" style="max-height: 500px; overflow-y: auto;"></div>
<div id="emptyScheduleLogs" class="empty-state" style="display: none;">
<div class="empty-state-icon">📝</div>
<p>暂无执行记录</p>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-text" onclick="closeModal('scheduleLogsModal')">关闭</button>
</div>
</div>
</div>
<script>'''
insert_marker = ' <script>'
if insert_marker in html_content:
html_content = html_content.replace(insert_marker, modal_html, 1)
print("✓ 已添加日志弹窗HTML")
else:
print("⚠ 未找到弹窗插入位置")
# 添加JavaScript函数
js_code = '''
// ==================== 定时任务执行日志 ====================
function viewScheduleLogs(scheduleId) {
const schedule = schedules.find(s => s.id === scheduleId);
if (!schedule) {
showToast('定时任务不存在', 'error');
return;
}
document.getElementById('scheduleLogsTitle').textContent = schedule.name + ' - 执行日志';
loadScheduleLogs(scheduleId);
openModal('scheduleLogsModal');
}
function loadScheduleLogs(scheduleId) {
fetch('/api/schedules/' + scheduleId + '/logs?limit=20')
.then(r => r.json())
.then(logs => {
const container = document.getElementById('scheduleLogsList');
const empty = document.getElementById('emptyScheduleLogs');
if (!logs || logs.length === 0) {
container.innerHTML = '';
empty.style.display = 'block';
return;
}
empty.style.display = 'none';
let html = '<div style="display: grid; gap: 12px;">';
logs.forEach(log => {
const statusColor = log.status === 'completed' ? '#4CAF50' :
log.status === 'failed' ? '#F44336' : '#FF9800';
const statusText = log.status === 'completed' ? '已完成' :
log.status === 'failed' ? '失败' : '运行中';
html += '<div style="background: #f5f5f5; border-radius: 8px; padding: 16px; border-left: 4px solid ' + statusColor + ';">';
html += '<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">';
html += '<div style="font-weight: 500; font-size: 14px;">' + log.execute_time + '</div>';
html += '<div style="color: ' + statusColor + '; font-weight: 600; font-size: 13px;">' + statusText + '</div>';
html += '</div>';
html += '<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; font-size: 13px;">';
html += '<div><span style="color: #666;">账号数:</span> <strong>' + (log.total_accounts || 0) + '</strong></div>';
html += '<div><span style="color: #666;">成功:</span> <strong style="color: #4CAF50;">' + (log.success_accounts || 0) + '</strong></div>';
html += '<div><span style="color: #666;">失败:</span> <strong style="color: #F44336;">' + (log.failed_accounts || 0) + '</strong></div>';
html += '<div><span style="color: #666;">浏览内容:</span> <strong>' + (log.total_items || 0) + '</strong></div>';
html += '<div><span style="color: #666;">查看附件:</span> <strong>' + (log.total_attachments || 0) + '</strong></div>';
html += '<div><span style="color: #666;">截图数:</span> <strong>' + (log.total_screenshots || 0) + '</strong></div>';
if (log.duration_seconds) {
const mins = Math.floor(log.duration_seconds / 60);
const secs = log.duration_seconds % 60;
html += '<div><span style="color: #666;">耗时:</span> <strong>' + mins + '' + secs + '秒</strong></div>';
}
html += '</div>';
if (log.error_message) {
html += '<div style="margin-top: 8px; padding: 8px; background: #FFEBEE; border-radius: 4px; color: #C62828; font-size: 12px;">';
html += '<strong>错误:</strong> ' + escapeHtml(log.error_message);
html += '</div>';
}
html += '</div>';
});
html += '</div>';
container.innerHTML = html;
})
.catch(err => {
showToast('加载日志失败', 'error');
console.error(err);
});
}
'''
# 在logout函数之前插入
insert_marker2 = ' function logout() {'
if insert_marker2 in html_content:
html_content = html_content.replace(insert_marker2, js_code + insert_marker2)
print("✓ 已添加日志查看JavaScript函数")
else:
print("⚠ 未找到JavaScript插入位置")
return html_content
def main():
import sys
print("=" * 60)
print("定时任务修复和日志功能添加脚本")
print("=" * 60)
print()
# 读取database.py
print("[1/3] 修改 database.py...")
with open('database.py', 'r', encoding='utf-8') as f:
database_content = f.read()
database_content = add_schedule_logs_table(database_content)
with open('database.py', 'w', encoding='utf-8') as f:
f.write(database_content)
print()
# 读取app.py
print("[2/3] 修改 app.py...")
with open('app.py', 'r', encoding='utf-8') as f:
app_content = f.read()
app_content = add_schedule_log_tracking(app_content)
with open('app.py', 'w', encoding='utf-8') as f:
f.write(app_content)
print()
# 读取templates/index.html
print("[3/3] 修改 templates/index.html...")
with open('templates/index.html', 'r', encoding='utf-8') as f:
html_content = f.read()
html_content = add_frontend_log_button(html_content)
with open('templates/index.html', 'w', encoding='utf-8') as f:
f.write(html_content)
print()
print("=" * 60)
print("✅ 所有修改完成!")
print("=" * 60)
print()
print("下一步操作:")
print("1. 重启Docker容器: docker-compose restart")
print("2. 检查定时任务是否启用: enabled = 1")
print("3. 测试定时任务执行")
print()
if __name__ == '__main__':
main()

View File

@@ -1,403 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""修复定时任务并添加执行日志功能"""
def add_schedule_logs_table(database_content):
"""在database.py中添加定时任务执行日志表"""
# 在init_database函数中添加表创建代码
insert_position = ''' print(" ✓ 创建 user_schedules 表 (用户定时任务)")'''
new_table_code = ''' print(" ✓ 创建 user_schedules 表 (用户定时任务)")
# 定时任务执行日志表
cursor.execute('''
CREATE TABLE IF NOT EXISTS schedule_execution_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
schedule_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
schedule_name TEXT,
execute_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
total_accounts INTEGER DEFAULT 0,
success_accounts INTEGER DEFAULT 0,
failed_accounts INTEGER DEFAULT 0,
total_items INTEGER DEFAULT 0,
total_attachments INTEGER DEFAULT 0,
total_screenshots INTEGER DEFAULT 0,
duration_seconds INTEGER DEFAULT 0,
status TEXT DEFAULT 'running',
error_message TEXT,
FOREIGN KEY (schedule_id) REFERENCES user_schedules (id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
)
''')
print(" ✓ 创建 schedule_execution_logs 表 (定时任务执行日志)")'''
if insert_position in database_content:
database_content = database_content.replace(insert_position, new_table_code)
print("✓ 已添加schedule_execution_logs表创建代码")
else:
print("❌ 未找到插入位置")
return database_content
# 添加数据库操作函数
functions_code = '''
# ==================== 定时任务执行日志 ====================
def create_schedule_execution_log(schedule_id, user_id, schedule_name):
"""创建定时任务执行日志"""
with db_pool.get_db() as conn:
cursor = conn.cursor()
cst_tz = pytz.timezone("Asia/Shanghai")
execute_time = datetime.now(cst_tz).strftime("%Y-%m-%d %H:%M:%S")
cursor.execute('''
INSERT INTO schedule_execution_logs (
schedule_id, user_id, schedule_name, execute_time, status
) VALUES (?, ?, ?, ?, 'running')
''', (schedule_id, user_id, schedule_name, execute_time))
conn.commit()
return cursor.lastrowid
def update_schedule_execution_log(log_id, **kwargs):
"""更新定时任务执行日志"""
with db_pool.get_db() as conn:
cursor = conn.cursor()
updates = []
params = []
allowed_fields = ['total_accounts', 'success_accounts', 'failed_accounts',
'total_items', 'total_attachments', 'total_screenshots',
'duration_seconds', 'status', 'error_message']
for field in allowed_fields:
if field in kwargs:
updates.append(f'{field} = ?')
params.append(kwargs[field])
if not updates:
return False
params.append(log_id)
sql = f"UPDATE schedule_execution_logs SET {', '.join(updates)} WHERE id = ?"
cursor.execute(sql, params)
conn.commit()
return cursor.rowcount > 0
def get_schedule_execution_logs(schedule_id, limit=10):
"""获取定时任务执行日志"""
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute('''
SELECT * FROM schedule_execution_logs
WHERE schedule_id = ?
ORDER BY execute_time DESC
LIMIT ?
''', (schedule_id, limit))
return [dict(row) for row in cursor.fetchall()]
def get_user_all_schedule_logs(user_id, limit=50):
"""获取用户所有定时任务的执行日志"""
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute('''
SELECT * FROM schedule_execution_logs
WHERE user_id = ?
ORDER BY execute_time DESC
LIMIT ?
''', (user_id, limit))
return [dict(row) for row in cursor.fetchall()]
'''
# 在文件末尾添加这些函数
database_content += functions_code
print("✓ 已添加定时任务日志操作函数")
return database_content
def add_schedule_log_tracking(app_content):
"""在app.py中添加定时任务执行日志记录"""
# 修改check_user_schedules函数添加日志记录
old_code = ''' print(f"[用户定时任务] 用户 {schedule_config.get('user_username', user_id)} 的任务 '{schedule_config.get('name', '')}' 开始执行")
started_count = 0
for account_id in account_ids:'''
new_code = ''' print(f"[用户定时任务] 用户 {schedule_config.get('user_username', user_id)} 的任务 '{schedule_config.get('name', '')}' 开始执行")
# 创建执行日志
import time as time_mod
execution_start_time = time_mod.time()
log_id = database.create_schedule_execution_log(
schedule_id=schedule_id,
user_id=user_id,
schedule_name=schedule_config.get('name', '未命名任务')
)
started_count = 0
for account_id in account_ids:'''
if old_code in app_content:
app_content = app_content.replace(old_code, new_code)
print("✓ 已添加执行日志创建代码")
else:
print("⚠ 未找到执行日志创建位置")
# 添加日志更新代码(在任务执行完成后)
old_code2 = ''' # 更新最后执行时间
database.update_schedule_last_run(schedule_id)
print(f"[用户定时任务] 已启动 {started_count} 个账号")'''
new_code2 = ''' # 更新最后执行时间
database.update_schedule_last_run(schedule_id)
# 更新执行日志
execution_duration = int(time_mod.time() - execution_start_time)
database.update_schedule_execution_log(
log_id,
total_accounts=len(account_ids),
success_accounts=started_count,
failed_accounts=len(account_ids) - started_count,
duration_seconds=execution_duration,
status='completed'
)
print(f"[用户定时任务] 已启动 {started_count} 个账号")'''
if old_code2 in app_content:
app_content = app_content.replace(old_code2, new_code2)
print("✓ 已添加执行日志更新代码")
else:
print("⚠ 未找到执行日志更新位置")
# 添加日志查询API
api_code = '''
# ==================== 定时任务执行日志API ====================
@app.route('/api/schedules/<int:schedule_id>/logs', methods=['GET'])
@login_required
def get_schedule_logs_api(schedule_id):
"""获取定时任务执行日志"""
schedule = database.get_schedule_by_id(schedule_id)
if not schedule:
return jsonify({"error": "定时任务不存在"}), 404
if schedule['user_id'] != current_user.id:
return jsonify({"error": "无权访问"}), 403
limit = request.args.get('limit', 10, type=int)
logs = database.get_schedule_execution_logs(schedule_id, limit)
return jsonify(logs)
'''
# 在批量操作API之前插入
insert_marker = '# ==================== 批量操作API ===================='
if insert_marker in app_content:
app_content = app_content.replace(insert_marker, api_code + insert_marker)
print("✓ 已添加日志查询API")
else:
print("⚠ 未找到API插入位置")
return app_content
def add_frontend_log_button(html_content):
"""在前端添加日志按钮"""
# 修改定时任务卡片,添加日志按钮
old_html = ''' '<button class="btn btn-text btn-small" onclick="editSchedule(' + s.id + ')">编辑</button>' +
'<button class="btn btn-text btn-small" style="color: var(--md-error);" onclick="deleteSchedule(' + s.id + ')">删除</button>' +'''
new_html = ''' '<button class="btn btn-text btn-small" onclick="editSchedule(' + s.id + ')">编辑</button>' +
'<button class="btn btn-text btn-small" onclick="viewScheduleLogs(' + s.id + ')">日志</button>' +
'<button class="btn btn-text btn-small" style="color: var(--md-error);" onclick="deleteSchedule(' + s.id + ')">删除</button>' +'''
if old_html in html_content:
html_content = html_content.replace(old_html, new_html)
print("✓ 已添加日志按钮HTML")
else:
print("⚠ 未找到日志按钮插入位置")
# 添加日志弹窗HTML
modal_html = ''' <!-- 定时任务执行日志弹窗 -->
<div class="modal-overlay" id="scheduleLogsModal">
<div class="modal" style="max-width: 800px;">
<div class="modal-header"><h3 class="modal-title" id="scheduleLogsTitle">执行日志</h3></div>
<div class="modal-body">
<div id="scheduleLogsList" style="max-height: 500px; overflow-y: auto;"></div>
<div id="emptyScheduleLogs" class="empty-state" style="display: none;">
<div class="empty-state-icon">📝</div>
<p>暂无执行记录</p>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-text" onclick="closeModal('scheduleLogsModal')">关闭</button>
</div>
</div>
</div>
<script>'''
insert_marker = ' <script>'
if insert_marker in html_content:
html_content = html_content.replace(insert_marker, modal_html, 1)
print("✓ 已添加日志弹窗HTML")
else:
print("⚠ 未找到弹窗插入位置")
# 添加JavaScript函数
js_code = '''
// ==================== 定时任务执行日志 ====================
function viewScheduleLogs(scheduleId) {
const schedule = schedules.find(s => s.id === scheduleId);
if (!schedule) {
showToast('定时任务不存在', 'error');
return;
}
document.getElementById('scheduleLogsTitle').textContent = schedule.name + ' - 执行日志';
loadScheduleLogs(scheduleId);
openModal('scheduleLogsModal');
}
function loadScheduleLogs(scheduleId) {
fetch('/api/schedules/' + scheduleId + '/logs?limit=20')
.then(r => r.json())
.then(logs => {
const container = document.getElementById('scheduleLogsList');
const empty = document.getElementById('emptyScheduleLogs');
if (!logs || logs.length === 0) {
container.innerHTML = '';
empty.style.display = 'block';
return;
}
empty.style.display = 'none';
let html = '<div style="display: grid; gap: 12px;">';
logs.forEach(log => {
const statusColor = log.status === 'completed' ? '#4CAF50' :
log.status === 'failed' ? '#F44336' : '#FF9800';
const statusText = log.status === 'completed' ? '已完成' :
log.status === 'failed' ? '失败' : '运行中';
html += '<div style="background: #f5f5f5; border-radius: 8px; padding: 16px; border-left: 4px solid ' + statusColor + ';">';
html += '<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">';
html += '<div style="font-weight: 500; font-size: 14px;">' + log.execute_time + '</div>';
html += '<div style="color: ' + statusColor + '; font-weight: 600; font-size: 13px;">' + statusText + '</div>';
html += '</div>';
html += '<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; font-size: 13px;">';
html += '<div><span style="color: #666;">账号数:</span> <strong>' + (log.total_accounts || 0) + '</strong></div>';
html += '<div><span style="color: #666;">成功:</span> <strong style="color: #4CAF50;">' + (log.success_accounts || 0) + '</strong></div>';
html += '<div><span style="color: #666;">失败:</span> <strong style="color: #F44336;">' + (log.failed_accounts || 0) + '</strong></div>';
html += '<div><span style="color: #666;">浏览内容:</span> <strong>' + (log.total_items || 0) + '</strong></div>';
html += '<div><span style="color: #666;">查看附件:</span> <strong>' + (log.total_attachments || 0) + '</strong></div>';
html += '<div><span style="color: #666;">截图数:</span> <strong>' + (log.total_screenshots || 0) + '</strong></div>';
if (log.duration_seconds) {
const mins = Math.floor(log.duration_seconds / 60);
const secs = log.duration_seconds % 60;
html += '<div><span style="color: #666;">耗时:</span> <strong>' + mins + '' + secs + '秒</strong></div>';
}
html += '</div>';
if (log.error_message) {
html += '<div style="margin-top: 8px; padding: 8px; background: #FFEBEE; border-radius: 4px; color: #C62828; font-size: 12px;">';
html += '<strong>错误:</strong> ' + escapeHtml(log.error_message);
html += '</div>';
}
html += '</div>';
});
html += '</div>';
container.innerHTML = html;
})
.catch(err => {
showToast('加载日志失败', 'error');
console.error(err);
});
}
'''
# 在logout函数之前插入
insert_marker2 = ' function logout() {'
if insert_marker2 in html_content:
html_content = html_content.replace(insert_marker2, js_code + insert_marker2)
print("✓ 已添加日志查看JavaScript函数")
else:
print("⚠ 未找到JavaScript插入位置")
return html_content
def main():
import sys
print("=" * 60)
print("定时任务修复和日志功能添加脚本")
print("=" * 60)
print()
# 读取database.py
print("[1/3] 修改 database.py...")
with open('database.py', 'r', encoding='utf-8') as f:
database_content = f.read()
database_content = add_schedule_logs_table(database_content)
with open('database.py', 'w', encoding='utf-8') as f:
f.write(database_content)
print()
# 读取app.py
print("[2/3] 修改 app.py...")
with open('app.py', 'r', encoding='utf-8') as f:
app_content = f.read()
app_content = add_schedule_log_tracking(app_content)
with open('app.py', 'w', encoding='utf-8') as f:
f.write(app_content)
print()
# 读取templates/index.html
print("[3/3] 修改 templates/index.html...")
with open('templates/index.html', 'r', encoding='utf-8') as f:
html_content = f.read()
html_content = add_frontend_log_button(html_content)
with open('templates/index.html', 'w', encoding='utf-8') as f:
f.write(html_content)
print()
print("=" * 60)
print("✅ 所有修改完成!")
print("=" * 60)
print()
print("下一步操作:")
print("1. 重启Docker容器: docker-compose restart")
print("2. 检查定时任务是否启用: enabled = 1")
print("3. 测试定时任务执行")
print()
if __name__ == '__main__':
main()

View File

@@ -1,166 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import re
NEW_FUNC = '''def take_screenshot_for_account(user_id, account_id, browse_type="应读", source="manual", task_start_time=None, browse_result=None):
"""为账号任务完成后截图(使用独立进程避免eventlet冲突)"""
import subprocess
import json
import uuid
if user_id not in user_accounts or account_id not in user_accounts[user_id]:
return
account = user_accounts[user_id][account_id]
# 使用截图信号量控制并发
semaphore, max_concurrent = get_screenshot_semaphore()
screenshot_acquired = semaphore.acquire(blocking=True, timeout=300)
if not screenshot_acquired:
log_to_client(f"截图资源获取超时,跳过截图", user_id, account_id)
if account_id in task_status:
del task_status[account_id]
return
max_retries = 2
screenshot_success = False
for attempt in range(1, max_retries + 1):
try:
if account_id in task_status:
task_status[account_id]["detail_status"] = f"正在截图{f' (第{attempt}次)' if attempt > 1 else ''}"
if attempt > 1:
log_to_client(f"🔄 第 {attempt} 次截图尝试...", user_id, account_id)
beijing_tz = pytz.timezone('Asia/Shanghai')
now_beijing = datetime.now(beijing_tz)
timestamp = now_beijing.strftime('%Y%m%d_%H%M%S')
user_info = database.get_user_by_id(user_id)
username_prefix = user_info['username'] if user_info else f"user{user_id}"
login_account = account.username
actual_browse_type = account.last_browse_type or browse_type
screenshot_filename = f"{username_prefix}_{login_account}_{actual_browse_type}_{timestamp}.jpg"
screenshot_path = os.path.join(SCREENSHOTS_DIR, screenshot_filename)
config = {
'username': account.username,
'password': account.password,
'remember': account.remember if hasattr(account, 'remember') else '',
'browse_type': actual_browse_type,
'screenshot_path': screenshot_path,
'proxy_config': account.proxy_config if hasattr(account, 'proxy_config') else None
}
config_file = f"/tmp/screenshot_config_{uuid.uuid4().hex}.json"
with open(config_file, 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False)
log_to_client(f"启动截图进程...", user_id, account_id)
try:
result = subprocess.run(
['python3', '/www/wwwroot/zsglpt/screenshot_worker.py', config_file],
capture_output=True,
text=True,
timeout=120
)
output = result.stdout
if "===RESULT===" in output:
result_json = output.split("===RESULT===")[1].strip()
result_data = json.loads(result_json)
if result_data.get('success'):
log_to_client(f"✓ 截图成功: {screenshot_filename}", user_id, account_id)
screenshot_success = True
break
else:
log_to_client(f"截图失败: {result_data.get('message', '未知错误')}", user_id, account_id)
else:
err_msg = result.stderr[:200] if result.stderr else '无输出'
log_to_client(f"截图进程异常: {err_msg}", user_id, account_id)
except subprocess.TimeoutExpired:
log_to_client(f"截图超时", user_id, account_id)
except Exception as e:
log_to_client(f"截图进程错误: {str(e)}", user_id, account_id)
try:
if os.path.exists(config_file):
os.remove(config_file)
except:
pass
if attempt < max_retries:
log_to_client(f"将重试...", user_id, account_id)
time.sleep(2)
except Exception as e:
log_to_client(f"截图出错: {str(e)}", user_id, account_id)
if attempt < max_retries:
log_to_client(f"将重试...", user_id, account_id)
time.sleep(2)
if not screenshot_success:
log_to_client(f"❌ 截图失败: 已重试{max_retries}", user_id, account_id)
account.status = "未开始"
socketio.emit('account_update', account.to_dict(), room=f'user_{user_id}')
semaphore.release()
if account_id in task_status:
del task_status[account_id]
if task_start_time and browse_result:
import time as time_module
total_duration = int(time_module.time() - task_start_time)
database.create_task_log(
user_id=user_id,
account_id=account_id,
username=account.username if account else "",
browse_type=actual_browse_type,
status='success',
total_items=browse_result.get('total_items', 0),
total_attachments=browse_result.get('total_attachments', 0),
error_message='' if screenshot_success else '截图失败',
duration=total_duration,
source=source
)
log_to_client(f"任务完成!总耗时 {total_duration}", user_id, account_id)
'''
def main():
with open('/www/wwwroot/zsglpt/app.py', 'r', encoding='utf-8') as f:
content = f.read()
lines = content.split('\n')
start_idx = None
end_idx = None
for i, line in enumerate(lines):
if line.startswith('def take_screenshot_for_account('):
start_idx = i
elif start_idx is not None and line.startswith('def ') and i > start_idx:
end_idx = i
break
if start_idx is None:
print("未找到函数")
return
if end_idx is None:
end_idx = len(lines)
new_lines = lines[:start_idx] + NEW_FUNC.split('\n') + [''] + lines[end_idx:]
with open('/www/wwwroot/zsglpt/app.py', 'w', encoding='utf-8') as f:
f.write('\n'.join(new_lines))
print(f"已替换函数 (行 {start_idx+1}{end_idx})")
if __name__ == '__main__':
main()

View File

@@ -1,313 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""统计页面UI精简 - 使用可视化图标,合并信息"""
with open('/www/wwwroot/zsglpt/templates/admin.html', 'r', encoding='utf-8') as f:
content = f.read()
# 找到统计标签页的内容,替换为精简版
old_stats_start = '''<div id="tab-stats" class="tab-content">
<h3 style="margin-bottom: 15px; font-size: 16px;">服务器信息</h3>
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin-bottom: 25px;">
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 24px; font-weight: bold; color: #f5576c;" id="serverCpu">-</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">CPU使用率</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 24px; font-weight: bold; color: #f093fb;" id="serverMemory">-</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">内存使用</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 24px; font-weight: bold; color: #764ba2;" id="serverDisk">-</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">磁盘使用</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 24px; font-weight: bold; color: #17a2b8;" id="serverUptime">-</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">运行时长</div>
</div>
</div>
<h3 style="margin: 25px 0 15px 0; font-size: 16px;">Docker容器状态</h3>
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin-bottom: 25px;">
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 20px; font-weight: bold; color: #28a745;" id="dockerContainerName">-</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">容器名称</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 20px; font-weight: bold; color: #17a2b8;" id="dockerUptime">-</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">容器运行时间</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 20px; font-weight: bold; color: #f093fb;" id="dockerMemory">-</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">容器内存使用</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 20px; font-weight: bold;" id="dockerStatus" style="color: #28a745;">-</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">运行状态</div>
</div>
</div>
<!-- 实时任务监控 -->
<h3 style="margin: 25px 0 15px 0; font-size: 16px;">实时任务监控 <span id="taskMonitorStatus" style="font-size: 12px; color: #28a745; font-weight: normal;">● 实时更新中</span></h3>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-bottom: 15px;">
<div style="background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); text-align: center;">
<div style="font-size: 28px; font-weight: bold; color: #28a745;" id="runningTaskCount">0</div>
<div style="font-size: 13px; color: #666; margin-top: 5px;">运行中</div>
</div>
<div style="background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); text-align: center;">
<div style="font-size: 28px; font-weight: bold; color: #fd7e14;" id="queuingTaskCount">0</div>
<div style="font-size: 13px; color: #666; margin-top: 5px;">排队中</div>
</div>
<div style="background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); text-align: center;">
<div style="font-size: 28px; font-weight: bold; color: #6c757d;" id="maxConcurrentDisplay">-</div>
<div style="font-size: 13px; color: #666; margin-top: 5px;">最大并发</div>
</div>
</div>
<!-- 运行中的任务列表 -->
<div style="background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); margin-bottom: 15px;">
<div style="font-size: 14px; font-weight: bold; color: #28a745; margin-bottom: 10px;">🚀 运行中的任务</div>
<div id="runningTasksList" style="font-size: 13px;">
<div style="color: #999; text-align: center; padding: 10px;">暂无运行中的任务</div>
</div>
</div>
<!-- 排队中的任务列表 -->
<div style="background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); margin-bottom: 25px;">
<div style="font-size: 14px; font-weight: bold; color: #fd7e14; margin-bottom: 10px;">⏳ 排队中的任务</div>
<div id="queuingTasksList" style="font-size: 13px;">
<div style="color: #999; text-align: center; padding: 10px;">暂无排队中的任务</div>
</div>
</div>
<h3 style="margin: 25px 0 15px 0; font-size: 16px;">当日任务统计</h3>
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin-bottom: 25px;">
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 32px; font-weight: bold; color: #28a745;" id="todaySuccessTasks">0</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">成功任务</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 32px; font-weight: bold; color: #dc3545;" id="todayFailedTasks">0</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">失败任务</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 32px; font-weight: bold; color: #007bff;" id="todayTotalItems">0</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">浏览内容数</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 32px; font-weight: bold; color: #17a2b8;" id="todayTotalAttachments">0</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">查看附件数</div>
</div>
</div>
<h3 style="margin: 25px 0 15px 0; font-size: 16px;">历史累计统计</h3>
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px;">
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 32px; font-weight: bold; color: #28a745;" id="totalSuccessTasks">0</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">累计成功任务</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 32px; font-weight: bold; color: #dc3545;" id="totalFailedTasks">0</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">累计失败任务</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 32px; font-weight: bold; color: #007bff;" id="totalTotalItems">0</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">累计浏览内容</div>
</div>
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<div style="font-size: 32px; font-weight: bold; color: #17a2b8;" id="totalTotalAttachments">0</div>
<div style="font-size: 14px; color: #666; margin-top: 5px;">累计查看附件</div>
</div>
</div>
</div>'''
new_stats = '''<div id="tab-stats" class="tab-content">
<!-- 系统状态概览 - 精简合并版 -->
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; padding: 20px; margin-bottom: 20px; color: white;">
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 15px;">
<div style="display: flex; align-items: center; gap: 10px;">
<span style="font-size: 24px;">💻</span>
<div>
<div style="font-size: 20px; font-weight: bold;" id="serverCpu">-</div>
<div style="font-size: 12px; opacity: 0.8;">CPU</div>
</div>
</div>
<div style="display: flex; align-items: center; gap: 10px;">
<span style="font-size: 24px;">🧠</span>
<div>
<div style="font-size: 20px; font-weight: bold;" id="serverMemory">-</div>
<div style="font-size: 12px; opacity: 0.8;">内存</div>
</div>
</div>
<div style="display: flex; align-items: center; gap: 10px;">
<span style="font-size: 24px;">💾</span>
<div>
<div style="font-size: 20px; font-weight: bold;" id="serverDisk">-</div>
<div style="font-size: 12px; opacity: 0.8;">磁盘</div>
</div>
</div>
<div style="display: flex; align-items: center; gap: 10px;">
<span style="font-size: 24px;">🐳</span>
<div>
<div style="font-size: 20px; font-weight: bold;" id="dockerMemory">-</div>
<div style="font-size: 12px; opacity: 0.8;">容器</div>
</div>
</div>
<div style="display: flex; align-items: center; gap: 10px;">
<span style="font-size: 24px;">⏱️</span>
<div>
<div style="font-size: 20px; font-weight: bold;" id="serverUptime">-</div>
<div style="font-size: 12px; opacity: 0.8;">运行</div>
</div>
</div>
</div>
</div>
<!-- 隐藏的元素保持JS兼容性 -->
<div style="display:none;">
<span id="dockerContainerName"></span>
<span id="dockerUptime"></span>
<span id="dockerStatus"></span>
</div>
<!-- 实时任务监控 -->
<div style="background: white; border-radius: 12px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 12px rgba(0,0,0,0.08);">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
<h3 style="margin: 0; font-size: 16px; display: flex; align-items: center; gap: 8px;">
<span>📊</span> 实时监控
</h3>
<span id="taskMonitorStatus" style="font-size: 12px; color: #28a745;">● 实时更新</span>
</div>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-bottom: 15px;">
<div style="background: linear-gradient(135deg, #28a745 0%, #20c997 100%); padding: 15px; border-radius: 10px; text-align: center; color: white;">
<div style="font-size: 32px; font-weight: bold;" id="runningTaskCount">0</div>
<div style="font-size: 12px; opacity: 0.9; margin-top: 3px;">🚀 运行中</div>
</div>
<div style="background: linear-gradient(135deg, #fd7e14 0%, #ffc107 100%); padding: 15px; border-radius: 10px; text-align: center; color: white;">
<div style="font-size: 32px; font-weight: bold;" id="queuingTaskCount">0</div>
<div style="font-size: 12px; opacity: 0.9; margin-top: 3px;">⏳ 排队中</div>
</div>
<div style="background: linear-gradient(135deg, #6c757d 0%, #495057 100%); padding: 15px; border-radius: 10px; text-align: center; color: white;">
<div style="font-size: 32px; font-weight: bold;" id="maxConcurrentDisplay">-</div>
<div style="font-size: 12px; opacity: 0.9; margin-top: 3px;">⚡ 最大并发</div>
</div>
</div>
<!-- 任务列表(折叠式) -->
<details style="margin-top: 10px;">
<summary style="cursor: pointer; font-size: 13px; color: #666; padding: 8px 0;">展开查看任务详情</summary>
<div style="margin-top: 10px; padding: 10px; background: #f8f9fa; border-radius: 8px;">
<div style="font-size: 13px; font-weight: bold; color: #28a745; margin-bottom: 8px;">运行中</div>
<div id="runningTasksList" style="font-size: 12px; margin-bottom: 10px;">
<div style="color: #999;">暂无</div>
</div>
<div style="font-size: 13px; font-weight: bold; color: #fd7e14; margin-bottom: 8px;">排队中</div>
<div id="queuingTasksList" style="font-size: 12px;">
<div style="color: #999;">暂无</div>
</div>
</div>
</details>
</div>
<!-- 任务统计 - 合并当日和累计 -->
<div style="background: white; border-radius: 12px; padding: 20px; box-shadow: 0 2px 12px rgba(0,0,0,0.08);">
<h3 style="margin: 0 0 15px 0; font-size: 16px; display: flex; align-items: center; gap: 8px;">
<span>📈</span> 任务统计
</h3>
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
<!-- 成功任务 -->
<div style="background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%); padding: 15px; border-radius: 10px;">
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
<span style="font-size: 20px;">✅</span>
<span style="font-size: 13px; color: #155724; font-weight: bold;">成功任务</span>
</div>
<div style="display: flex; justify-content: space-between; align-items: baseline;">
<div>
<span style="font-size: 28px; font-weight: bold; color: #155724;" id="todaySuccessTasks">0</span>
<span style="font-size: 12px; color: #155724;">今日</span>
</div>
<div style="text-align: right;">
<span style="font-size: 16px; color: #28a745;" id="totalSuccessTasks">0</span>
<span style="font-size: 11px; color: #666;">累计</span>
</div>
</div>
</div>
<!-- 失败任务 -->
<div style="background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%); padding: 15px; border-radius: 10px;">
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
<span style="font-size: 20px;">❌</span>
<span style="font-size: 13px; color: #721c24; font-weight: bold;">失败任务</span>
</div>
<div style="display: flex; justify-content: space-between; align-items: baseline;">
<div>
<span style="font-size: 28px; font-weight: bold; color: #721c24;" id="todayFailedTasks">0</span>
<span style="font-size: 12px; color: #721c24;">今日</span>
</div>
<div style="text-align: right;">
<span style="font-size: 16px; color: #dc3545;" id="totalFailedTasks">0</span>
<span style="font-size: 11px; color: #666;">累计</span>
</div>
</div>
</div>
<!-- 浏览内容 -->
<div style="background: linear-gradient(135deg, #cce5ff 0%, #b8daff 100%); padding: 15px; border-radius: 10px;">
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
<span style="font-size: 20px;">📄</span>
<span style="font-size: 13px; color: #004085; font-weight: bold;">浏览内容</span>
</div>
<div style="display: flex; justify-content: space-between; align-items: baseline;">
<div>
<span style="font-size: 28px; font-weight: bold; color: #004085;" id="todayTotalItems">0</span>
<span style="font-size: 12px; color: #004085;">今日</span>
</div>
<div style="text-align: right;">
<span style="font-size: 16px; color: #007bff;" id="totalTotalItems">0</span>
<span style="font-size: 11px; color: #666;">累计</span>
</div>
</div>
</div>
<!-- 查看附件 -->
<div style="background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%); padding: 15px; border-radius: 10px;">
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
<span style="font-size: 20px;">📎</span>
<span style="font-size: 13px; color: #0c5460; font-weight: bold;">查看附件</span>
</div>
<div style="display: flex; justify-content: space-between; align-items: baseline;">
<div>
<span style="font-size: 28px; font-weight: bold; color: #0c5460;" id="todayTotalAttachments">0</span>
<span style="font-size: 12px; color: #0c5460;">今日</span>
</div>
<div style="text-align: right;">
<span style="font-size: 16px; color: #17a2b8;" id="totalTotalAttachments">0</span>
<span style="font-size: 11px; color: #666;">累计</span>
</div>
</div>
</div>
</div>
</div>
</div>'''
if old_stats_start in content:
content = content.replace(old_stats_start, new_stats)
print("OK - 统计页面已更新为精简可视化版本")
else:
print("WARNING - 未找到原始统计页面内容")
# 尝试找到部分匹配
if '<div id="tab-stats" class="tab-content">' in content:
print("找到tab-stats开始标签但完整内容不匹配")
with open('/www/wwwroot/zsglpt/templates/admin.html', 'w', encoding='utf-8') as f:
f.write(content)
print("完成")

View File

@@ -1,204 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""统计页面UI精简 - 使用正则替换"""
import re
with open('/www/wwwroot/zsglpt/templates/admin.html', 'r', encoding='utf-8') as f:
content = f.read()
# 使用正则找到统计标签页的内容
# 从 <div id="tab-stats" 到下一个 <div id="tab- 或 </div>\s*<!-- 任务日志
pattern = r'(<div id="tab-stats" class="tab-content">).*?(</div>\s*\n\s*<!-- 任务日志 -->)'
new_stats = '''<div id="tab-stats" class="tab-content">
<!-- 系统状态概览 - 精简合并版 -->
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; padding: 20px; margin-bottom: 20px; color: white;">
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 15px;">
<div style="display: flex; align-items: center; gap: 10px;">
<span style="font-size: 24px;">💻</span>
<div>
<div style="font-size: 20px; font-weight: bold;" id="serverCpu">-</div>
<div style="font-size: 12px; opacity: 0.8;">CPU</div>
</div>
</div>
<div style="display: flex; align-items: center; gap: 10px;">
<span style="font-size: 24px;">🧠</span>
<div>
<div style="font-size: 20px; font-weight: bold;" id="serverMemory">-</div>
<div style="font-size: 12px; opacity: 0.8;">内存</div>
</div>
</div>
<div style="display: flex; align-items: center; gap: 10px;">
<span style="font-size: 24px;">💾</span>
<div>
<div style="font-size: 20px; font-weight: bold;" id="serverDisk">-</div>
<div style="font-size: 12px; opacity: 0.8;">磁盘</div>
</div>
</div>
<div style="display: flex; align-items: center; gap: 10px;">
<span style="font-size: 24px;">🐳</span>
<div>
<div style="font-size: 20px; font-weight: bold;" id="dockerMemory">-</div>
<div style="font-size: 12px; opacity: 0.8;">容器</div>
</div>
</div>
<div style="display: flex; align-items: center; gap: 10px;">
<span style="font-size: 24px;">⏱️</span>
<div>
<div style="font-size: 20px; font-weight: bold;" id="serverUptime">-</div>
<div style="font-size: 12px; opacity: 0.8;">运行</div>
</div>
</div>
</div>
</div>
<!-- 隐藏的元素保持JS兼容性 -->
<div style="display:none;">
<span id="dockerContainerName"></span>
<span id="dockerUptime"></span>
<span id="dockerStatus"></span>
</div>
<!-- 实时任务监控 -->
<div style="background: white; border-radius: 12px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 12px rgba(0,0,0,0.08);">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
<h3 style="margin: 0; font-size: 16px; display: flex; align-items: center; gap: 8px;">
<span>📊</span> 实时监控
</h3>
<span id="taskMonitorStatus" style="font-size: 12px; color: #28a745;">● 实时更新</span>
</div>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-bottom: 15px;">
<div style="background: linear-gradient(135deg, #28a745 0%, #20c997 100%); padding: 15px; border-radius: 10px; text-align: center; color: white;">
<div style="font-size: 32px; font-weight: bold;" id="runningTaskCount">0</div>
<div style="font-size: 12px; opacity: 0.9; margin-top: 3px;">🚀 运行中</div>
</div>
<div style="background: linear-gradient(135deg, #fd7e14 0%, #ffc107 100%); padding: 15px; border-radius: 10px; text-align: center; color: white;">
<div style="font-size: 32px; font-weight: bold;" id="queuingTaskCount">0</div>
<div style="font-size: 12px; opacity: 0.9; margin-top: 3px;">⏳ 排队中</div>
</div>
<div style="background: linear-gradient(135deg, #6c757d 0%, #495057 100%); padding: 15px; border-radius: 10px; text-align: center; color: white;">
<div style="font-size: 32px; font-weight: bold;" id="maxConcurrentDisplay">-</div>
<div style="font-size: 12px; opacity: 0.9; margin-top: 3px;">⚡ 最大并发</div>
</div>
</div>
<!-- 任务列表(折叠式) -->
<details style="margin-top: 10px;">
<summary style="cursor: pointer; font-size: 13px; color: #666; padding: 8px 0;">展开查看任务详情</summary>
<div style="margin-top: 10px; padding: 10px; background: #f8f9fa; border-radius: 8px;">
<div style="font-size: 13px; font-weight: bold; color: #28a745; margin-bottom: 8px;">运行中</div>
<div id="runningTasksList" style="font-size: 12px; margin-bottom: 10px;">
<div style="color: #999;">暂无</div>
</div>
<div style="font-size: 13px; font-weight: bold; color: #fd7e14; margin-bottom: 8px;">排队中</div>
<div id="queuingTasksList" style="font-size: 12px;">
<div style="color: #999;">暂无</div>
</div>
</div>
</details>
</div>
<!-- 任务统计 - 合并当日和累计 -->
<div style="background: white; border-radius: 12px; padding: 20px; box-shadow: 0 2px 12px rgba(0,0,0,0.08);">
<h3 style="margin: 0 0 15px 0; font-size: 16px; display: flex; align-items: center; gap: 8px;">
<span>📈</span> 任务统计
</h3>
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
<!-- 成功任务 -->
<div style="background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%); padding: 15px; border-radius: 10px;">
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
<span style="font-size: 20px;">✅</span>
<span style="font-size: 13px; color: #155724; font-weight: bold;">成功任务</span>
</div>
<div style="display: flex; justify-content: space-between; align-items: baseline;">
<div>
<span style="font-size: 28px; font-weight: bold; color: #155724;" id="todaySuccessTasks">0</span>
<span style="font-size: 12px; color: #155724;">今日</span>
</div>
<div style="text-align: right;">
<span style="font-size: 16px; color: #28a745;" id="totalSuccessTasks">0</span>
<span style="font-size: 11px; color: #666;">累计</span>
</div>
</div>
</div>
<!-- 失败任务 -->
<div style="background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%); padding: 15px; border-radius: 10px;">
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
<span style="font-size: 20px;">❌</span>
<span style="font-size: 13px; color: #721c24; font-weight: bold;">失败任务</span>
</div>
<div style="display: flex; justify-content: space-between; align-items: baseline;">
<div>
<span style="font-size: 28px; font-weight: bold; color: #721c24;" id="todayFailedTasks">0</span>
<span style="font-size: 12px; color: #721c24;">今日</span>
</div>
<div style="text-align: right;">
<span style="font-size: 16px; color: #dc3545;" id="totalFailedTasks">0</span>
<span style="font-size: 11px; color: #666;">累计</span>
</div>
</div>
</div>
<!-- 浏览内容 -->
<div style="background: linear-gradient(135deg, #cce5ff 0%, #b8daff 100%); padding: 15px; border-radius: 10px;">
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
<span style="font-size: 20px;">📄</span>
<span style="font-size: 13px; color: #004085; font-weight: bold;">浏览内容</span>
</div>
<div style="display: flex; justify-content: space-between; align-items: baseline;">
<div>
<span style="font-size: 28px; font-weight: bold; color: #004085;" id="todayTotalItems">0</span>
<span style="font-size: 12px; color: #004085;">今日</span>
</div>
<div style="text-align: right;">
<span style="font-size: 16px; color: #007bff;" id="totalTotalItems">0</span>
<span style="font-size: 11px; color: #666;">累计</span>
</div>
</div>
</div>
<!-- 查看附件 -->
<div style="background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%); padding: 15px; border-radius: 10px;">
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
<span style="font-size: 20px;">📎</span>
<span style="font-size: 13px; color: #0c5460; font-weight: bold;">查看附件</span>
</div>
<div style="display: flex; justify-content: space-between; align-items: baseline;">
<div>
<span style="font-size: 28px; font-weight: bold; color: #0c5460;" id="todayTotalAttachments">0</span>
<span style="font-size: 12px; color: #0c5460;">今日</span>
</div>
<div style="text-align: right;">
<span style="font-size: 16px; color: #17a2b8;" id="totalTotalAttachments">0</span>
<span style="font-size: 11px; color: #666;">累计</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 任务日志 -->'''
# 使用正则替换
new_content, count = re.subn(pattern, new_stats, content, flags=re.DOTALL)
if count > 0:
print(f"OK - 成功替换 {count}")
with open('/www/wwwroot/zsglpt/templates/admin.html', 'w', encoding='utf-8') as f:
f.write(new_content)
else:
print("WARNING - 未找到匹配内容")
# 检查文件中是否有tab-stats
if 'id="tab-stats"' in content:
print("文件中存在 tab-stats")
# 找到位置
import re
match = re.search(r'<div id="tab-stats"[^>]*>', content)
if match:
print(f"位置: {match.start()}")
print(f"周围内容: {content[match.start():match.start()+200]}")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff