新增 消息定时推送功能
This commit is contained in:
220
admin/dashboard/blueprints/message_push.py
Normal file
220
admin/dashboard/blueprints/message_push.py
Normal file
@@ -0,0 +1,220 @@
|
||||
from flask import Blueprint, render_template, jsonify, request, current_app
|
||||
from .auth import login_required
|
||||
from loguru import logger
|
||||
import json
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
# 创建消息推送管理蓝图
|
||||
message_push_bp = Blueprint('message_push', __name__, url_prefix='/message_push')
|
||||
|
||||
# 消息推送管理页面
|
||||
@message_push_bp.route('/')
|
||||
@login_required
|
||||
def message_push_management():
|
||||
"""消息推送管理页面"""
|
||||
return render_template('message_push_management.html')
|
||||
|
||||
# API路由
|
||||
@message_push_bp.route('/api/tasks', methods=['GET'])
|
||||
@login_required
|
||||
def api_tasks_list():
|
||||
"""获取任务列表API"""
|
||||
try:
|
||||
# 获取查询参数
|
||||
status = request.args.get('status')
|
||||
start_time = request.args.get('start_time')
|
||||
end_time = request.args.get('end_time')
|
||||
page = int(request.args.get('page', 1))
|
||||
limit = int(request.args.get('limit', 20))
|
||||
|
||||
# 获取任务列表
|
||||
db = current_app.dashboard_server.task_db
|
||||
tasks, total = db.get_tasks_list(status, start_time, end_time, page, limit)
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"data": {
|
||||
"tasks": tasks,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"limit": limit
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"获取任务列表失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@message_push_bp.route('/api/tasks', methods=['POST'])
|
||||
@login_required
|
||||
def api_create_task():
|
||||
"""创建任务API"""
|
||||
try:
|
||||
data = request.json
|
||||
if not data:
|
||||
return jsonify({"success": False, "error": "无效的请求数据"}), 400
|
||||
|
||||
# 生成任务ID
|
||||
data['task_id'] = str(uuid.uuid4())
|
||||
data['creator_id'] = request.user.get('id')
|
||||
|
||||
# 创建任务
|
||||
db = current_app.dashboard_server.task_db
|
||||
task = db.create_task(data)
|
||||
if not task:
|
||||
return jsonify({"success": False, "error": "创建任务失败"}), 500
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"data": {
|
||||
"task": task
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"创建任务失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@message_push_bp.route('/api/tasks/<task_id>', methods=['PUT'])
|
||||
@login_required
|
||||
def api_update_task(task_id):
|
||||
"""更新任务API"""
|
||||
try:
|
||||
data = request.json
|
||||
if not data:
|
||||
return jsonify({"success": False, "error": "无效的请求数据"}), 400
|
||||
|
||||
# 获取任务
|
||||
db = current_app.dashboard_server.task_db
|
||||
task = db.get_task(task_id)
|
||||
if not task:
|
||||
return jsonify({"success": False, "error": "任务不存在"}), 404
|
||||
|
||||
# 更新任务
|
||||
if not db.update_task(task_id, data):
|
||||
return jsonify({"success": False, "error": "更新任务失败"}), 500
|
||||
|
||||
# 获取更新后的任务
|
||||
updated_task = db.get_task(task_id)
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"data": {
|
||||
"task": updated_task
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"更新任务失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@message_push_bp.route('/api/tasks/<task_id>', methods=['DELETE'])
|
||||
@login_required
|
||||
def api_delete_task(task_id):
|
||||
"""删除任务API"""
|
||||
try:
|
||||
# 获取任务
|
||||
db = current_app.dashboard_server.task_db
|
||||
task = db.get_task(task_id)
|
||||
if not task:
|
||||
return jsonify({"success": False, "error": "任务不存在"}), 404
|
||||
|
||||
# 删除任务
|
||||
if not db.delete_task(task_id):
|
||||
return jsonify({"success": False, "error": "删除任务失败"}), 500
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "任务已删除"
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"删除任务失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@message_push_bp.route('/api/tasks/<task_id>/pause', methods=['POST'])
|
||||
@login_required
|
||||
def api_pause_task(task_id):
|
||||
"""暂停任务API"""
|
||||
try:
|
||||
# 获取任务
|
||||
db = current_app.dashboard_server.task_db
|
||||
task = db.get_task(task_id)
|
||||
if not task:
|
||||
return jsonify({"success": False, "error": "任务不存在"}), 404
|
||||
|
||||
# 暂停任务
|
||||
if not db.update_task(task_id, {'status': 'paused'}):
|
||||
return jsonify({"success": False, "error": "暂停任务失败"}), 500
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "任务已暂停"
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"暂停任务失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@message_push_bp.route('/api/tasks/<task_id>/resume', methods=['POST'])
|
||||
@login_required
|
||||
def api_resume_task(task_id):
|
||||
"""恢复任务API"""
|
||||
try:
|
||||
# 获取任务
|
||||
db = current_app.dashboard_server.task_db
|
||||
task = db.get_task(task_id)
|
||||
if not task:
|
||||
return jsonify({"success": False, "error": "任务不存在"}), 404
|
||||
|
||||
# 恢复任务
|
||||
if not db.update_task(task_id, {'status': 'scheduled'}):
|
||||
return jsonify({"success": False, "error": "恢复任务失败"}), 500
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "任务已恢复"
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"恢复任务失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@message_push_bp.route('/api/tasks/<task_id>/preview', methods=['POST'])
|
||||
@login_required
|
||||
def api_preview_task(task_id):
|
||||
"""预览任务API"""
|
||||
try:
|
||||
# 获取任务
|
||||
db = current_app.dashboard_server.task_db
|
||||
task = db.get_task(task_id)
|
||||
if not task:
|
||||
return jsonify({"success": False, "error": "任务不存在"}), 404
|
||||
|
||||
# 发送预览
|
||||
message_push = current_app.dashboard_server.message_push_task.message_push
|
||||
if not message_push.send_preview(task, [request.user.get('id')]):
|
||||
return jsonify({"success": False, "error": "发送预览失败"}), 500
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "预览已发送"
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"发送预览失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
|
||||
@message_push_bp.route('/api/tasks/<task_id>/logs', methods=['GET'])
|
||||
@login_required
|
||||
def api_task_logs(task_id):
|
||||
"""获取任务日志API"""
|
||||
try:
|
||||
# 获取查询参数
|
||||
page = int(request.args.get('page', 1))
|
||||
limit = int(request.args.get('limit', 20))
|
||||
|
||||
# 查询日志
|
||||
db = current_app.dashboard_server.task_db
|
||||
logs_data = db.get_task_logs_with_pagination(task_id, page, limit)
|
||||
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"data": logs_data
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"获取任务日志失败: {e}")
|
||||
return jsonify({"success": False, "error": str(e)}), 500
|
||||
@@ -5,14 +5,15 @@
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
|
||||
import toml
|
||||
from flask import Flask, send_from_directory
|
||||
from loguru import logger
|
||||
|
||||
from db.contacts_db import ContactsDBOperator
|
||||
from db.message_storage import MessageStorageDB
|
||||
from db.stats_db import StatsDBOperator
|
||||
from flask import Flask, send_from_directory
|
||||
import toml
|
||||
|
||||
from db.task_db import TaskDBOperator
|
||||
from wechat_ipad import WechatAPIClient
|
||||
|
||||
# 添加项目根目录到系统路径,确保可以导入项目模块
|
||||
@@ -42,6 +43,7 @@ class DashboardServer:
|
||||
self.stats_db = StatsDBOperator(self.db_manager)
|
||||
self.message_storage = MessageStorageDB(self.db_manager)
|
||||
self.contact_db: ContactsDBOperator = ContactsDBOperator(self.db_manager)
|
||||
self.task_db: TaskDBOperator = TaskDBOperator(self.db_manager)
|
||||
# 获取联系人管理器实例
|
||||
self.contact_manager = robot_instance.contact_manager
|
||||
self.plugin_manager = robot_instance.plugin_manager
|
||||
|
||||
439
admin/dashboard/templates/message_push_management.html
Normal file
439
admin/dashboard/templates/message_push_management.html
Normal file
@@ -0,0 +1,439 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}消息推送管理{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">消息推送管理</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#createTaskModal">
|
||||
<i class="fas fa-plus"></i> 新建任务
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- 搜索栏 -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">
|
||||
<select class="form-control" id="statusFilter">
|
||||
<option value="">全部状态</option>
|
||||
<option value="draft">草稿</option>
|
||||
<option value="scheduled">已排期</option>
|
||||
<option value="paused">已暂停</option>
|
||||
<option value="completed">已完成</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="date" class="form-control" id="startTimeFilter" placeholder="开始时间">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="date" class="form-control" id="endTimeFilter" placeholder="结束时间">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<button type="button" class="btn btn-info" id="searchBtn">
|
||||
<i class="fas fa-search"></i> 搜索
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" id="resetBtn">
|
||||
<i class="fas fa-redo"></i> 重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 任务列表 -->
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>任务ID</th>
|
||||
<th>任务名称</th>
|
||||
<th>状态</th>
|
||||
<th>计划时间</th>
|
||||
<th>创建时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="taskList">
|
||||
<!-- 任务列表将通过JavaScript动态加载 -->
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="dataTables_info" id="taskInfo" role="status" aria-live="polite">
|
||||
显示 0 到 0 条,共 0 条记录
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="dataTables_paginate paging_simple_numbers" id="taskPagination">
|
||||
<!-- 分页将通过JavaScript动态加载 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新建任务模态框 -->
|
||||
<div class="modal fade" id="createTaskModal" tabindex="-1" role="dialog" aria-labelledby="createTaskModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="createTaskModalLabel">新建任务</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="createTaskForm">
|
||||
<div class="form-group">
|
||||
<label for="taskName">任务名称</label>
|
||||
<input type="text" class="form-control" id="taskName" name="task_name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="scheduleTime">计划时间</label>
|
||||
<input type="datetime-local" class="form-control" id="scheduleTime" name="schedule_time" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="groups">目标群组</label>
|
||||
<select class="form-control" id="groups" name="groups" multiple required>
|
||||
<!-- 群组列表将通过JavaScript动态加载 -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="contentText">文本内容</label>
|
||||
<textarea class="form-control" id="contentText" name="content_text" rows="3"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="contentImage">图片内容</label>
|
||||
<input type="file" class="form-control-file" id="contentImage" name="content_image">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="contentLink">链接内容</label>
|
||||
<input type="url" class="form-control" id="contentLink" name="content_link">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="contentMiniprogram">小程序内容</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="miniprogramTitle" name="miniprogram_title" placeholder="标题">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<input type="text" class="form-control" id="miniprogramPath" name="miniprogram_path" placeholder="路径">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="previewRecipients">预览接收人</label>
|
||||
<select class="form-control" id="previewRecipients" name="preview_recipients" multiple>
|
||||
<!-- 用户列表将通过JavaScript动态加载 -->
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="saveTaskBtn">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 任务日志模态框 -->
|
||||
<div class="modal fade" id="taskLogsModal" tabindex="-1" role="dialog" aria-labelledby="taskLogsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="taskLogsModalLabel">任务日志</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>时间</th>
|
||||
<th>操作</th>
|
||||
<th>操作人</th>
|
||||
<th>变更内容</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="taskLogsList">
|
||||
<!-- 日志列表将通过JavaScript动态加载 -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// 全局变量
|
||||
let currentPage = 1;
|
||||
let pageSize = 20;
|
||||
let totalPages = 0;
|
||||
|
||||
// 加载任务列表
|
||||
function loadTasks(page = 1) {
|
||||
const status = $('#statusFilter').val();
|
||||
const startTime = $('#startTimeFilter').val();
|
||||
const endTime = $('#endTimeFilter').val();
|
||||
|
||||
$.get('/message_push/api/tasks', {
|
||||
page: page,
|
||||
limit: pageSize,
|
||||
status: status,
|
||||
start_time: startTime,
|
||||
end_time: endTime
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
const { tasks, total } = response.data;
|
||||
totalPages = Math.ceil(total / pageSize);
|
||||
|
||||
// 渲染任务列表
|
||||
let html = '';
|
||||
tasks.forEach(task => {
|
||||
html += `
|
||||
<tr>
|
||||
<td>${task.task_id}</td>
|
||||
<td>${task.task_name}</td>
|
||||
<td>${getStatusText(task.status)}</td>
|
||||
<td>${formatDateTime(task.schedule_time)}</td>
|
||||
<td>${formatDateTime(task.created_at)}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-info" onclick="previewTask('${task.task_id}')">
|
||||
<i class="fas fa-eye"></i> 预览
|
||||
</button>
|
||||
<button class="btn btn-sm btn-warning" onclick="editTask('${task.task_id}')">
|
||||
<i class="fas fa-edit"></i> 编辑
|
||||
</button>
|
||||
${task.status === 'scheduled' ? `
|
||||
<button class="btn btn-sm btn-secondary" onclick="pauseTask('${task.task_id}')">
|
||||
<i class="fas fa-pause"></i> 暂停
|
||||
</button>
|
||||
` : task.status === 'paused' ? `
|
||||
<button class="btn btn-sm btn-success" onclick="resumeTask('${task.task_id}')">
|
||||
<i class="fas fa-play"></i> 恢复
|
||||
</button>
|
||||
` : ''}
|
||||
<button class="btn btn-sm btn-danger" onclick="deleteTask('${task.task_id}')">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</button>
|
||||
<button class="btn btn-sm btn-primary" onclick="viewLogs('${task.task_id}')">
|
||||
<i class="fas fa-history"></i> 日志
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
$('#taskList').html(html);
|
||||
|
||||
// 更新分页信息
|
||||
updatePagination(page, total);
|
||||
} else {
|
||||
showError('加载任务列表失败:' + response.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 更新分页信息
|
||||
function updatePagination(currentPage, total) {
|
||||
// 更新信息文本
|
||||
const start = (currentPage - 1) * pageSize + 1;
|
||||
const end = Math.min(currentPage * pageSize, total);
|
||||
$('#taskInfo').text(`显示 ${start} 到 ${end} 条,共 ${total} 条记录`);
|
||||
|
||||
// 更新分页按钮
|
||||
let html = '<ul class="pagination">';
|
||||
|
||||
// 上一页
|
||||
html += `
|
||||
<li class="page-item ${currentPage === 1 ? 'disabled' : ''}">
|
||||
<a class="page-link" href="#" onclick="return loadTasks(${currentPage - 1})">上一页</a>
|
||||
</li>
|
||||
`;
|
||||
|
||||
// 页码
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
if (i === 1 || i === totalPages || (i >= currentPage - 2 && i <= currentPage + 2)) {
|
||||
html += `
|
||||
<li class="page-item ${i === currentPage ? 'active' : ''}">
|
||||
<a class="page-link" href="#" onclick="return loadTasks(${i})">${i}</a>
|
||||
</li>
|
||||
`;
|
||||
} else if (i === currentPage - 3 || i === currentPage + 3) {
|
||||
html += '<li class="page-item disabled"><a class="page-link">...</a></li>';
|
||||
}
|
||||
}
|
||||
|
||||
// 下一页
|
||||
html += `
|
||||
<li class="page-item ${currentPage === totalPages ? 'disabled' : ''}">
|
||||
<a class="page-link" href="#" onclick="return loadTasks(${currentPage + 1})">下一页</a>
|
||||
</li>
|
||||
`;
|
||||
|
||||
html += '</ul>';
|
||||
$('#taskPagination').html(html);
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
function getStatusText(status) {
|
||||
const statusMap = {
|
||||
'draft': '草稿',
|
||||
'scheduled': '已排期',
|
||||
'paused': '已暂停',
|
||||
'completed': '已完成'
|
||||
};
|
||||
return statusMap[status] || status;
|
||||
}
|
||||
|
||||
function formatDateTime(datetime) {
|
||||
return new Date(datetime).toLocaleString();
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
toastr.error(message);
|
||||
}
|
||||
|
||||
function showSuccess(message) {
|
||||
toastr.success(message);
|
||||
}
|
||||
|
||||
// 事件处理
|
||||
$('#searchBtn').click(function() {
|
||||
loadTasks(1);
|
||||
});
|
||||
|
||||
$('#resetBtn').click(function() {
|
||||
$('#statusFilter').val('');
|
||||
$('#startTimeFilter').val('');
|
||||
$('#endTimeFilter').val('');
|
||||
loadTasks(1);
|
||||
});
|
||||
|
||||
$('#saveTaskBtn').click(function() {
|
||||
const formData = new FormData($('#createTaskForm')[0]);
|
||||
const data = {};
|
||||
formData.forEach((value, key) => {
|
||||
if (key === 'groups' || key === 'preview_recipients') {
|
||||
data[key] = Array.from($(`#${key}`).val());
|
||||
} else {
|
||||
data[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
// 处理小程序内容
|
||||
data.content_miniprogram = {
|
||||
title: $('#miniprogramTitle').val(),
|
||||
path: $('#miniprogramPath').val()
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: '/message_push/api/tasks',
|
||||
type: 'POST',
|
||||
contentType: 'application/json',
|
||||
data: JSON.stringify(data),
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showSuccess('创建任务成功');
|
||||
$('#createTaskModal').modal('hide');
|
||||
loadTasks(1);
|
||||
} else {
|
||||
showError('创建任务失败:' + response.error);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 初始化
|
||||
loadTasks(1);
|
||||
});
|
||||
|
||||
// 任务操作函数
|
||||
function previewTask(taskId) {
|
||||
$.post(`/message_push/api/tasks/${taskId}/preview`, function(response) {
|
||||
if (response.success) {
|
||||
showSuccess('预览已发送');
|
||||
} else {
|
||||
showError('发送预览失败:' + response.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function editTask(taskId) {
|
||||
// TODO: 实现编辑任务功能
|
||||
}
|
||||
|
||||
function pauseTask(taskId) {
|
||||
$.post(`/message_push/api/tasks/${taskId}/pause`, function(response) {
|
||||
if (response.success) {
|
||||
showSuccess('任务已暂停');
|
||||
loadTasks(currentPage);
|
||||
} else {
|
||||
showError('暂停任务失败:' + response.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resumeTask(taskId) {
|
||||
$.post(`/message_push/api/tasks/${taskId}/resume`, function(response) {
|
||||
if (response.success) {
|
||||
showSuccess('任务已恢复');
|
||||
loadTasks(currentPage);
|
||||
} else {
|
||||
showError('恢复任务失败:' + response.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteTask(taskId) {
|
||||
if (confirm('确定要删除这个任务吗?')) {
|
||||
$.ajax({
|
||||
url: `/message_push/api/tasks/${taskId}`,
|
||||
type: 'DELETE',
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showSuccess('任务已删除');
|
||||
loadTasks(currentPage);
|
||||
} else {
|
||||
showError('删除任务失败:' + response.error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function viewLogs(taskId) {
|
||||
$.get(`/message_push/api/tasks/${taskId}/logs`, function(response) {
|
||||
if (response.success) {
|
||||
const { logs } = response.data;
|
||||
let html = '';
|
||||
logs.forEach(log => {
|
||||
html += `
|
||||
<tr>
|
||||
<td>${formatDateTime(log.timestamp)}</td>
|
||||
<td>${log.action}</td>
|
||||
<td>${log.operator_id}</td>
|
||||
<td>${JSON.stringify(log.changes)}</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
$('#taskLogsList').html(html);
|
||||
$('#taskLogsModal').modal('show');
|
||||
} else {
|
||||
showError('获取任务日志失败:' + response.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user