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);
    }
}