<think>我们需要构建一个基于Flask的后台管理系统,使用Layui作为前端框架,PostgreSQL作为数据库。系统需要实现对定时任务的增删改查功能。
步骤:
1. 设计数据库表(定时任务表)
2. 创建Flask应用,配置数据库连接
3. 实现路由和视图函数,包括:
- 显示任务列表页面
- 提供API接口供前端Layui表格使用(获取任务数据、添加、删除、修改)
4. 前端页面使用Layui的表格、表单等组件
详细步骤:
1. 数据库设计(PostgreSQL)
表名:scheduled_tasks
字段:
id: 主键,自增
name: 任务名称(字符串)
description: 任务描述(文本,可选)
cron_expr: cron表达式(字符串,例如 "0 * * * *")
command: 要执行的命令(字符串)
status: 状态(整数,0表示禁用,1表示启用)
created_at: 创建时间(时间戳,默认当前时间)
updated_at: 更新时间(时间戳,默认当前时间,更新时自动更新)
2. 创建Flask项目结构
项目目录结构示例:
app.py
templates/
index.html # 主页面,包含Layui表格
static/
# 存放静态文件,如layui的代码(也可以使用CDN)
3. 使用SQLAlchemy操作PostgreSQL(或者使用psycopg2,这里为了简便,我们使用Flask-SQLAlchemy)
4. 实现增删改查的API接口,并按照Layui Table要求的格式返回JSON数据。
5. 前端使用Layui的table模块,实现数据的展示和操作。
由于代码较长,我将分步骤给出关键代码。
步骤1:安装必要的库
pip install flask flask-sqlalchemy psycopg2-binary
步骤2:创建Flask应用和模型
文件:app.py</think>下面我将详细讲解如何使用 Flask + Layui + PostgreSQL 实现一个定时任务后台管理系统。这个方案包含完整的 MVC 架构实现,采用 RESTful API 设计风格。
### 解决方案概览
1. **技术栈**:
- 后端:Flask (Python Web 框架)
- 前端:Layui (UI 框架)
- 数据库:PostgreSQL (关系型数据库)
- ORM:SQLAlchemy (数据库操作)
2. **系统功能**:
- 定时任务增删改查
- 分页展示
- 状态管理(启用/禁用)
- Cron 表达式验证
---
### 一、数据库设计 (PostgreSQL)
创建定时任务表 `scheduled_tasks`:
```sql
CREATE TABLE scheduled_tasks (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
cron_expr VARCHAR(50) NOT NULL CHECK (cron_expr ~ '^(\S+\s){4}\S+$'),
command TEXT NOT NULL,
status INTEGER NOT NULL DEFAULT 1, -- 1启用 0禁用
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
---
### 二、后端实现 (Flask)
#### 1. 项目结构
```
project/
├── app.py
├── models.py
├── templates/
│ └── index.html
└── requirements.txt
```
#### 2. 模型定义 (models.py)
```python
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
db = SQLAlchemy()
class ScheduledTask(db.Model):
__tablename__ = 'scheduled_tasks'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text)
cron_expr = db.Column(db.String(50), nullable=False)
command = db.Column(db.Text, nullable=False)
status = db.Column(db.Integer, default=1)
created_at = db.Column(db.TIMESTAMP, default=datetime.utcnow)
updated_at = db.Column(db.TIMESTAMP, default=datetime.utcnow, onupdate=datetime.utcnow)
def to_dict(self):
return {
"id": self.id,
"name": self.name,
"description": self.description,
"cron_expr": self.cron_expr,
"command": self.command,
"status": self.status,
"created_at": self.created_at.strftime("%Y-%m-%d %H:%M:%S"),
"updated_at": self.updated_at.strftime("%Y-%m-%d %H:%M:%S")
}
```
#### 3. Flask 主应用 (app.py)
```python
from flask import Flask, render_template, request, jsonify
from models import db, ScheduledTask
import re
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:password@localhost/dbname'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
# Cron 表达式验证函数
def validate_cron(expr):
return re.match(r'^(\S+\s){4}\S+$', expr) is not None
# 任务列表API
@app.route('/api/tasks', methods=['GET'])
def get_tasks():
page = int(request.args.get('page', 1))
limit = int(request.args.get('limit', 10))
pagination = ScheduledTask.query.paginate(page=page, per_page=limit, error_out=False)
tasks = [task.to_dict() for task in pagination.items]
return jsonify({
"code": 0,
"msg": "success",
"count": pagination.total,
"data": tasks
})
# 任务操作API
@app.route('/api/task', methods=['POST', 'PUT', 'DELETE'])
def handle_task():
data = request.json
# 添加任务
if request.method == 'POST':
if not validate_cron(data['cron_expr']):
return jsonify({"code": 400, "msg": "Invalid cron expression"})
new_task = ScheduledTask(
name=data['name'],
description=data.get('description', ''),
cron_expr=data['cron_expr'],
command=data['command'],
status=data.get('status', 1)
)
db.session.add(new_task)
db.session.commit()
return jsonify({"code": 0, "msg": "Added successfully"})
# 更新任务
if request.method == 'PUT':
task = ScheduledTask.query.get(data['id'])
if not task:
return jsonify({"code": 404, "msg": "Task not found"})
if 'cron_expr' in data and not validate_cron(data['cron_expr']):
return jsonify({"code": 400, "msg": "Invalid cron expression"})
task.name = data.get('name', task.name)
task.description = data.get('description', task.description)
task.cron_expr = data.get('cron_expr', task.cron_expr)
task.command = data.get('command', task.command)
task.status = data.get('status', task.status)
db.session.commit()
return jsonify({"code": 0, "msg": "Updated successfully"})
# 删除任务
if request.method == 'DELETE':
task = ScheduledTask.query.get(data['id'])
if not task:
return jsonify({"code": 404, "msg": "Task not found"})
db.session.delete(task)
db.session.commit()
return jsonify({"code": 0, "msg": "Deleted successfully"})
# 主页面
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)
```
---
### 三、前端实现 (Layui)
#### templates/index.html
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>定时任务管理</title>
<link rel="stylesheet" href="https://cdn.layui.com/2.8.3/css/layui.css">
</head>
<body>
<div class="layui-container">
<div class="layui-card">
<div class="layui-card-header">
<h2>定时任务管理</h2>
<button class="layui-btn layui-btn-normal" id="btn-add">
<i class="layui-icon"></i> 添加任务
</button>
</div>
<div class="layui-card-body">
<table id="task-table" lay-filter="task-table"></table>
</div>
</div>
</div>
<!-- 添加/编辑弹窗 -->
<div id="task-form" style="display: none; padding: 20px;">
<form class="layui-form">
<input type="hidden" name="id">
<div class="layui-form-item">
<label class="layui-form-label">任务名称</label>
<div class="layui-input-block">
<input type="text" name="name" required lay-verify="required" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">Cron表达式</label>
<div class="layui-input-block">
<input type="text" name="cron_expr" placeholder="* * * * *" required lay-verify="required" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">执行命令</label>
<div class="layui-input-block">
<textarea name="command" required lay-verify="required" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">任务描述</label>
<div class="layui-input-block">
<textarea name="description" class="layui-textarea"></textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
<input type="checkbox" name="status" lay-skin="switch" lay-text="启用|禁用" checked>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="form-submit">提交</button>
</div>
</div>
</form>
</div>
<script src="https://cdn.layui.com/2.8.3/layui.js"></script>
<script>
layui.use(['table', 'form', 'layer'], function() {
const table = layui.table;
const form = layui.form;
const layer = layui.layer;
const $ = layui.$;
// 初始化表格
table.render({
elem: '#task-table',
url: '/api/tasks',
page: true,
cols: [[
{field: 'id', title: 'ID', width: 80},
{field: 'name', title: '任务名称'},
{field: 'cron_expr', title: 'Cron表达式', width: 150},
{field: 'command', title: '执行命令', width: 200},
{field: 'status', title: '状态', width: 100, templet: d =>
`<span class="layui-badge ${d.status ? 'layui-bg-green' : ''}">${d.status ? '启用' : '禁用'}</span>`
},
{field: 'created_at', title: '创建时间', width: 180},
{title: '操作', width: 200, toolbar: '#toolbar'}
]]
});
// 添加任务按钮
$('#btn-add').click(() => {
form.val('form', { status: 1 });
layer.open({
type: 1,
title: '添加任务',
area: '600px',
content: $('#task-form')
});
});
// 表格工具栏
table.on('tool(task-table)', obj => {
const data = obj.data;
if (obj.event === 'edit') {
form.val('form', {
id: data.id,
name: data.name,
cron_expr: data.cron_expr,
command: data.command,
description: data.description,
status: data.status
});
layer.open({
type: 1,
title: '编辑任务',
area: '600px',
content: $('#task-form')
});
} else if (obj.event === 'delete') {
layer.confirm('确定删除该任务?', () => {
$.ajax({
url: '/api/task',
type: 'DELETE',
contentType: 'application/json',
data: JSON.stringify({id: data.id}),
success: () => {
layer.msg('删除成功');
table.reload('task-table');
}
});
});
}
});
// 表单提交
form.on('submit(form-submit)', formData => {
const method = formData.field.id ? 'PUT' : 'POST';
$.ajax({
url: '/api/task',
type: method,
contentType: 'application/json',
data: JSON.stringify(formData.field),
success: () => {
layer.msg(method === 'POST' ? '添加成功' : '更新成功');
layer.closeAll();
table.reload('task-table');
},
error: (res) => {
layer.msg(res.responseJSON.msg || '操作失败');
}
});
return false;
});
});
</script>
<!-- 工具栏模板 -->
<script type="text/html" id="toolbar">
<div class="layui-btn-group">
<button class="layui-btn layui-btn-xs" lay-event="edit">编辑</button>
<button class="layui-btn layui-btn-xs layui-btn-danger" lay-event="delete">删除</button>
</div>
</script>
</body>
</html>
```
---
### 关键功能说明
1. **Cron 表达式验证**:
- 使用正则表达式 `^(\S+\s){4}\S+$` 验证格式
- 确保用户输入合法的5段式表达式
2. **RESTful API 设计**:
- `GET /api/tasks`:分页获取任务列表
- `POST /api/task`:创建新任务
- `PUT /api/task`:更新任务
- `DELETE /api/task`:删除任务
3. **Layui 表格功能**:
- 分页加载数据
- 工具栏操作(编辑/删除)
- 状态标签显示
4. **数据交互**:
- 前端使用 AJAX 与后端通信
- 统一 JSON 响应格式:`{code, msg, data}`
---
### 部署运行步骤
1. 创建 PostgreSQL 数据库并执行提供的 SQL
2. 安装依赖:
```bash
pip install flask flask-sqlalchemy psycopg2-binary
```
3. 修改 `app.py` 中的数据库连接字符串
4. 启动应用:
```bash
python app.py
```
5. 访问 `http://localhost:5000`
---
### 系统优化建议
1. 增加用户认证(使用 Flask-Login)
2. 添加任务日志记录功能
3. 集成 Celery 实现真正的定时任务调度
4. 实现任务立即执行功能
5. 添加操作审计日志