Topic.java
6.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
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));
}
}