package com.wecom.robot.util; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; @Slf4j @Component @RequiredArgsConstructor public class IdempotentHelper { private static final String KEY_PREFIX = "idempotent:"; private static final long DEFAULT_TTL_HOURS = 1; private final StringRedisTemplate redisTemplate; public boolean processMessageIdempotent(String channelMessageId, Runnable processor) { String key = KEY_PREFIX + channelMessageId; Boolean absent = redisTemplate.opsForValue() .setIfAbsent(key, "1", DEFAULT_TTL_HOURS, TimeUnit.HOURS); if (Boolean.TRUE.equals(absent)) { processor.run(); return true; } log.info("[AC-MCA-11-IDEMPOTENT] 重复消息,跳过处理: channelMessageId={}", channelMessageId); return false; } public boolean checkAndSet(String channelMessageId) { String key = KEY_PREFIX + channelMessageId; Boolean absent = redisTemplate.opsForValue() .setIfAbsent(key, "1", DEFAULT_TTL_HOURS, TimeUnit.HOURS); if (Boolean.TRUE.equals(absent)) { return true; } log.info("[AC-MCA-11-IDEMPOTENT] 重复消息检测: channelMessageId={}", channelMessageId); return false; } public boolean exists(String channelMessageId) { String key = KEY_PREFIX + channelMessageId; return Boolean.TRUE.equals(redisTemplate.hasKey(key)); } public void remove(String channelMessageId) { String key = KEY_PREFIX + channelMessageId; redisTemplate.delete(key); } }