修复所有bug并添加新功能
- 修复添加账号按钮无反应问题
- 添加账号备注字段(可选)
- 添加账号设置按钮(修改密码/备注)
- 修复用户反馈���能
- 添加定时任务执行日志
- 修复容器重启后账号加载问题
- 修复所有JavaScript语法错误
- 优化账号加载机制(4层保障)
🤖 Generated with Claude Code
This commit is contained in:
403
fix_schedule.py
Executable file
403
fix_schedule.py
Executable file
@@ -0,0 +1,403 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user