Fix details

This commit is contained in:
2026-03-25 01:32:19 +05:00
parent 4f3d955879
commit 8cc40cd450
4 changed files with 78 additions and 24 deletions

View File

@@ -2,6 +2,7 @@ package com.backend.unifier.title.mapper;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.mapstruct.AfterMapping; import org.mapstruct.AfterMapping;
@@ -13,7 +14,6 @@ import org.mapstruct.MappingTarget;
import org.mapstruct.Named; import org.mapstruct.Named;
import org.mapstruct.NullValuePropertyMappingStrategy; import org.mapstruct.NullValuePropertyMappingStrategy;
import com.backend.metadata.kodik.api.model.KodikResponse;
import com.backend.metadata.kodik.api.model.KodikResponse.Result; import com.backend.metadata.kodik.api.model.KodikResponse.Result;
import com.backend.metadata.shikimori.api.model.Anime; import com.backend.metadata.shikimori.api.model.Anime;
import com.backend.metadata.shikimori.api.model.CharacterRole; import com.backend.metadata.shikimori.api.model.CharacterRole;
@@ -43,22 +43,18 @@ public interface KodikAndShikimoriDetailMapper {
@Mapping(target = "directors", source = "result.materialData.directors") @Mapping(target = "directors", source = "result.materialData.directors")
@Mapping(target = "related", ignore = true) @Mapping(target = "related", ignore = true)
@Mapping(target = "characters", ignore = true) @Mapping(target = "characters", ignore = true)
ContentDetailEntry toDto(Result result, @Context KodikResponse allKodikResults, @Context Anime anime); ContentDetailEntry toDto(Result result, @Context Anime anime);
@AfterMapping @AfterMapping
default void mergeShikimoriAndKodik(@MappingTarget ContentDetailEntry dto, default void mergeShikimoriAndKodik(@MappingTarget ContentDetailEntry dto,
Result result, Result result,
@Context KodikResponse allKodikResults,
@Context Anime anime) { @Context Anime anime) {
if (allKodikResults != null && allKodikResults.results != null) { if (result.translations != null) {
Map<Integer, TranslationDto> seen = allKodikResults.results.stream() dto.translations = result.translations.stream()
.filter(r -> r.translation != null) .filter(Objects::nonNull)
.collect(Collectors.toMap( .map(t -> new TranslationDto(t.id, t.title, t.type))
r -> r.translation.id, .toList();
r -> new TranslationDto(r.translation.id, r.translation.title, r.translation.type),
(a, b) -> a));
dto.translations = List.copyOf(seen.values());
} }
if (anime == null) if (anime == null)

View File

@@ -3,6 +3,8 @@ package com.backend.unifier.title.service;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import org.jboss.logging.Logger;
import com.backend.unifier.title.model.ContentDetailEntry; import com.backend.unifier.title.model.ContentDetailEntry;
import com.backend.unifier.title.model.ContentIdentifier; import com.backend.unifier.title.model.ContentIdentifier;
import com.backend.unifier.title.model.ContentProviderSource; import com.backend.unifier.title.model.ContentProviderSource;
@@ -12,6 +14,8 @@ import jakarta.enterprise.inject.Instance;
@ApplicationScoped @ApplicationScoped
public class GeneralDetailService { public class GeneralDetailService {
Logger logger = Logger.getLogger(GeneralDetailService.class);
private final IdentifierMaskService maskService; private final IdentifierMaskService maskService;
private final Instance<DetailService> detailServices; private final Instance<DetailService> detailServices;
@@ -22,8 +26,10 @@ public class GeneralDetailService {
public Optional<ContentDetailEntry> details(UUID id) { public Optional<ContentDetailEntry> details(UUID id) {
ContentIdentifier realIdentifier = maskService.realIdentifier(id); ContentIdentifier realIdentifier = maskService.realIdentifier(id);
if (realIdentifier == null) if (realIdentifier == null) {
throw new IllegalStateException("content identifier not found for id '" + id + "'"); logger.info("content identifier not found for id '" + id + "', returning no details");
return Optional.empty();
}
ContentProviderSource source = ContentProviderSource.valueOf(realIdentifier.source()); ContentProviderSource source = ContentProviderSource.valueOf(realIdentifier.source());
for (DetailService detailService : detailServices) { for (DetailService detailService : detailServices) {
if (detailService.source() == source) if (detailService.source() == source)

View File

@@ -11,21 +11,37 @@ import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped @ApplicationScoped
public class IdentifierMaskService { public class IdentifierMaskService {
private ValueCommands<String, ContentIdentifier> countCommands; private ValueCommands<String, ContentIdentifier> pointerToIdentifier;
private ValueCommands<String, String> identifierToPointer;
public IdentifierMaskService(RedisDataSource ds) { public IdentifierMaskService(RedisDataSource ds) {
countCommands = ds.value(ContentIdentifier.class); pointerToIdentifier = ds.value(ContentIdentifier.class);
identifierToPointer = ds.value(String.class);
}
private String reverseKey(String id, ContentProviderSource source) {
return "reverse:" + source.name() + ":" + id;
} }
public UUID createMask(String id, ContentProviderSource source) { public UUID createMask(String id, ContentProviderSource source) {
String rKey = reverseKey(id, source);
String existingPointer = identifierToPointer.get(rKey);
if (existingPointer != null) {
return UUID.fromString(existingPointer);
}
ContentIdentifier contentIdentifier = new ContentIdentifier(source.name(), id); ContentIdentifier contentIdentifier = new ContentIdentifier(source.name(), id);
UUID pointerID = UUID.randomUUID(); UUID pointerID = UUID.randomUUID();
countCommands.set(pointerID.toString(), contentIdentifier);
pointerToIdentifier.set(pointerID.toString(), contentIdentifier);
identifierToPointer.set(rKey, pointerID.toString());
return pointerID; return pointerID;
} }
public ContentIdentifier realIdentifier(UUID id) { public ContentIdentifier realIdentifier(UUID id) {
return countCommands.get(id.toString()); return pointerToIdentifier.get(id.toString());
} }
} }

View File

@@ -1,9 +1,12 @@
package com.backend.unifier.title.service.kodik; package com.backend.unifier.title.service.kodik;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import org.eclipse.microprofile.rest.client.inject.RestClient; import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.logging.Logger;
import com.backend.metadata.kodik.api.model.KodikResponse;
import com.backend.unifier.title.api.KodikAPI; import com.backend.unifier.title.api.KodikAPI;
import com.backend.unifier.title.api.ShikimoriAPI; import com.backend.unifier.title.api.ShikimoriAPI;
import com.backend.unifier.title.mapper.KodikAndShikimoriDetailMapper; import com.backend.unifier.title.mapper.KodikAndShikimoriDetailMapper;
@@ -15,6 +18,8 @@ import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped @ApplicationScoped
public class KodikDetailService implements DetailService { public class KodikDetailService implements DetailService {
Logger LOG = Logger.getLogger(KodikDetailService.class);
private final KodikAPI kodikAPI; private final KodikAPI kodikAPI;
private final ShikimoriAPI shikimoriAPI; private final ShikimoriAPI shikimoriAPI;
private final KodikAndShikimoriDetailMapper detailMapper; private final KodikAndShikimoriDetailMapper detailMapper;
@@ -33,15 +38,46 @@ public class KodikDetailService implements DetailService {
@Override @Override
public Optional<ContentDetailEntry> details(String id) { public Optional<ContentDetailEntry> details(String id) {
var kodikResponse = kodikAPI.findByKodikId(id); var firstOnlyKodikResponse = kodikAPI.findByKodikId(id);
int totalSize = kodikResponse.total; var firstResult = findFirst(firstOnlyKodikResponse);
if (kodikResponse.results != null) if (firstResult.isEmpty())
totalSize = kodikResponse.results.size(); return Optional.empty();
var groupedResponse = findAllGrouped(firstResult.get());
var firstGroupedResult = findFirst(groupedResponse);
if (firstGroupedResult.isEmpty())
return Optional.empty();
var shikimoriResponse = shikimoriAPI.findById(firstGroupedResult.get().shikimoriId);
return Optional.ofNullable(detailMapper.toDto(firstGroupedResult.get(), shikimoriResponse));
}
private Optional<KodikResponse.Result> findFirst(KodikResponse response) {
int totalSize = response.total;
if (response.results != null)
totalSize = response.results.size();
if (totalSize <= 0) if (totalSize <= 0)
return Optional.empty(); return Optional.empty();
var kodikResult = kodikResponse.results.get(0); if (totalSize > 1)
var shikimoriResponse = shikimoriAPI.findById(kodikResult.shikimoriId); LOG.infof("excess results, {}", response);
return Optional.ofNullable(detailMapper.toDto(kodikResult, kodikResponse, shikimoriResponse)); return Optional.ofNullable(response.results.get(0));
}
private KodikResponse findAllGrouped(KodikResponse.Result firstOnly) {
if (firstOnly.shikimoriId != null) {
return kodikAPI.findByShikimoriId(firstOnly.shikimoriId);
}
if (firstOnly.kinopoiskId != null) {
return kodikAPI.findByKinopoiskId(firstOnly.kinopoiskId);
}
if (firstOnly.imdbId != null) {
return kodikAPI.findByImdbId(firstOnly.imdbId);
}
LOG.warnf("cannot form grouped result, {}", firstOnly);
KodikResponse result = new KodikResponse();
result.total = 1;
result.results = List.of(firstOnly);
return result;
} }
} }