title: 24小时留言板
date: 2025-06-25 23:32:53
tags: 代码工具
24小时留言板
核心效果如图所示
代码解析
# TodoController 代码解析## 整体架构
这是一个基于Spring WebFlux的响应式控制器,结合Redis发布\订阅机制实现实时更新的待办事项系统。关键组件包括:
- **TodoRepo**:数据访问层接口
- **ReactiveRedisTemplate**:Redis响应式操作模板
- **ChannelTopic**:Redis消息通道
- **Sinks.Many**:Project Reactor的事件分发器## 核心功能解析### 1. Redis发布\订阅初始化
```java
public TodoController(TodoRepo repo, ReactiveRedisTemplate<String, String> redisTemplate) {this.repo = repo;this.redisTemplate = redisTemplate;redisTemplate.listenTo(todoTopic).map(msg -> msg.getMessage()).filter(message -> !StringUtils.isEmpty(message)).subscribe(eventSink::tryEmitNext);
}
- 功能:控制器启动时自动订阅Redis频道
- 处理流程:
- 监听
todo_events
频道 - 提取消息内容
- 过滤空消息
- 将消息推送到事件流(Sinks)
- 监听
2. 事件广播机制
private void broadcastEvent(String eventType, Todo todo) {String messageId = "global_" + todo.getId() + "_" + System.currentTimeMillis();String payload = "";\\ 事件类型处理switch (eventType) {case "create":case "update":payload = String.format("id:%s\nevent:%s\ndata:%s\n\n",messageId, eventType, renderItem(todo));break;case "delete":payload = String.format("id:%s\nevent:delete\ndata:%d\n\n",messageId, todo.getId());break;}\\ Redis发布if (StringUtils.hasLength(payload)) {redisTemplate.convertAndSend(todoTopic.getTopic(), payload).subscribe();}
}
- 事件格式:符合SSE规范,包含:
id
:全局唯一事件ID(含时间戳)event
:事件类型(create\update\delete)data
:有效载荷
- 安全设计:广播前检查消息有效性
3. SSE数据流接口
@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> list() {Flux<String> initialFlux = repo.findAllByOrderByIdDesc().map(todo -> String.format("id:init_%d\nevent:create\ndata:%s\n\n",todo.getId(), renderItem(todo)));return Flux.merge(initialFlux, eventSink.asFlux()).filter(payload -> !StringUtils.isEmpty(payload));
}
- 实现策略:
- 加载初始数据并转换为SSE格式(init前缀)
- 合并实时事件流(Redis订阅)
- 过滤空消息
- SSE规范:
id: [event_id]
event: [event_type]
data: [payload]
- 消息以双换行结束
4. CRUD操作实现
创建待办事项
@PostMapping
public Mono<String> create(@ModelAttribute Form form) {return repo.save(new Todo(null, HtmlUtils.htmlEscape(form.getContent()))).doOnSuccess(todo -> broadcastEvent("create", todo)).thenReturn("");
}
- 安全特性:HTML内容转义防御XSS
- 响应策略:空字符串实现PRG(Post-Redirect-Get)模式
删除待办事项
@PostMapping(path = "\{id}\delete", produces = MediaType.TEXT_HTML_VALUE)
public Mono<String> delete(@PathVariable Long id) {return repo.findById(id).doOnSuccess(todo -> broadcastEvent("delete", todo)).then(repo.deleteById(id)).thenReturn("");
}
- 关键流程:
- 先查询再删除(确保广播正确的todo数据)
- 广播删除事件
- 执行删除操作
5. 视图渲染引擎
查看模式渲染
private String renderItem(Todo t) {return """<li id="todo-%d" class="message-item"><div class="message-content">%s<div class="message-extra"><div class="message-time"><i class="far fa-clock"><\i> %s<\div><\div><\div><div class="message-actions"><button class="delete-btn" onclick="handleDelete(%d)"><i class="fas fa-trash-alt"><\i><\button><\div><\li>""";
}
- 元素结构:
- 唯一ID绑定:
todo-{id}
- 内容显示区
- 时间戳(当前服务器时间)
- 删除按钮(绑定JavaScript操作)
- 唯一ID绑定:
编辑模式渲染(未使用)
private String renderItemEditing(Todo t) {\\ 包含表单和按钮
}
- 潜在扩展点:提供编辑功能时可复用
6. 安全机制
\\ 输入转义
HtmlUtils.htmlEscape(form.getContent())\\ 输出转义
private String escapeHtml(String s) {return HtmlUtils.htmlEscape(s);
}
- 双保险策略:输入输出双重转义防御XSS攻击
- 规范实践:使用Spring内置HtmlUtils工具类
设计亮点
-
响应式架构:
- Flux\Mono异步处理
- 背压支持(onBackpressureBuffer)
-
实时广播系统:
- Redis发布\订阅
- 多级事件分发(Sinks->Flux)
-
客户端驱动更新:
- HTML片段直接注入页面
- 无JavaScript框架依赖
-
优雅降级策略:
- 事件ID前缀区分初始化\实时事件
- 空消息过滤保证稳定性
-
扩展性设计:
- 事件类型支持(create\update\delete)
- 预留编辑模式渲染器
优化建议
-
时间戳改进:
\\ 当前使用服务器当前时间 LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))\\ 建议:使用Todo创建时间 t.getCreatedAt().format(DateTimeFormatter.ofPattern("HH:mm:ss"))
-
事件完整性保证:
\\ 增加错误处理 .doOnError(e -> log.error("Event broadcast failed", e))
-
集群支持增强:
\\ 当前事件ID格式 "global_" + todo.getId() + "_" + System.currentTimeMillis()\\ 建议增加实例标识 "node1_" + todo.getId() + "_" + System.currentTimeMillis()
-
编辑功能集成:
可启用预留的renderItemEditing
方法实现编辑功能 -
自动清理机制:
增加定时任务清理24小时前的待办事项
该系统实现了基于响应式编程和Redis发布\订阅的高效实时应用,兼顾了性能和安全,代码结构清晰且扩展性强,是实时应用开发的优秀范例。
## GITHUb仓库
https:\\github.com\qingyun201908\HTMXANDSpringWebflux