场景
站点同步场景,如果判断是该站点所属的运营商是新增的,则需要针对该运营商,给运营人员发送一次邮件通知,每天仅需发送即可。次日凌晨,清除当前邮件已发送的标记。
方案
- 利用setIfAbsent(如果不存在,则写入并返回true,否则不写并返回false)
- 利用scan,返回全部新增运营商的KEY,便于批量删除邮件已发送的标记
代码示例
package tech.foolfish.demo.service;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
@Service
public class EmailService {
private static final String REDIS_KEY_EMAIL_SENT_EXCEPTIONAL_OPERATOR = "EMAIL_SENT_EXCEPTIONAL_OPERATOR:";
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 统计周期内,未发送过邮件
*
* @param operatorId
* @return
*/
public boolean noEmailSent(String operatorId) {
return redisTemplate.opsForValue().setIfAbsent(REDIS_KEY_EMAIL_SENT_EXCEPTIONAL_OPERATOR + operatorId, "true");
}
/**
* 统计周期内,发送过邮件
*
* @param operatorId
* @return
*/
public boolean emailSent(String operatorId) {
return !noEmailSent(operatorId);
}
/**
* 统计周期内,打标记:该运营商刚发送过一次邮件
*
* @param operatorId
*/
public void markEmailSent(String operatorId) {
redisTemplate.opsForValue().setIfAbsent(REDIS_KEY_EMAIL_SENT_EXCEPTIONAL_OPERATOR + operatorId, "true");
}
/**
* 统计周期内,清除标记
*/
public void clearMarkOfEmailSent() {
String redisKey = REDIS_KEY_EMAIL_SENT_EXCEPTIONAL_OPERATOR + "*";
deleteKey(redisKey);
}
/**
* 模糊清除KEY
*
* @param pattern
*/
private void deleteKey(String pattern) {
List<String> keys = getKeys(pattern);
if (CollectionUtils.isEmpty(keys)) {
return;
}
redisTemplate.delete(keys);
}
private List<String> getKeys(String pattern) {
List<String> keys = new ArrayList<>();
this.scan(pattern, item -> {
String key = new String(item, StandardCharsets.UTF_8);
keys.add(key);
});
return keys;
}
private void scan(String pattern, Consumer<byte[]> consumer) {
redisTemplate.execute((RedisConnection connection) -> {
try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().count(Long.MAX_VALUE).match(pattern).build())) {
cursor.forEachRemaining(consumer);
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
}