184 lines
7.2 KiB
Python
184 lines
7.2 KiB
Python
from django.db import models
|
||
from django.contrib.auth.models import User
|
||
import json
|
||
|
||
|
||
class Task(models.Model):
|
||
"""任务模型"""
|
||
|
||
TASK_TYPES = [
|
||
('single', '单次任务'),
|
||
('scheduled', '周期任务'),
|
||
]
|
||
|
||
TASK_STATUSES = [
|
||
('running', '运行中'), # 当前任务在运行
|
||
('generating', '生成中'), # 当前任务有报告正在生成
|
||
('error', '异常'), # 报告生成异常
|
||
('paused', '暂停'), # 任务暂停
|
||
]
|
||
|
||
title = models.CharField(max_length=255, verbose_name='任务标题')
|
||
description = models.TextField(blank=True, verbose_name='任务描述')
|
||
requirement = models.TextField(verbose_name='用户需求描述')
|
||
type = models.CharField(max_length=20, choices=TASK_TYPES, verbose_name='任务类型')
|
||
status = models.CharField(max_length=20, choices=TASK_STATUSES, default='running', verbose_name='任务状态')
|
||
|
||
# JSON字段存储配置
|
||
schedule_config = models.JSONField(null=True, blank=True, verbose_name='周期任务配置')
|
||
sources_config = models.JSONField(default=dict, verbose_name='信息源配置')
|
||
|
||
web_search_enabled = models.BooleanField(default=True, verbose_name='是否启用联网搜索')
|
||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户')
|
||
|
||
# 时间字段
|
||
last_report_time = models.DateTimeField(null=True, blank=True, verbose_name='最新报告生成时间')
|
||
next_run_time = models.DateTimeField(null=True, blank=True, verbose_name='下次执行时间')
|
||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||
|
||
class Meta:
|
||
db_table = 'tasks'
|
||
verbose_name = '任务'
|
||
verbose_name_plural = '任务'
|
||
ordering = ['-last_report_time', '-created_at']
|
||
indexes = [
|
||
models.Index(fields=['user', 'status']),
|
||
models.Index(fields=['last_report_time']),
|
||
models.Index(fields=['status']),
|
||
]
|
||
|
||
def __str__(self):
|
||
return f"{self.title} ({self.get_type_display()})"
|
||
|
||
@property
|
||
def report_count(self):
|
||
"""获取报告数量"""
|
||
return self.reports.count()
|
||
|
||
@property
|
||
def has_new_report(self):
|
||
"""是否有新报告(用于排序)"""
|
||
if not self.last_report_time:
|
||
return False
|
||
# 如果最新报告是24小时内生成的,认为是新报告
|
||
from django.utils import timezone
|
||
from datetime import timedelta
|
||
return timezone.now() - self.last_report_time < timedelta(hours=24)
|
||
|
||
|
||
class Report(models.Model):
|
||
"""报告模型"""
|
||
|
||
REPORT_STATUSES = [
|
||
('generating', '生成中'),
|
||
('completed', '已完成'),
|
||
('failed', '失败'),
|
||
]
|
||
|
||
task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name='reports', verbose_name='任务')
|
||
title = models.CharField(max_length=255, verbose_name='报告标题')
|
||
summary = models.TextField(blank=True, verbose_name='报告摘要')
|
||
content = models.TextField(verbose_name='报告内容')
|
||
source_tag = models.CharField(max_length=100, blank=True, verbose_name='信息来源标签')
|
||
word_count = models.IntegerField(default=0, verbose_name='字数统计')
|
||
status = models.CharField(max_length=20, choices=REPORT_STATUSES, default='generating', verbose_name='生成状态')
|
||
|
||
generated_at = models.DateTimeField(null=True, blank=True, verbose_name='生成完成时间')
|
||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||
|
||
class Meta:
|
||
db_table = 'reports'
|
||
verbose_name = '报告'
|
||
verbose_name_plural = '报告'
|
||
ordering = ['-generated_at', '-created_at']
|
||
indexes = [
|
||
models.Index(fields=['task', 'generated_at']),
|
||
models.Index(fields=['status']),
|
||
models.Index(fields=['generated_at']),
|
||
]
|
||
|
||
def __str__(self):
|
||
return f"{self.title} - {self.task.title}"
|
||
|
||
def save(self, *args, **kwargs):
|
||
# 自动计算字数
|
||
if self.content:
|
||
self.word_count = len(self.content.replace(' ', '').replace('\n', ''))
|
||
|
||
# 如果状态变为完成,更新生成时间和任务的最新报告时间
|
||
if self.status == 'completed' and not self.generated_at:
|
||
from django.utils import timezone
|
||
self.generated_at = timezone.now()
|
||
self.task.last_report_time = self.generated_at
|
||
self.task.save(update_fields=['last_report_time'])
|
||
|
||
super().save(*args, **kwargs)
|
||
|
||
|
||
class ReportSource(models.Model):
|
||
"""报告来源数据模型"""
|
||
|
||
SOURCE_TYPES = [
|
||
('wechat', '微信'),
|
||
('official-account', '公众号'),
|
||
('feishu', '飞书'),
|
||
('dingtalk', '钉钉'),
|
||
('email', '邮箱'),
|
||
('website', '网页'),
|
||
('baidu-pan', '百度网盘'),
|
||
('web-search', '联网搜索'),
|
||
]
|
||
|
||
report = models.ForeignKey(Report, on_delete=models.CASCADE, related_name='sources', verbose_name='报告')
|
||
source_type = models.CharField(max_length=20, choices=SOURCE_TYPES, verbose_name='信息源类型')
|
||
source_name = models.CharField(max_length=255, verbose_name='信息源名称')
|
||
source_url = models.URLField(blank=True, verbose_name='原始链接')
|
||
raw_content = models.TextField(blank=True, verbose_name='原始内容')
|
||
extracted_content = models.TextField(blank=True, verbose_name='提取的关键内容')
|
||
collected_at = models.DateTimeField(auto_now_add=True, verbose_name='采集时间')
|
||
|
||
class Meta:
|
||
db_table = 'report_sources'
|
||
verbose_name = '报告来源数据'
|
||
verbose_name_plural = '报告来源数据'
|
||
indexes = [
|
||
models.Index(fields=['report']),
|
||
models.Index(fields=['source_type']),
|
||
]
|
||
|
||
def __str__(self):
|
||
return f"{self.source_name} - {self.report.title}"
|
||
|
||
|
||
class TaskLog(models.Model):
|
||
"""任务执行日志模型"""
|
||
|
||
ACTION_TYPES = [
|
||
('created', '创建'),
|
||
('started', '开始'),
|
||
('paused', '暂停'),
|
||
('resumed', '恢复'),
|
||
('completed', '完成'),
|
||
('error', '错误'),
|
||
]
|
||
|
||
task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name='logs', verbose_name='任务')
|
||
action_type = models.CharField(max_length=20, choices=ACTION_TYPES, verbose_name='操作类型')
|
||
message = models.TextField(blank=True, verbose_name='日志信息')
|
||
error_details = models.JSONField(null=True, blank=True, verbose_name='错误详情')
|
||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||
|
||
class Meta:
|
||
db_table = 'task_logs'
|
||
verbose_name = '任务执行日志'
|
||
verbose_name_plural = '任务执行日志'
|
||
ordering = ['-created_at']
|
||
indexes = [
|
||
models.Index(fields=['task', 'created_at']),
|
||
models.Index(fields=['action_type']),
|
||
]
|
||
|
||
def __str__(self):
|
||
return f"{self.task.title} - {self.get_action_type_display()}" |