RecommendationSourceVersionService.java

package org.darkroomlibrary.service.support;

import jakarta.annotation.Resource;
import org.darkroomlibrary.infrastructure.cache.CacheService;
import org.darkroomlibrary.utils.TransactionCallbacks;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Optional;
import java.util.UUID;

@Service
public class RecommendationSourceVersionService {

    private static final String GLOBAL_KEY = "recommendation:source:global";
    private static final String USER_KEY_PREFIX = "recommendation:source:user:";
    private static final Duration VERSION_TTL = Duration.ofDays(3650);

    @Resource
    private CacheService cacheService;

    public Optional<String> currentSeed(Integer userId) {
        if (userId == null) return Optional.empty();
        Optional<String> globalVersion = getOrCreate(GLOBAL_KEY);
        if (globalVersion.isEmpty()) return Optional.empty();
        Optional<String> userVersion = getOrCreate(userKey(userId));
        return userVersion.map(value -> globalVersion.get() + '|' + value);
    }

    public void invalidateGlobalAfterCommit() {
        afterCommit(GLOBAL_KEY);
    }

    public void invalidateUserAfterCommit(Integer userId) {
        if (userId != null) afterCommit(userKey(userId));
    }

    public void invalidateUserAndGlobalAfterCommit(Integer userId) {
        TransactionCallbacks.afterCommit(() -> {
            cacheService.delete(GLOBAL_KEY);
            if (userId != null) cacheService.delete(userKey(userId));
        });
    }

    private Optional<String> getOrCreate(String key) {
        Optional<String> existing = cacheService.getString(key);
        if (existing.isPresent()) return existing;
        String candidate = UUID.randomUUID().toString();
        Optional<Boolean> stored = cacheService.setIfAbsent(key, candidate, VERSION_TTL);
        if (stored.isEmpty()) return Optional.empty();
        return Boolean.TRUE.equals(stored.get())
                ? Optional.of(candidate) : cacheService.getString(key);
    }

    private void afterCommit(String key) {
        TransactionCallbacks.afterCommit(() -> cacheService.delete(key));
    }

    private String userKey(Integer userId) {
        return USER_KEY_PREFIX + userId;
    }
}