添加定时任务日志管理功能

- 添加用户清空日志按钮
- 添加30天自动清理定时任务执行日志
- 简化日志API代码,移除调试日志

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-10 20:20:44 +08:00
parent 9df2122fb5
commit 7954aeaf59
3 changed files with 71 additions and 43 deletions

69
app.py
View File

@@ -2816,9 +2816,9 @@ def scheduled_task_worker():
print(f"[定时清理] 清理验证码出错: {str(e)}")
def cleanup_old_data():
"""清理7天前截图和日志"""
"""清理旧数据:7天前截图和任务日志30天前操作日志和定时任务执行日志"""
try:
print(f"[定时清理] 开始清理7天前的数据...")
print(f"[定时清理] 开始清理数据...")
# 清理7天前的任务日志
deleted_logs = database.delete_old_task_logs(7)
@@ -2827,6 +2827,10 @@ def scheduled_task_worker():
# 清理30天前的操作日志
deleted_operation_logs = database.clean_old_operation_logs(30)
print(f"[定时清理] 已删除 {deleted_operation_logs} 条操作日志")
# 清理30天前的定时任务执行日志
deleted_schedule_logs = database.clean_old_schedule_logs(30)
print(f"[定时清理] 已删除 {deleted_schedule_logs} 条定时任务执行日志")
# 清理7天前的截图
deleted_screenshots = 0
if os.path.exists(SCREENSHOTS_DIR):
@@ -3278,53 +3282,32 @@ def run_schedule_now_api(schedule_id):
def get_schedule_logs_api(schedule_id):
"""获取定时任务执行日志"""
try:
print(f"[API日志] 开始处理日志请求 - schedule_id={schedule_id}, user_id={current_user.id}")
# 先验证定时任务是否存在和权限
try:
schedule = database.get_schedule_by_id(schedule_id)
print(f"[API日志] 查询定时任务结果: {schedule}")
except Exception as e:
print(f"[API日志] 查询定时任务失败: {e}")
# 即使查询失败也返回空数组避免500错误
return jsonify([])
if not schedule:
print(f"[API日志] 定时任务#{schedule_id}不存在,返回空数组")
return jsonify([])
try:
if schedule['user_id'] != current_user.id:
print(f"[API日志] 权限不足,返回空数组")
return jsonify([])
except Exception as e:
print(f"[API日志] 权限检查失败: {e},返回空数组")
return jsonify([])
# 查询日志
try:
limit = request.args.get('limit', 10, type=int)
print(f"[API日志] 开始查询日志limit={limit}")
logs = database.get_schedule_execution_logs(schedule_id, limit)
print(f"[API日志] 成功查询到{len(logs)}条日志")
return jsonify(logs if logs else [])
except Exception as e:
print(f"[API日志] 查询日志失败: {e}")
import traceback
traceback.print_exc()
# 查询失败也返回空数组
schedule = database.get_schedule_by_id(schedule_id)
if not schedule or schedule['user_id'] != current_user.id:
return jsonify([])
limit = request.args.get('limit', 20, type=int)
logs = database.get_schedule_execution_logs(schedule_id, limit)
return jsonify(logs if logs else [])
except Exception as e:
print(f"[API日志] 外层异常捕获: {str(e)}")
import traceback
traceback.print_exc()
# 任何错误都返回空数组不返回500
return jsonify([])
@app.route('/api/schedules/<int:schedule_id>/logs', methods=['DELETE'])
@login_required
def delete_schedule_logs_api(schedule_id):
"""清空定时任务执行日志"""
try:
schedule = database.get_schedule_by_id(schedule_id)
if not schedule or schedule['user_id'] != current_user.id:
return jsonify({"error": "无权限"}), 403
deleted = database.delete_schedule_logs(schedule_id, current_user.id)
return jsonify({"success": True, "deleted": deleted})
except Exception as e:
return jsonify({"error": str(e)}), 500
# ==================== 批量操作API ====================
@app.route('/api/accounts/batch/start', methods=['POST'])

View File

@@ -1706,3 +1706,27 @@ def get_user_all_schedule_logs(user_id, limit=50):
LIMIT ?
''', (user_id, limit))
return [dict(row) for row in cursor.fetchall()]
def delete_schedule_logs(schedule_id, user_id):
"""删除指定定时任务的所有执行日志(需验证用户权限)"""
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute('''
DELETE FROM schedule_execution_logs
WHERE schedule_id = ? AND user_id = ?
''', (schedule_id, user_id))
conn.commit()
return cursor.rowcount
def clean_old_schedule_logs(days=30):
"""清理指定天数前的定时任务执行日志"""
with db_pool.get_db() as conn:
cursor = conn.cursor()
cursor.execute('''
DELETE FROM schedule_execution_logs
WHERE execute_time < datetime('now', '-' || ? || ' days')
''', (days,))
conn.commit()
return cursor.rowcount

View File

@@ -493,6 +493,7 @@
<div id="scheduleLogsList" style="max-height: 400px; overflow-y: auto;"></div>
</div>
<div class="modal-footer">
<button class="btn btn-text" style="color: var(--md-error);" onclick="clearScheduleLogs()">清空日志</button>
<button class="btn btn-primary" onclick="closeModal('scheduleLogsModal')">关闭</button>
</div>
</div>
@@ -1312,6 +1313,8 @@
if (schedule) openScheduleModal(schedule);
}
let currentLogsScheduleId = null;
function viewScheduleLogs(scheduleId) {
const numericId = parseInt(scheduleId, 10);
const schedule = schedules.find(s => s.id == numericId);
@@ -1320,6 +1323,7 @@
return;
}
currentLogsScheduleId = numericId;
document.getElementById("scheduleLogsTitle").textContent = "【" + (schedule.name || "未命名任务") + "】 执行日志";
fetch("/api/schedules/" + numericId + "/logs?limit=20")
@@ -1362,6 +1366,23 @@
});
}
function clearScheduleLogs() {
if (!currentLogsScheduleId) return;
if (!confirm('确定要清空该任务的所有执行日志吗?')) return;
fetch("/api/schedules/" + currentLogsScheduleId + "/logs", { method: 'DELETE' })
.then(r => r.json())
.then(data => {
if (data.success) {
showToast('已清空 ' + data.deleted + ' 条日志', 'success');
document.getElementById("scheduleLogsList").innerHTML = "<div class=\"empty-state\"><p>暂无执行日志</p></div>";
} else {
showToast(data.error || '清空失败', 'error');
}
})
.catch(err => showToast('清空失败', 'error'));
}
function formatDuration(seconds) {
if (seconds < 60) return seconds + "秒";
const minutes = Math.floor(seconds / 60);