Compare commits

..

12 Commits

Author SHA1 Message Date
54130d4401 docs: 更新README文档和修复模型复数名称
- 更新README文档,添加新功能描述和API端点详情
- 修复TaskResult模型的verbose_name_plural字段
2026-01-28 16:47:17 +08:00
a8e24fad63 Merge branch 'main' of http://14.103.237.41:16001/xiaji/central-task 2025-12-12 12:02:59 +08:00
97c0e0db0d 当创建一个客户端的时候,自动创建一个API Token 2025-12-12 11:55:36 +08:00
c4086b9f46 删除 'db.sqlite3' 2025-12-11 15:12:02 +08:00
d7c1d00ba1 删除pyc文件 2025-12-11 15:06:16 +08:00
ddcd490575 删除 'task_center/__pycache__/wsgi.cpython-311.pyc' 2025-12-11 15:05:01 +08:00
c71f8f7740 删除 'task_center/__pycache__/urls.cpython-311.pyc' 2025-12-11 15:04:56 +08:00
de0af70958 删除 'task_center/__pycache__/settings.cpython-311.pyc' 2025-12-11 15:04:50 +08:00
2630405dac 删除 'task_center/__pycache__/__init__.cpython-311.pyc' 2025-12-11 15:04:43 +08:00
98925f4cce 更新了admin的显示内容 2025-12-11 14:33:32 +08:00
becca344bf Merge branch 'main' of http://14.103.237.41:16001/xiaji/central-task 2025-12-11 13:29:54 +08:00
527cb1dc3e 修改了admin的入口地址 2025-12-11 13:17:19 +08:00
21 changed files with 301 additions and 132 deletions

View File

@@ -1,13 +0,0 @@
# Django settings
SECRET_KEY=your-secret-key
DEBUG=True
ALLOWED_HOSTS=127.0.0.1,localhost
# Database settings
# DATABASE_URL=sqlite:///db.sqlite3
# For PostgreSQL: DATABASE_URL=postgres://user:password@localhost:5432/dbname
# For MySQL: DATABASE_URL=mysql://user:password@localhost:3306/dbname
# Static and media files
STATIC_ROOT=static
MEDIA_ROOT=media

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
# Virtual environments # Virtual environments
.env
venv/ venv/
.venv/ .venv/
env/ env/

View File

@@ -1,6 +1,7 @@
# 任务中心管理系统最终实现计划 # 任务中心管理系统最终实现计划
## 项目结构设计 ## 项目结构设计
1. 创建Django项目`task_center` 1. 创建Django项目`task_center`
2. 创建任务管理应用:`tasks` 2. 创建任务管理应用:`tasks`
3. 配置数据库为SQLite 3. 配置数据库为SQLite
@@ -10,41 +11,45 @@
## 数据库模型设计 ## 数据库模型设计
### Client模型 ### Client模型
| 字段名 | 类型 | 描述 |
|-------|------|------| | 字段名 | 类型 | 描述 |
| id | AutoField | 客户端ID | | ----------- | -------------------------- | --------- |
| name | CharField(unique=True) | 客户端标识 | | id | AutoField | 客户端ID |
| token | CharField(max_length=128) | API Token | | name | CharField(unique=True) | 客户端标识 |
| last_seen | DateTimeField | 最后活跃时间 | | token | CharField(max\_length=128) | API Token |
| created_at | DateTimeField | 创建时间 | | last\_seen | DateTimeField | 最后活跃时间 |
| created\_at | DateTimeField | 创建时间 |
### Task模型 ### Task模型
| 字段名 | 类型 | 描述 |
|-------|------|------| | 字段名 | 类型 | 描述 |
| id | AutoField | 任务ID | | ---------------- | ----------------------------------------------------- | ------------------ |
| name | CharField | 任务名称 | | id | AutoField | 任务ID |
| client_name | CharField(null=True, blank=True) | 指定执行客户端 | | name | CharField | 任务名称 |
| script | TextField(null=True, blank=True) | 执行脚本 | | client\_name | CharField(null=True, blank=True) | 指定执行客户端 |
| status | CharField(choices=STATUS_CHOICES, default='pending') | 任务状态 | | script | TextField(null=True, blank=True) | 执行脚本 |
| timeout_seconds | IntegerField(default=259200) | 超时时间默认3天=259200秒 | | status | CharField(choices=STATUS\_CHOICES, default='pending') | 任务状态 |
| created_at | DateTimeField | 创建时间 | | timeout\_seconds | IntegerField(default=259200) | 超时时间默认3天=259200秒 |
| updated_at | DateTimeField | 更新时间 | | created\_at | DateTimeField | 创建时间 |
| assigned_to | CharField(null=True, blank=True) | 实际执行客户端 | | updated\_at | DateTimeField | 更新时间 |
| started_at | DateTimeField(null=True, blank=True) | 开始执行时间 | | assigned\_to | CharField(null=True, blank=True) | 实际执行客户端 |
| completed_at | DateTimeField(null=True, blank=True) | 完成时间 | | started\_at | DateTimeField(null=True, blank=True) | 开始执行时间 |
| completed\_at | DateTimeField(null=True, blank=True) | 完成时间 |
### TaskResult模型 ### TaskResult模型
| 字段名 | 类型 | 描述 |
|-------|------|------| | 字段名 | 类型 | 描述 |
| id | AutoField | 结果ID | | ------------ | -------------------------------------- | ----- |
| task | ForeignKey(Task) | 关联任务 | | id | AutoField | 结果ID |
| client | ForeignKey(Client) | 执行客户端 | | task | ForeignKey(Task) | 关联任务 |
| result_file | FileField(upload_to='task_results/') | 结果文件 | | client | ForeignKey(Client) | 执行客户端 |
| status | CharField(choices=STATUS_CHOICES) | 执行状态 | | result\_file | FileField(upload\_to='task\_results/') | 结果文件 |
| message | TextField(null=True, blank=True) | 执行消息 | | status | CharField(choices=STATUS\_CHOICES) | 执行状态 |
| created_at | DateTimeField | 创建时间 | | message | TextField(null=True, blank=True) | 执行消息 |
| created\_at | DateTimeField | 创建时间 |
## 状态定义 ## 状态定义
```python ```python
STATUS_CHOICES = [ STATUS_CHOICES = [
('pending', '待分配'), ('pending', '待分配'),
@@ -60,35 +65,53 @@ STATUS_CHOICES = [
## API接口设计 ## API接口设计
### 认证机制 ### 认证机制
- **所有API端点均需token认证**
- 客户端通过HTTP头`Authorization: Token <token>`进行身份验证 * **所有API端点均需token认证**
- 使用Django REST Framework的TokenAuthentication
- 未提供有效token的请求将返回401 Unauthorized * 客户端通过HTTP头`Authorization: Token <token>`进行身份验证
* 使用Django REST Framework的TokenAuthentication
* 未提供有效token的请求将返回401 Unauthorized
### 任务管理API ### 任务管理API
- `GET /api/tasks/` - 获取任务列表(需认证)
- `GET /api/tasks/<id>/` - 获取任务详情(需认证) * `GET /api/tasks/` - 获取任务列表(需认证)
- `POST /api/tasks/` - 创建任务(需认证)
- `PUT /api/tasks/<id>/` - 更新任务(需认证) * `GET /api/tasks/<id>/` - 获取任务详情(需认证)
- `DELETE /api/tasks/<id>/` - 删除任务(需认证)
* `POST /api/tasks/` - 创建任务(需认证)
* `PUT /api/tasks/<id>/` - 更新任务(需认证)
* `DELETE /api/tasks/<id>/` - 删除任务(需认证)
### 客户端API ### 客户端API
- `POST /api/tasks/claim/` - 客户端原子认领任务(需认证)
- `POST /api/tasks/<id>/start/` - 客户端开始执行任务(需认证) * `POST /api/tasks/claim/` - 客户端原子认领任务(需认证)
- `POST /api/tasks/<id>/complete/` - 客户端完成任务(需认证)
- `POST /api/task_results/` - 上传任务结果(需认证,支持文件上传 * `POST /api/tasks/<id>/start/` - 客户端开始执行任务(需认证
* `POST /api/tasks/<id>/complete/` - 客户端完成任务(需认证)
* `POST /api/task_results/` - 上传任务结果(需认证,支持文件上传)
### 客户端管理API ### 客户端管理API
- `GET /api/clients/` - 获取客户端列表(需认证)
- `POST /api/clients/` - 创建客户端(需认证) * `GET /api/clients/` - 获取客户端列表(需认证)
- `GET /api/clients/<id>/` - 获取客户端详情(需认证)
* `POST /api/clients/` - 创建客户端(需认证)
* `GET /api/clients/<id>/` - 获取客户端详情(需认证)
### 文件下载API ### 文件下载API
- `GET /api/task_results/<id>/download/` - 下载任务结果文件(需认证)
* `GET /api/task_results/<id>/download/` - 下载任务结果文件(需认证)
## 文件上传实现细节 ## 文件上传实现细节
### 1. Django媒体文件配置 ### 1. Django媒体文件配置
```python ```python
# settings.py # settings.py
MEDIA_URL = '/media/' MEDIA_URL = '/media/'
@@ -96,28 +119,43 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
``` ```
### 2. 文件上传API实现 ### 2. 文件上传API实现
- 使用Django REST Framework的`MultiPartParser``FormParser`
- API端点`POST /api/task_results/` * 使用Django REST Framework的`MultiPartParser``FormParser`
- 支持`multipart/form-data`格式上传文件
- 上传字段:`task_id``status``message``result_file` * API端点`POST /api/task_results/`
* 支持`multipart/form-data`格式上传文件
* 上传字段:`task_id``status``message``result_file`
### 3. 前端文件上传实现 ### 3. 前端文件上传实现
- 使用HTML5的`input type="file"`元素
- 表单设置`enctype="multipart/form-data"` * 使用HTML5的`input type="file"`元素
- 使用Bootstrap样式美化文件上传控件
- 支持进度条显示(可选) * 表单设置`enctype="multipart/form-data"`
* 使用Bootstrap样式美化文件上传控件
* 支持进度条显示(可选)
### 4. 文件存储策略 ### 4. 文件存储策略
- 本地文件系统存储:`media/task_results/`目录
- 文件名自动生成,避免冲突 * 本地文件系统存储:`media/task_results/`目录
- 支持大文件上传通过Django默认配置
* 文件名自动生成,避免冲突
* 支持大文件上传通过Django默认配置
### 5. 文件下载实现 ### 5. 文件下载实现
- API端点`GET /api/task_results/<id>/download/`
- 返回`Content-Disposition: attachment`头,触发浏览器下载 * API端点`GET /api/task_results/<id>/download/`
- 支持断点续传通过Django默认配置
* 返回`Content-Disposition: attachment`头,触发浏览器下载
* 支持断点续传通过Django默认配置
## 前端页面设计 ## 前端页面设计
1. 任务列表页:展示所有任务,支持筛选和搜索 1. 任务列表页:展示所有任务,支持筛选和搜索
2. 任务创建页:表单创建新任务 2. 任务创建页:表单创建新任务
3. 任务详情页:查看任务详情和执行结果历史 3. 任务详情页:查看任务详情和执行结果历史
@@ -128,26 +166,37 @@ MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
## 核心功能实现 ## 核心功能实现
### 1. 全API Token认证 ### 1. 全API Token认证
- 所有API视图均使用`TokenAuthentication`
- 配置`DEFAULT_AUTHENTICATION_CLASSES``DEFAULT_PERMISSION_CLASSES` * 所有API视图均使用`TokenAuthentication`
* 配置`DEFAULT_AUTHENTICATION_CLASSES``DEFAULT_PERMISSION_CLASSES`
### 2. 原子任务认领机制 ### 2. 原子任务认领机制
- 使用数据库事务确保任务认领的原子性
- 客户端调用`/api/tasks/claim/`时,系统自动查找并分配可用任务 * 使用数据库事务确保任务认领的原子性
* 客户端调用`/api/tasks/claim/`时,系统自动查找并分配可用任务
### 3. 任务超时自动回收 ### 3. 任务超时自动回收
- 使用Django管理命令定期检查超时任务
- 超过`timeout_seconds`的任务自动设置为`timeout`状态 * 使用Django管理命令定期检查超时任务
- 管理命令:`python manage.py check_task_timeouts`
* 超过`timeout_seconds`的任务自动设置为`timeout`状态
* 管理命令:`python manage.py check_task_timeouts`
### 4. 任务结果版本管理 ### 4. 任务结果版本管理
- TaskResult模型记录每次执行结果
- 支持查看任务的完整执行历史 * TaskResult模型记录每次执行结果
- 支持下载不同版本的结果文件
* 支持查看任务的完整执行历史
* 支持下载不同版本的结果文件
## 测试用例设计 ## 测试用例设计
### 目录结构 ### 目录结构
``` ```
task_center/ task_center/
├── tasks/ ├── tasks/
@@ -164,14 +213,16 @@ task_center/
``` ```
### 测试类型 ### 测试类型
1. **模型测试**:测试模型字段、方法和关系 1. **模型测试**:测试模型字段、方法和关系
2. **API测试**测试所有API端点的功能和认证机制 2. **API测试**测试所有API端点的功能和认证机制
3. **文件上传测试**:测试文件上传和下载功能 3. **文件上传测试**:测试文件上传和下载功能
4. **视图测试**:测试前端视图渲染 4. **视图测试**:测试前端视图渲染
5. **集成测试**:测试完整的任务流程 5. **集成测试**:测试完整的任务流程
6. **工厂测试**使用factory_boy创建测试数据 6. **工厂测试**使用factory\_boy创建测试数据
## 实现步骤 ## 实现步骤
1. 创建Django项目和应用 1. 创建Django项目和应用
2. 配置项目设置数据库、REST Framework、认证、媒体文件等 2. 配置项目设置数据库、REST Framework、认证、媒体文件等
3. 实现数据库模型 3. 实现数据库模型
@@ -183,18 +234,32 @@ task_center/
9. 测试API接口和功能 9. 测试API接口和功能
## 技术栈 ## 技术栈
- 后端Django 5.0.6 + Django REST Framework
- 端:HTML + Bootstrap 5 * 端:Django 5.0.6 + Django REST Framework
- 数据库SQLite
- 文件存储:本地文件系统 * 前端HTML + Bootstrap 5
- 测试pytest + factory_boy
* 数据库SQLite
* 文件存储:本地文件系统
* 测试pytest + factory\_boy
## 预期效果 ## 预期效果
- 所有API端点均需要token认证
- 支持结果文件的上传和下载功能 * 所有API端点均需要token认证
- 后台可通过admin或API创建和管理任务
- 客户端通过API进行身份验证原子认领任务 * 支持结果文件的上传和下载功能
- 支持任务超时自动回收
- 完整的任务执行历史记录 * 后台可通过admin或API创建和管理任务
- 简单大方的Bootstrap前端界面
- 全面的测试用例覆盖 * 客户端通过API进行身份验证原子认领任务
* 支持任务超时自动回收
* 完整的任务执行历史记录
* 简单大方的Bootstrap前端界面
* 全面的测试用例覆盖

View File

@@ -9,21 +9,30 @@
- 设置任务执行客户端(指定或自动分配) - 设置任务执行客户端(指定或自动分配)
- 配置任务超时时间 - 配置任务超时时间
- 支持任务脚本存储 - 支持任务脚本存储
- 完整的任务生命周期管理
### 2. 客户端管理 ### 2. 客户端管理
- 客户端注册和身份验证 - 客户端注册和身份验证
- 自动生成API Token - 自动生成安全的API Token
- 客户端活跃状态跟踪 - 客户端活跃状态跟踪
### 3. 任务执行流程 ### 3. 任务执行流程
- **原子任务认领**:避免竞态条件 - **原子任务认领**使用数据库事务确保并发安全,避免竞态条件
- 任务状态跟踪:待分配、已分配、执行中、成功、失败、重试中、超时 - 任务状态跟踪:待分配、已分配、执行中、成功、失败、重试中、超时
- 任务结果上传和下载 - 任务结果上传和下载
- 完整的任务执行API认领、开始、完成
### 4. 系统管理 ### 4. 系统管理
- 完整的后台管理界面 - 完整的后台管理界面
- 任务超时自动回收 - 任务超时自动回收
- 结果文件管理 - 结果文件管理
- API权限控制所有API端点均需要认证
### 5. 前端界面
- 响应式设计基于Bootstrap 5
- 任务管理:列表、创建、详情
- 客户端管理:列表、创建
- 结果查看和下载
## 技术栈 ## 技术栈
@@ -91,7 +100,7 @@ python manage.py runserver
#### 1.2 管理客户端 #### 1.2 管理客户端
- 在左侧菜单栏点击 "客户端" - 在左侧菜单栏点击 "客户端"
- 点击 "添加客户端" 创建新客户端 - 点击 "添加客户端" 创建新客户端
- 系统会自动生成API Token - 系统会自动生成安全的API Token
#### 1.3 管理任务 #### 1.3 管理任务
- 在左侧菜单栏点击 "任务" - 在左侧菜单栏点击 "任务"
@@ -127,19 +136,30 @@ python manage.py runserver
Authorization: Token <your-token> Authorization: Token <your-token>
``` ```
**注意**所有API端点均需要认证使用 `IsAuthenticated` 权限类控制。
#### 3.2 主要API端点 #### 3.2 主要API端点
| 端点 | 方法 | 功能 | | 端点 | 方法 | 功能 | 权限 |
|------|------|------| |------|------|------|------|
| `/api/clients/` | GET | 获取客户端列表 | | `/api/clients/` | GET | 获取客户端列表 | 需要认证 |
| `/api/clients/` | POST | 创建客户端 | | `/api/clients/` | POST | 创建客户端 | 需要认证 |
| `/api/tasks/` | GET | 获取任务列表 | | `/api/clients/<id>/` | GET | 获取客户端详情 | 需要认证 |
| `/api/tasks/` | POST | 创建任务 | | `/api/clients/<id>/` | PUT | 更新客户端 | 需要认证 |
| `/api/tasks/claim/` | POST | 客户端认领任务 | | `/api/clients/<id>/` | DELETE | 删除客户端 | 需要认证 |
| `/api/tasks/<id>/start/` | POST | 开始执行任务 | | `/api/tasks/` | GET | 获取任务列表 | 需要认证 |
| `/api/tasks/<id>/complete/` | POST | 完成任务 | | `/api/tasks/` | POST | 创建任务 | 需要认证 |
| `/api/task_results/` | POST | 上传任务结果 | | `/api/tasks/<id>/` | GET | 获取任务详情 | 需要认证 |
| `/api/task_results/<id>/download/` | GET | 下载结果文件 | | `/api/tasks/<id>/` | PUT | 更新任务 | 需要认证 |
| `/api/tasks/<id>/` | DELETE | 删除任务 | 需要认证 |
| `/api/tasks/claim/` | POST | 客户端认领任务 | 需要认证 |
| `/api/tasks/<id>/start/` | POST | 开始执行任务 | 需要认证 |
| `/api/tasks/<id>/complete/` | POST | 完成任务 | 需要认证 |
| `/api/task_results/` | GET | 获取任务结果列表 | 需要认证 |
| `/api/task_results/` | POST | 上传任务结果 | 需要认证 |
| `/api/task_results/<id>/` | GET | 获取任务结果详情 | 需要认证 |
| `/api/task_results/<id>/` | DELETE | 删除任务结果 | 需要认证 |
| `/api/task_results/<id>/download/` | GET | 下载结果文件 | 需要认证 |
#### 3.3 API使用示例 #### 3.3 API使用示例
@@ -159,6 +179,20 @@ curl -X POST http://127.0.0.1:8000/api/tasks/claim/ \
-d '{"client_name":"client1"}' -d '{"client_name":"client1"}'
``` ```
##### 开始执行任务
```bash
curl -X POST http://127.0.0.1:8000/api/tasks/1/start/ \
-H "Authorization: Token <token>"
```
##### 完成任务
```bash
curl -X POST http://127.0.0.1:8000/api/tasks/1/complete/ \
-H "Authorization: Token <token>" \
-H "Content-Type: application/json" \
-d '{"status":"success","message":"任务执行成功"}'
```
##### 上传任务结果 ##### 上传任务结果
```bash ```bash
curl -X POST http://127.0.0.1:8000/api/task_results/ \ curl -X POST http://127.0.0.1:8000/api/task_results/ \
@@ -170,22 +204,31 @@ curl -X POST http://127.0.0.1:8000/api/task_results/ \
-F "result_file=@result.txt" -F "result_file=@result.txt"
``` ```
##### 下载任务结果文件
```bash
curl -X GET http://127.0.0.1:8000/api/task_results/1/download/ \
-H "Authorization: Token <token>" \
-o result.txt
```
## 项目结构 ## 项目结构
``` ```
task_center/ 任务中心/
├── task_center/ # 项目配置 ├── task_center/ # 项目配置
│ ├── settings.py # 项目设置 │ ├── settings.py # 项目设置
│ └── urls.py # 主URL配置 │ └── urls.py # 主URL配置
├── tasks/ # 任务应用 ├── tasks/ # 任务应用
│ ├── models.py # 数据库模型 │ ├── models.py # 数据库模型
│ ├── serializers.py # API序列化器 │ ├── serializers.py # API序列化器
│ ├── views.py # API视图 │ ├── views.py # API视图使用IsAuthenticated权限
│ ├── views_frontend.py # 前端视图 │ ├── views_frontend.py # 前端视图
│ ├── urls.py # 应用URL配置 │ ├── urls.py # 应用URL配置
│ ├── templates/ # 前端模板 │ ├── templates/ # 前端模板
│ ├── tests/ # 测试用例 │ ├── tests/ # 测试用例
│ └── management/ # 管理命令 │ └── management/ # 管理命令
├── media/ # 媒体文件存储
│ └── task_results/ # 任务结果文件
└── manage.py # 项目入口 └── manage.py # 项目入口
``` ```
@@ -199,15 +242,20 @@ python manage.py check_task_timeouts
建议将此命令添加到系统定时任务中,例如每小时执行一次。 建议将此命令添加到系统定时任务中,例如每小时执行一次。
### 命令功能说明
- **check_task_timeouts**:检查所有执行中任务,将超过超时时间的任务标记为超时状态,使其可以被重新分配执行。
## 注意事项 ## 注意事项
1. **文件存储**:结果文件存储在 `media/task_results/` 目录下,请确保该目录有写入权限 1. **文件存储**:结果文件存储在 `media/task_results/` 目录下,请确保该目录有写入权限
2. **API Token安全**请妥善保管客户端API Token避免泄露 2. **API Token安全**请妥善保管客户端API Token避免泄露
3. **生产环境配置** 3. **API权限控制**所有API端点均需要认证使用 `IsAuthenticated` 权限类控制
4. **生产环境配置**
- 建议使用PostgreSQL或MySQL数据库 - 建议使用PostgreSQL或MySQL数据库
- 配置合适的文件存储后端如S3 - 配置合适的文件存储后端如S3
- 启用HTTPS - 启用HTTPS
- 配置适当的日志记录 - 配置适当的日志记录
- 实现更细粒度的权限控制(如基于角色的权限)
## 许可证 ## 许可证
@@ -233,6 +281,13 @@ python -m pytest --ds=task_center.settings
## 更新日志 ## 更新日志
### v1.1.0
- 增强API权限控制所有API端点均使用 `IsAuthenticated` 权限类
- 完善API文档更新API端点描述和使用示例
- 优化前端界面:改进任务和客户端管理功能
- 增强任务执行流程完善认领、开始、完成的API实现
- 改进项目结构文档:添加媒体文件存储目录说明
### v1.0.0 ### v1.0.0
- 初始版本发布 - 初始版本发布
- 完成任务管理核心功能 - 完成任务管理核心功能

Binary file not shown.

View File

@@ -14,15 +14,15 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path 1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from tasks.views_frontend import index from tasks.views_frontend import index
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('houtai/', admin.site.urls),
path('api/', include('tasks.urls')), path('api/', include('tasks.urls')),
# Root URL points to home page # Root URL points to home page
path('', index, name='index'), path('', index, name='index'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -1,3 +1,56 @@
from django.contrib import admin from django.contrib import admin
from .models import Client, Task, TaskResult
# Register your models here.
@admin.register(Client)
class ClientAdmin(admin.ModelAdmin):
"""客户端管理类"""
list_display = ('name', 'token', 'last_seen', 'created_at')
list_filter = ('created_at', 'last_seen')
search_fields = ('name',)
readonly_fields = ('token', 'created_at', 'last_seen')
@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
"""任务管理类"""
list_display = ('name', 'client_name', 'status', 'timeout_seconds', 'created_at', 'updated_at', 'assigned_to')
list_filter = ('status', 'created_at', 'updated_at', 'started_at', 'completed_at')
search_fields = ('name', 'client_name', 'assigned_to')
readonly_fields = ('created_at', 'updated_at', 'started_at', 'completed_at')
fieldsets = (
(None, {
'fields': ('name', 'client_name', 'script', 'status', 'timeout_seconds')
}),
('执行信息', {
'fields': ('assigned_to', 'started_at', 'completed_at'),
'classes': ('collapse',)
}),
('时间信息', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
@admin.register(TaskResult)
class TaskResultAdmin(admin.ModelAdmin):
"""任务结果管理类"""
list_display = ('task', 'client', 'status', 'created_at')
list_filter = ('status', 'created_at', 'client')
search_fields = ('task__name', 'client__name', 'message')
readonly_fields = ('created_at',)
fieldsets = (
(None, {
'fields': ('task', 'client', 'status', 'message')
}),
('结果文件', {
'fields': ('result_file',),
'classes': ('collapse',)
}),
('时间信息', {
'fields': ('created_at',),
'classes': ('collapse',)
}),
)

View File

@@ -1,5 +1,6 @@
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
import secrets
# Status choices for tasks # Status choices for tasks
STATUS_CHOICES = [ STATUS_CHOICES = [
@@ -20,6 +21,13 @@ class Client(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
def save(self, *args, **kwargs):
# Generate a unique token if this is a new client
if not self.pk and not self.token:
# Use secrets module to generate a secure random token
self.token = secrets.token_urlsafe(64) # 64 bytes -> ~86 characters
super().save(*args, **kwargs)
class Meta: class Meta:
verbose_name = '客户端' verbose_name = '客户端'
@@ -57,4 +65,4 @@ class TaskResult(models.Model):
class Meta: class Meta:
verbose_name = '任务结果' verbose_name = '任务结果'
verbose_name_plural = '任务结果' verbose_name_plural = '任务结果'

View File

@@ -28,7 +28,7 @@
<a class="nav-link" href="{% url 'client_list' %}">客户端管理</a> <a class="nav-link" href="{% url 'client_list' %}">客户端管理</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/admin/">后台管理</a> <a class="nav-link" href="/houtai/">后台管理</a>
</li> </li>
</ul> </ul>
</div> </div>
@@ -50,7 +50,7 @@
</ul> </ul>
<div class="mt-4"> <div class="mt-4">
<a class="btn btn-primary btn-lg" href="{% url 'task_list' %}" role="button">查看任务</a> <a class="btn btn-primary btn-lg" href="{% url 'task_list' %}" role="button">查看任务</a>
<a class="btn btn-secondary btn-lg" href="/admin/" role="button">后台管理</a> <a class="btn btn-secondary btn-lg" href="/houtai/" role="button">后台管理</a>
</div> </div>
</div> </div>
@@ -78,7 +78,7 @@
<div class="card-body"> <div class="card-body">
<h5 class="card-title">后台管理</h5> <h5 class="card-title">后台管理</h5>
<p class="card-text">使用Django Admin进行系统管理包括用户、任务和客户端。</p> <p class="card-text">使用Django Admin进行系统管理包括用户、任务和客户端。</p>
<a href="/admin/" class="btn btn-primary">进入</a> <a href="/houtai/" class="btn btn-primary">进入</a>
</div> </div>
</div> </div>
</div> </div>