Topic.java 6.2 KB
package com.aigeo.keyword.entity;

import com.aigeo.common.enums.KeywordSource;
import com.aigeo.common.enums.TopicStatus;
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_topics
 * 
 * 用于存储内容生成的话题信息,包括:
 * - 话题标题和描述
 * - 来源信息和抓取数据
 * - 处理状态和质量评级
 * - 相关关键词和分类标签
 *
 * @author AIGEO Team
 * @since 1.0.0
 */
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "ai_topics", indexes = {
    @Index(name = "idx_topics_company_status", columnList = "company_id, status"),
    @Index(name = "idx_topics_source_task", columnList = "source_task_id"),
    @Index(name = "idx_topics_source", columnList = "source")
})
public class Topic {
    
    /**
     * 主键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
     */
    @Column(name = "source_task_id")
    private Integer sourceTaskId;

    /**
     * 话题标题
     */
    @NotBlank(message = "话题标题不能为空")
    @Column(name = "title", nullable = false, length = 500)
    private String title;

    /**
     * 话题描述
     */
    @Column(name = "description", length = 2000)
    private String description;

    /**
     * 来源URL
     */
    @Column(name = "source_url", length = 500)
    private String sourceUrl;

    /**
     * 来源类型
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "source")
    @Builder.Default
    private KeywordSource source = KeywordSource.MANUAL;

    /**
     * 话题状态
     */
    @Enumerated(EnumType.STRING)
    @Column(name = "status")
    @Builder.Default
    private TopicStatus status = TopicStatus.RAW;

    /**
     * 相关关键词(逗号分隔)
     */
    @Column(name = "keywords", length = 1000)
    private String keywords;

    /**
     * 话题标签(逗号分隔)
     */
    @Column(name = "tags", length = 500)
    private String tags;

    /**
     * 质量评分(1-10分)
     */
    @Column(name = "quality_score")
    private Integer qualityScore;

    /**
     * 热度评分(基于搜索量、社交媒体热度等)
     */
    @Column(name = "trending_score")
    private Double trendingScore;

    /**
     * 内容长度(字符数)
     */
    @Column(name = "content_length")
    private Integer contentLength;

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

    /**
     * 话题分类
     */
    @Column(name = "category", length = 100)
    private String category;

    /**
     * 是否为原创话题
     */
    @Column(name = "is_original")
    @Builder.Default
    private Boolean isOriginal = false;

    /**
     * 使用次数统计
     */
    @Column(name = "usage_count")
    @Builder.Default
    private Integer usageCount = 0;

    /**
     * 最后使用时间
     */
    @Column(name = "last_used_at")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime lastUsedAt;

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

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

    /**
     * 实体创建前的处理
     */
    @PrePersist
    protected void onCreate() {
        if (source == null) source = KeywordSource.MANUAL;
        if (status == null) status = TopicStatus.RAW;
        if (isOriginal == null) isOriginal = false;
        if (usageCount == null) usageCount = 0;
        if (language == null) language = "zh-CN";
    }

    /**
     * 检查是否为高质量话题
     */
    public boolean isHighQuality() {
        return qualityScore != null && qualityScore >= 8;
    }

    /**
     * 检查是否为热门话题
     */
    public boolean isTrending() {
        return trendingScore != null && trendingScore >= 7.0;
    }

    /**
     * 检查是否适合生成内容
     */
    public boolean isSuitableForGeneration() {
        return status == TopicStatus.CURATED && 
               title != null && !title.trim().isEmpty() &&
               (qualityScore == null || qualityScore >= 5);
    }

    /**
     * 获取话题类型(基于来源)
     */
    public String getTopicType() {
        if (source == KeywordSource.GOOGLE || source == KeywordSource.BAIDU) {
            return "搜索热点";
        } else if (sourceUrl != null && sourceUrl.contains("news")) {
            return "新闻资讯";
        } else if (Boolean.TRUE.equals(isOriginal)) {
            return "原创话题";
        } else {
            return "一般话题";
        }
    }

    /**
     * 增加使用次数
     */
    public void incrementUsageCount() {
        this.usageCount = (usageCount == null ? 0 : usageCount) + 1;
        this.lastUsedAt = LocalDateTime.now();
    }

    /**
     * 获取话题摘要(限制长度)
     */
    public String getSummary(int maxLength) {
        if (description == null) return title;
        
        String text = description.length() > maxLength ? 
                      description.substring(0, maxLength) + "..." : 
                      description;
        return text;
    }

    /**
     * 检查话题是否过期(超过30天未更新)
     */
    public boolean isStale() {
        if (updatedAt == null) return true;
        return updatedAt.isBefore(LocalDateTime.now().minusDays(30));
    }
}