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()}"
|