ArticleGenerationTask.java 8.5 KB
package com.aigeo.article.entity;

import com.aigeo.common.enums.TaskStatus;
import com.aigeo.common.enums.AiTasteLevel;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;

/**
 * 文章生成任务实体类
 * 对应数据库表:ai_article_generation_tasks
 * 
 * 用于存储AI文章生成任务信息,包括:
 * - 任务配置和参数设置
 * - 执行状态和进度跟踪
 * - 参考资料和知识来源
 * - 生成结果和错误信息
 *
 * @author AIGEO Team
 * @since 1.0.0
 */
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "ai_article_generation_tasks", indexes = {
    @Index(name = "idx_tasks_company_status", columnList = "company_id, status"),
    @Index(name = "idx_tasks_user_id", columnList = "user_id"),
    @Index(name = "idx_tasks_created_at", columnList = "created_at DESC")
})
public class ArticleGenerationTask {
    
    /**
     * 主键ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false, updatable = false)
    private Integer id;

    /**
     * 公司ID(多租户字段)
     */
    @NotNull(message = "公司ID不能为空")
    @Column(name = "company_id", nullable = false)
    private Integer companyId;

    /**
     * 创建用户ID
     */
    @NotNull(message = "用户ID不能为空")
    @Column(name = "user_id", nullable = false)
    private Integer userId;

    /**
     * 生成配置ID
     */
    @Column(name = "config_id")
    private Integer configId;

    /**
     * 关键词ID
     */
    @Column(name = "keyword_id")
    private Integer keywordId;

    /**
     * 文章主题
     */
    @NotBlank(message = "文章主题不能为空")
    @Column(name = "article_theme", nullable = false, length = 500)
    private String articleTheme;

    /**
     * 关联话题ID列表(逗号分隔)
     */
    @Column(name = "topic_ids", length = 500)
    private String topicIds;

    /**
     * 参考资料URL列表(逗号分隔)
     */
    @Column(name = "reference_urls", columnDefinition = "TEXT")
    private String referenceUrls;

    /**
     * 参考内容
     */
    @Column(name = "reference_content", columnDefinition = "LONGTEXT")
    private String referenceContent;

    /**
     * AI写作风格等级
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "ai_taste_level")
    @Builder.Default
    private AiTasteLevel aiTasteLevel = AiTasteLevel.JUNIOR_HIGH;

    /**
     * 任务状态
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "status")
    @Builder.Default
    private TaskStatus status = TaskStatus.PENDING;

    /**
     * 任务进度(0-100)
     */
    @Column(name = "progress")
    @Builder.Default
    private Integer progress = 0;

    /**
     * 错误信息
     */
    @Column(name = "error_message", length = 2000)
    private String errorMessage;

    /**
     * 生成的文章ID
     */
    @Column(name = "generated_article_id")
    private Integer generatedArticleId;

    /**
     * AI配置ID
     */
    @Column(name = "dify_api_config_id")
    private Integer difyApiConfigId;

    /**
     * 提示模板ID
     */
    @Column(name = "prompt_template_id")
    private Integer promptTemplateId;

    /**
     * 目标字数
     */
    @Column(name = "target_word_count")
    @Builder.Default
    private Integer targetWordCount = 1000;

    /**
     * 生成语言
     */
    @Column(name = "language", length = 10)
    @Builder.Default
    private String language = "zh-CN";

    /**
     * 是否包含FAQ
     */
    @Column(name = "include_faq")
    @Builder.Default
    private Boolean includeFaq = false;

    /**
     * 是否生成SEO数据
     */
    @Column(name = "generate_seo")
    @Builder.Default
    private Boolean generateSeo = true;

    /**
     * 执行时长(毫秒)
     */
    @Column(name = "execution_time_ms")
    private Long executionTimeMs;

    /**
     * 重试次数
     */
    @Column(name = "retry_count")
    @Builder.Default
    private Integer retryCount = 0;

    /**
     * 最大重试次数
     */
    @Column(name = "max_retries")
    @Builder.Default
    private Integer maxRetries = 3;

    /**
     * 任务优先级(1-10,数字越大优先级越高)
     */
    @Column(name = "priority")
    @Builder.Default
    private Integer priority = 5;

    /**
     * 创建时间
     */
    @CreationTimestamp
    @Column(name = "created_at", updatable = false)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createdAt;

    /**
     * 开始执行时间
     */
    @Column(name = "started_at")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime startedAt;

    /**
     * 完成时间
     */
    @Column(name = "completed_at")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime completedAt;

    /**
     * 更新时间
     */
    @UpdateTimestamp
    @Column(name = "updated_at")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updatedAt;

    /**
     * 实体创建前的处理
     */
    @PrePersist
    protected void onCreate() {
        if (status == null) status = TaskStatus.PENDING;
        if (progress == null) progress = 0;
        if (aiTasteLevel == null) aiTasteLevel = AiTasteLevel.JUNIOR_HIGH;
        if (targetWordCount == null) targetWordCount = 1000;
        if (language == null) language = "zh-CN";
        if (includeFaq == null) includeFaq = false;
        if (generateSeo == null) generateSeo = true;
        if (retryCount == null) retryCount = 0;
        if (maxRetries == null) maxRetries = 3;
        if (priority == null) priority = 5;
    }

    /**
     * 检查任务是否已完成
     */
    public boolean isCompleted() {
        return status == TaskStatus.COMPLETED || status == TaskStatus.FAILED || status == TaskStatus.CANCELLED;
    }

    /**
     * 检查任务是否正在运行
     */
    public boolean isRunning() {
        return status == TaskStatus.PROCESSING;
    }

    /**
     * 检查是否可以重试
     */
    public boolean canRetry() {
        return status == TaskStatus.FAILED && retryCount < maxRetries;
    }

    /**
     * 检查是否可以取消
     */
    public boolean isCancellable() {
        return status == TaskStatus.PENDING || status == TaskStatus.PROCESSING;
    }

    /**
     * 开始任务执行
     */
    public void start() {
        this.status = TaskStatus.PROCESSING;
        this.startedAt = LocalDateTime.now();
        this.progress = 0;
    }

    /**
     * 完成任务
     */
    public void complete(Integer articleId) {
        this.status = TaskStatus.COMPLETED;
        this.completedAt = LocalDateTime.now();
        this.progress = 100;
        this.generatedArticleId = articleId;
        
        if (startedAt != null) {
            this.executionTimeMs = java.time.Duration.between(startedAt, completedAt).toMillis();
        }
    }

    /**
     * 任务失败
     */
    public void fail(String errorMessage) {
        this.status = TaskStatus.FAILED;
        this.completedAt = LocalDateTime.now();
        this.errorMessage = errorMessage;
        
        if (startedAt != null) {
            this.executionTimeMs = java.time.Duration.between(startedAt, completedAt).toMillis();
        }
    }

    /**
     * 取消任务
     */
    public void cancel(String reason) {
        this.status = TaskStatus.CANCELLED;
        this.completedAt = LocalDateTime.now();
        this.errorMessage = "任务已取消: " + reason;
    }

    /**
     * 增加重试次数
     */
    public void incrementRetryCount() {
        this.retryCount = (retryCount == null ? 0 : retryCount) + 1;
    }

    /**
     * 更新进度
     */
    public void updateProgress(Integer progress) {
        if (progress >= 0 && progress <= 100) {
            this.progress = progress;
        }
    }

    /**
     * 获取任务执行时长(秒)
     */
    public Long getExecutionTimeSeconds() {
        if (executionTimeMs == null) return null;
        return executionTimeMs / 1000;
    }

    /**
     * 获取任务优先级描述
     */
    public String getPriorityDescription() {
        if (priority == null) return "普通";
        
        if (priority <= 3) return "低";
        if (priority <= 7) return "普通";
        return "高";
    }
}