CollabEventPublisher.java
package com.yumu.noveltranslator.domain.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yumu.noveltranslator.util.SseEmitterUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 协作事件发布器
*
* 将协作相关的事件写入 Redis Stream,供 SSE 重放机制消费。
* 所有方法都应在事务提交后调用,确保数据库写入先于事件发布。
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class CollabEventPublisher {
private final SseEmitterUtil sseEmitterUtil;
private final ObjectMapper objectMapper;
/**
* 发布章节状态变更事件
*
* @param projectId 项目ID
* @param chapterId 章节ID
* @param userId 操作用户ID
* @param action 操作类型: "updated", "submitted", "assigned"
*/
public void publishChapterUpdate(Long projectId, Long chapterId, String userId, String action) {
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("chapterId", chapterId);
payload.put("userId", userId);
payload.put("action", action);
String json;
try {
json = objectMapper.writeValueAsString(payload);
} catch (JsonProcessingException e) {
log.error("序列化章节事件失败: projectId={}, chapterId={}", projectId, chapterId, e);
return;
}
sseEmitterUtil.publishCollabEvent(String.valueOf(projectId), "chapter." + action, json);
}
/**
* 发布章节评论事件
*
* @param projectId 项目ID
* @param chapterTaskId 章节任务ID
* @param userId 评论用户ID
* @param content 评论内容
*/
public void publishCommentAdded(Long projectId, Long chapterTaskId, Long userId, String content) {
Map<String, Object> payload = new LinkedHashMap<>();
payload.put("chapterTaskId", chapterTaskId);
payload.put("userId", userId);
payload.put("content", content);
String json;
try {
json = objectMapper.writeValueAsString(payload);
} catch (JsonProcessingException e) {
log.error("序列化评论事件失败: projectId={}, chapterTaskId={}", projectId, chapterTaskId, e);
return;
}
sseEmitterUtil.publishCollabEvent(String.valueOf(projectId), "comment.added", json);
}
}