这次做了两部分的内容,一个是对于管理员用户,用户管理和邮件部分的设置,另一部分是对于每日帖子自动生成这个板块的初步实现

一  管理端:用户管理与邮件模板设置

本文记录管理员端两块能力:

  • 用户管理(禁用/启用、会话失效)
  • 邮件模板设置(注册验证码 / 找回密码验证码 / 绑定邮箱验证码)

1. 目标与边界

目标:

  • 管理员可统一管理用户状态,快速处理异常账号
  • 管理员可按场景分别配置验证码邮件内容
  • 模板支持占位符,避免每次改代码发版

边界:

  • 当前仅支持验证码类模板,不包含营销/通知类模板
  • 模板变量目前固定为 {{purpose}}、{{code}}、{{minutes}}

2 .用户管理(Admin Users)

前端入口:

  • 路由:/admin/users
  • 页面:AdminUsersPage

后端接口:

  • GET /api/v1/admin/users
  • PATCH /api/v1/admin/users/{userId}(更新 status)
  • POST /api/v1/admin/users/{userId}/revoke-tokens(使 token 失效)

权限模型:

  • 依赖网关注入 X-User-Roles
  • ADMIN 才可访问上述接口

3 .邮件模板设置(Admin Mail Templates)

前端入口:

  • 路由:/admin/settings/mail
  • 页面:AdminMailSettingsPage

后端接口:

  • GET /api/v1/admin/settings/mail-templates/types
  • GET /api/v1/admin/settings/mail-templates/{templateType}
  • PUT /api/v1/admin/settings/mail-templates/{templateType}

支持的模板类型:

  • REGISTER_VERIFICATION
  • PASSWORD_RESET_VERIFICATION
  • BIND_EMAIL_VERIFICATION

模板存储:

  • 表:pf_mail_template
  • Flyway新增脚本:
    • V6__mail_template_settings.sql(建表 + 兼容旧模板)
    • V7__mail_template_multi_types.sql(新增三类模板初始数据)

4 .发送链路如何命中对应模板

验证码发信时,后端会按业务场景传入模板类型:

  • 注册验证码:REGISTER_VERIFICATION
  • 找回密码验证码:PASSWORD_RESET_VERIFICATION
  • 绑定邮箱验证码:BIND_EMAIL_VERIFICATION

MailService 通过 MailTemplateService 渲染标题和正文,再交给 Spring Mail 发送。

5 .运维与排查建议

  • 若管理页更新模板后无效果,先确认当前环境 PF_MAIL_ENABLED=true
  • 若启动报 Flyway checksum mismatch,不要修改已执行版本,新增更高版本迁移
  • 模板文本建议保留占位符,避免发出无验证码的无效邮件

二  内容服务:每日帖子自动生成(Scheduler)

1. 功能目标

  • 每天自动生成一条“每日更新”帖子,作为前端 /posts 页面的内容来源
  • 当前实现为占位版本(便于先跑通端到端闭环)
  • 后续可替换为 Curator/Editor Agent 产出的内容(网关转发下游 Agent,不在本次范围)

2 .端到端行为

  1. 定时任务触发(默认每天 UTC 09:00)
  2. 检查当天是否已有帖子
  3. 若没有,写入一条新帖子(source=scheduler)

3 .关键代码原文 + 解读

代码位置:[DailyPostJob.java](file:///f:/Gitee/PaperFlow/PaperFlow/backend/services/content-service/src/main/java/com/paperflow/content/job/DailyPostJob.java)

@Component

public class DailyPostJob {

  private final PostRepository posts;

  public DailyPostJob(PostRepository posts) {

    this.posts = posts;

  }

  @Scheduled(cron = "0 0 9 * * *")

  public void ensureDailyPost() {

    OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);

    OffsetDateTime start = now.truncatedTo(ChronoUnit.DAYS);

    OffsetDateTime end = start.plusDays(1);

    if (posts.existsByPublishedAtBetween(start, end)) {

      return;

    }

    PostEntity p = new PostEntity();

    p.setId("post_" + UUID.randomUUID().toString().replace("-", ""));

    p.setTitle("Daily Update " + start.toLocalDate());

    p.setContent("This is an auto-generated daily post placeholder.");

    p.setSource("scheduler");

    p.setPublishedAt(now);

    posts.save(p);

  }

}

逐段解释:

  • @Scheduled(cron = "0 0 9 * * *"):每天 09:00 触发一次(当前以服务端进程时间为准)。
  • OffsetDateTime.now(ZoneOffset.UTC):
    • 明确以 UTC 作为“每日”划分基准,避免部署到不同时区后“日界线错乱”。
  • start/end:
    • 用“当天 00:00:00”到“次日 00:00:00”定义当天范围;
    • 配合 existsByPublishedAtBetween 判断当天是否已生成过帖子。
  • 生成帖子:
    • id 外部可用稳定字符串,不暴露自增主键;
    • source=scheduler 方便追踪帖子来源(未来会出现 curator_push、editor_digest 等)。

另外,现在是用 ZoneOffset.UTC 写死时间。但 服务器时区必须与 UTC 对齐,否则 cron 表达式仍会以本地时区执行,导致每日帖子生成时间与预期不符

4 演进方向

  • 幂等增强:把“每日唯一性”改成数据库唯一约束(例如 published_date 唯一),更抗并发/重启
  • 内容生成替换:
    • 由 Curator 选题 → Editor 生成摘要/配图 → content-service 入库
    • 调用通过网关转发到 Agent 服务

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐