1. 概述
在 Reddit 发帖有点像掷骰子——有时候一篇内容平平的帖子能爆火,而另一篇精心准备的却无人问津。有没有可能在发布后早期监控帖子表现,一旦发现反响不佳,就立即删除并重新发布?
本文是 Spring 构建 Reddit 应用案例 的延续,我们将实现一个实用功能:当帖子在指定时间内未达到预期互动量时,自动删除并重新提交。
核心逻辑很简单:允许用户配置两个关键参数——
✅ 在多长时间内(分钟)观察帖子表现
✅ 至少需要多少投票(score)才算“有 traction”
如果未达标,则触发删除 + 重发机制,相当于给帖子“续命”。
2. 申请额外的 Reddit 权限
要实现删除和编辑帖子,我们需要向 Reddit API 申请 edit
权限。
在 OAuth2 配置中,为 Reddit 资源添加 edit
scope:
@Bean
public OAuth2ProtectedResourceDetails reddit() {
AuthorizationCodeResourceDetails details =
new AuthorizationCodeResourceDetails();
details.setScope(Arrays.asList("identity", "read", "submit", "edit"));
// 其他配置...
return details;
}
⚠️ 注意:少了 edit
权限,后续删除接口会返回 403。
3. 实体类与数据访问层
3.1 扩展 Post 实体
在原有 Post
实体基础上,新增几个字段用于控制重试逻辑:
@Entity
public class Post {
// ... 其他字段
private String redditID; // Reddit 上的帖子唯一ID(如:abc123)
private int noOfAttempts; // 最大重试次数
private int timeInterval; // 观察期(单位:分钟)
private int minScoreRequired; // 最低达标分数
}
字段说明:
字段 | 说明 |
---|---|
redditID |
用于调用 Reddit API 查询分数或删除帖子 |
noOfAttempts |
比如设为 3,表示最多尝试删除重发 3 次 |
timeInterval |
比如设为 60,表示发出去后 1 小时内看表现 |
minScoreRequired |
比如设为 5,表示 1 小时内没到 5 票就重发 |
3.2 扩展 PostRepository
我们需要能快速查出“正在监控中”的帖子,因此在 Repository 中添加自定义查询方法:
public interface PostRepository extends JpaRepository<Post, Long> {
// 查找已发送但未超时的帖子(用于定时任务扫描)
List<Post> findBySubmissionDateBeforeAndIsSent(Date date, boolean sent);
// 用户个人历史记录
List<Post> findByUser(User user);
// 核心:查找已发布到 Reddit 且还有重试机会的帖子
List<Post> findByRedditIDNotNullAndNoOfAttemptsGreaterThan(int attempts);
}
✅ findByRedditIDNotNullAndNoOfAttemptsGreaterThan(0)
是定时任务的核心查询,确保只处理“可抢救”的帖子。
4. 定时任务:检查并重发
使用 @Scheduled
定义一个每 3 分钟执行一次的任务:
@Scheduled(fixedRate = 3 * 60 * 1000)
public void checkAndReSubmitPosts() {
List<Post> submitted =
postRepository.findByRedditIDNotNullAndNoOfAttemptsGreaterThan(0);
for (Post post : submitted) {
checkAndReSubmit(post);
}
}
4.1 checkAndReSubmit 方法实现
private void checkAndReSubmit(Post post) {
try {
checkAndReSubmitInternal(post);
} catch (final Exception e) {
logger.error("Error occurred while checking post " + post.toString(), e);
}
}
private void checkAndReSubmitInternal(Post post) {
// 判断是否已超过观察期
if (didIntervalPassed(post.getSubmissionDate(), post.getTimeInterval())) {
int score = getPostScore(post.getRedditID());
if (score < post.getMinScoreRequired()) {
// 未达标:删除 + 重置状态
deletePost(post.getRedditID());
resetPost(post);
} else {
// 达标:清空重试次数,不再处理
post.setNoOfAttempts(0);
postRepository.save(post);
}
}
}
// 判断当前时间是否已超过“发布时间 + 观察期”
private boolean didIntervalPassed(Date submissionDate, int intervalInMinutes) {
long currentTime = new Date().getTime();
long elapsed = currentTime - submissionDate.getTime();
long elapsedInMinutes = TimeUnit.MINUTES.convert(elapsed, TimeUnit.MILLISECONDS);
return elapsedInMinutes > intervalInMinutes;
}
// 重置帖子状态,准备重新提交
private void resetPost(Post post) {
long nextSubmitTime = new Date().getTime();
nextSubmitTime += TimeUnit.MILLISECONDS.convert(post.getTimeInterval(), TimeUnit.MINUTES);
post.setRedditID(null); // 清除旧ID
post.setSubmissionDate(new Date(nextSubmitTime));
post.setSent(false);
post.setSubmissionResponse("Not sent yet");
postRepository.save(post); // 状态持久化
}
⚠️ 踩坑提醒:submissionDate
是原始发布时间,不是当前时间。判断超时必须基于它 + timeInterval
。
5. 获取 Reddit 帖子当前得分
通过 Reddit 的 api/info
接口查询帖子详情:
private int getPostScore(String redditId) {
JsonNode node = redditRestTemplate.getForObject(
"https://oauth.reddit.com/api/info?id=t3_" + redditId, JsonNode.class);
int score = node.get("data")
.get("children")
.get(0)
.get("data")
.get("score")
.asInt();
return score;
}
📌 注意点:
- 需要
read
权限 - Reddit 的 full name 规则:帖子 ID 前缀为
t3_
,例如原始 ID 是abc123
,完整 ID 就是t3_abc123
6. 删除 Reddit 帖子
调用 Reddit 的 api/del
接口删除帖子:
private void deletePost(String redditId) {
MultiValueMap<String, String> param = new LinkedMultiValueMap<>();
param.add("id", "t3_" + redditId);
redditRestTemplate.postForObject(
"https://oauth.reddit.com/api/del.json", param, JsonNode.class);
}
✅ 简单粗暴,POST 提交 id=t3_xxx
即可。
⚠️ 注意返回值可能包含错误信息,生产环境建议做错误解析。
7. 控制器层:接收重试配置
在 RedditController
中接收前端传来的重试策略参数:
@RequestMapping(value = "/schedule", method = RequestMethod.POST)
public String schedule(Model model,
@RequestParam Map<String, String> formParams) throws ParseException {
Post post = new Post();
post.setTitle(formParams.get("title"));
post.setSubreddit(formParams.get("sr"));
post.setUrl(formParams.get("url"));
// 接收重试策略
post.setNoOfAttempts(Integer.parseInt(formParams.get("attempt")));
post.setTimeInterval(Integer.parseInt(formParams.get("interval")));
post.setMinScoreRequired(Integer.parseInt(formParams.get("score")));
// 其他业务逻辑...
return "redirect:/dashboard";
}
8. 前端界面:配置重试规则
在发布表单中加入重试策略选项:
<label class="col-sm-3">重发策略</label>
<label>重试次数</label>
<select name="attempt">
<option value="0" selected>不重试</option>
<option value="2">最多2次</option>
<option value="3">最多3次</option>
<option value="4">最多4次</option>
<option value="5">最多5次</option>
</select>
<label>观察期</label>
<select name="interval">
<option value="0" selected>不监控</option>
<option value="45">45分钟</option>
<option value="60">1小时</option>
<option value="120">2小时</option>
</select>
<label>最低达标分数</label>
<input type="number" value="0" name="score" required/>
📌 用户体验建议:
- 默认
attempt=0
表示关闭该功能 score
可设为 0,表示“只要有投票就不重发”
9. 总结
我们在这个小项目中又迈出了一步:
✅ 不仅能自动发布到 Reddit
✅ 还能智能监控早期表现,对“冷启动失败”的帖子进行自动删除 + 重发
这相当于给发布系统加了一层“容错机制”,尤其适合那些对曝光敏感的内容运营场景。
💡 扩展思路:
- 可加入指数退避重试(如第一次等1小时,第二次等2小时)
- 可结合 subreddit 的活跃时间段智能调度
- 可记录每次重发后的 score 变化,做数据分析
这个功能虽小,但体现了“自动化 + 数据反馈”的闭环思维,值得在类似内容分发系统中借鉴。