From eb4c249832fa569f7525c7e1590ead024bd2954b Mon Sep 17 00:00:00 2001 From: ChiveHao Date: Sun, 20 Oct 2024 00:48:56 +0800 Subject: [PATCH] refactor: subject sync pull and merge in SubjectSyncService (#712) * refactor: subject sync pull and merge in SubjectSyncService * feat: add GetSubjectSyncsByPlatformAndPlatformId in SubjectSyncEndpoint * docs: update CHANGELOG.MD --- CHANGELOG.MD | 7 + .../ikaros/api/core/character/Character.java | 19 + .../run/ikaros/api/core/person/Person.java | 19 + .../api/core/subject/SubjectOperate.java | 2 +- .../api/core/subject/SubjectSyncAction.java | 12 - ...rmOperate.java => SubjectSyncOperate.java} | 16 +- .../api/core/subject/SubjectSynchronizer.java | 41 +- .../subject/vo/PostSubjectSyncCondition.java | 7 +- gradle.properties | 2 +- .../server/core/subject/SubjectOperator.java | 8 +- ...Operator.java => SubjectSyncOperator.java} | 22 +- ...Endpoint.java => SubjectSyncEndpoint.java} | 57 +-- ...rmService.java => SubjectSyncService.java} | 20 +- .../impl/SubjectSyncPlatformServiceImpl.java | 264 ------------- .../service/impl/SubjectSyncServiceImpl.java | 360 ++++++++++++++++++ .../store/repository/CharacterRepository.java | 2 + .../store/repository/PersonRepository.java | 2 + .../SubjectCharacterRepository.java | 10 + .../repository/SubjectPersonRepository.java | 10 + .../store/repository/TagRepository.java | 2 + ...eTest.java => SubjectSyncServiceTest.java} | 8 +- 21 files changed, 527 insertions(+), 363 deletions(-) create mode 100644 api/src/main/java/run/ikaros/api/core/character/Character.java create mode 100644 api/src/main/java/run/ikaros/api/core/person/Person.java delete mode 100644 api/src/main/java/run/ikaros/api/core/subject/SubjectSyncAction.java rename api/src/main/java/run/ikaros/api/core/subject/{SubjectSyncPlatformOperate.java => SubjectSyncOperate.java} (62%) rename server/src/main/java/run/ikaros/server/core/subject/{SubjectSyncPlatformOperator.java => SubjectSyncOperator.java} (70%) rename server/src/main/java/run/ikaros/server/core/subject/endpoint/{SubjectSyncPlatformEndpoint.java => SubjectSyncEndpoint.java} (73%) rename server/src/main/java/run/ikaros/server/core/subject/service/{SubjectSyncPlatformService.java => SubjectSyncService.java} (66%) delete mode 100644 server/src/main/java/run/ikaros/server/core/subject/service/impl/SubjectSyncPlatformServiceImpl.java create mode 100644 server/src/main/java/run/ikaros/server/core/subject/service/impl/SubjectSyncServiceImpl.java create mode 100644 server/src/main/java/run/ikaros/server/store/repository/SubjectCharacterRepository.java create mode 100644 server/src/main/java/run/ikaros/server/store/repository/SubjectPersonRepository.java rename server/src/test/java/run/ikaros/server/core/subject/service/{SubjectSyncPlatformServiceTest.java => SubjectSyncServiceTest.java} (87%) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index a8aa72c96..55fa92fba 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -2,6 +2,13 @@ 更新日志文档,版本顺序从新到旧,最新版本在最前(上)面。 +# 0.18.0 + +> 注意:提供给插件的内部条目同步接口有所变化,此版本升级后,番组计划插件也需要升级到v18.x.x,不然无法正常快速拉取条目 + +- 重构条目三方同步逻辑 +- 添加根据平台和平台ID查询条目ID + # 0.17.7 - 优化第三方同步在多线程下同步出现重复条目的情况 diff --git a/api/src/main/java/run/ikaros/api/core/character/Character.java b/api/src/main/java/run/ikaros/api/core/character/Character.java new file mode 100644 index 000000000..d8019babf --- /dev/null +++ b/api/src/main/java/run/ikaros/api/core/character/Character.java @@ -0,0 +1,19 @@ +package run.ikaros.api.core.character; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class Character { + private Long id; + private String name; + private String infobox; + private String summary; +} diff --git a/api/src/main/java/run/ikaros/api/core/person/Person.java b/api/src/main/java/run/ikaros/api/core/person/Person.java new file mode 100644 index 000000000..c7c0f6925 --- /dev/null +++ b/api/src/main/java/run/ikaros/api/core/person/Person.java @@ -0,0 +1,19 @@ +package run.ikaros.api.core.person; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Accessors(chain = true) +public class Person { + private Long id; + private String name; + private String infobox; + private String summary; +} diff --git a/api/src/main/java/run/ikaros/api/core/subject/SubjectOperate.java b/api/src/main/java/run/ikaros/api/core/subject/SubjectOperate.java index b5ce35c52..fd4914185 100644 --- a/api/src/main/java/run/ikaros/api/core/subject/SubjectOperate.java +++ b/api/src/main/java/run/ikaros/api/core/subject/SubjectOperate.java @@ -20,7 +20,7 @@ public interface SubjectOperate extends AllowPluginOperate { Mono update(Subject subject); - Mono syncByPlatform(@Nullable Long subjectId, SubjectSyncPlatform platform, + Mono syncByPlatform(@Nullable Long subjectId, SubjectSyncPlatform platform, String platformId); diff --git a/api/src/main/java/run/ikaros/api/core/subject/SubjectSyncAction.java b/api/src/main/java/run/ikaros/api/core/subject/SubjectSyncAction.java deleted file mode 100644 index 0ea896e9a..000000000 --- a/api/src/main/java/run/ikaros/api/core/subject/SubjectSyncAction.java +++ /dev/null @@ -1,12 +0,0 @@ -package run.ikaros.api.core.subject; - -public enum SubjectSyncAction { - /** - * will override all subject meta info. - */ - PULL, - /** - * will update meta info that absent. - */ - MERGE -} diff --git a/api/src/main/java/run/ikaros/api/core/subject/SubjectSyncPlatformOperate.java b/api/src/main/java/run/ikaros/api/core/subject/SubjectSyncOperate.java similarity index 62% rename from api/src/main/java/run/ikaros/api/core/subject/SubjectSyncPlatformOperate.java rename to api/src/main/java/run/ikaros/api/core/subject/SubjectSyncOperate.java index 916a49da9..cc3fe7e3c 100644 --- a/api/src/main/java/run/ikaros/api/core/subject/SubjectSyncPlatformOperate.java +++ b/api/src/main/java/run/ikaros/api/core/subject/SubjectSyncOperate.java @@ -1,16 +1,22 @@ package run.ikaros.api.core.subject; import jakarta.annotation.Nullable; +import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import run.ikaros.api.core.subject.vo.PostSubjectSyncCondition; import run.ikaros.api.plugin.AllowPluginOperate; import run.ikaros.api.store.enums.SubjectSyncPlatform; -public interface SubjectSyncPlatformOperate extends AllowPluginOperate { - Mono sync(@Nullable Long subjectId, SubjectSyncPlatform platform, String platformId); - - Mono sync(PostSubjectSyncCondition condition); +public interface SubjectSyncOperate extends AllowPluginOperate { + /** + * 从第三方平台同步条目,需要插件提供底层的数据获取支持. + * + * @param subjectId 可以为空,为空则是拉取创建新的,不为空则是先查询数据库,存在则更新,不存在则新增. + * @param platform 对应的平台 + * @param platformId 对应的平台ID + */ + @Transactional + Mono sync(@Nullable Long subjectId, SubjectSyncPlatform platform, String platformId); Mono save(SubjectSync subjectSync); diff --git a/api/src/main/java/run/ikaros/api/core/subject/SubjectSynchronizer.java b/api/src/main/java/run/ikaros/api/core/subject/SubjectSynchronizer.java index 7cce5cf43..d9b7d25cc 100644 --- a/api/src/main/java/run/ikaros/api/core/subject/SubjectSynchronizer.java +++ b/api/src/main/java/run/ikaros/api/core/subject/SubjectSynchronizer.java @@ -1,7 +1,10 @@ package run.ikaros.api.core.subject; +import java.util.List; import org.pf4j.ExtensionPoint; -import reactor.core.publisher.Mono; +import run.ikaros.api.core.character.Character; +import run.ikaros.api.core.person.Person; +import run.ikaros.api.core.tag.Tag; import run.ikaros.api.store.enums.SubjectSyncPlatform; public interface SubjectSynchronizer extends ExtensionPoint { @@ -13,31 +16,27 @@ public interface SubjectSynchronizer extends ExtensionPoint { SubjectSyncPlatform getSyncPlatform(); /** - * 根据对应API从对应的平台拉取数据并转化成Ikaros的条目格式. - * 插件只需要组装数据即可. - * - * @param platformId 对应平台的条目Id. - * @return 从平台拉取的数据,进行格式转化。 - * @see Subject + * 根据平台ID获取条目. */ - Mono pull(String platformId); + Subject fetchSubjectWithPlatformId(String platformId); /** - * 根据对应API从对应平台拉取数据并合并到Ikaros已经存在的条目. - * 插件只需要组装数据即可. - * - * @param subject 已经存在的条目 - * @param platformId 三方平台的条目ID - * @return 更新后的条目对象 + * 根据平台ID获取剧集. */ - Mono merge(Subject subject, String platformId); + List fetchEpisodesWithPlatformId(String platformId); /** - * 同步对应平台ID条目及其所有的相关条目. - * 插件需要自己更新数据库相关表. - * - * @param platformId 平台ID - * @return 第一个(起点)条目 + * 根据平台ID获取标签. + */ + List fetchTagsWithPlatformId(String platformId); + + /** + * 根据平台ID获取角色. + */ + List fetchPersonsWithPlatformId(String platformId); + + /** + * 根据平台ID获取人物. */ - Mono pullSelfAndRelations(String platformId); + List fetchCharactersWithPlatformId(String platformId); } diff --git a/api/src/main/java/run/ikaros/api/core/subject/vo/PostSubjectSyncCondition.java b/api/src/main/java/run/ikaros/api/core/subject/vo/PostSubjectSyncCondition.java index 4e712be22..9c4823f44 100644 --- a/api/src/main/java/run/ikaros/api/core/subject/vo/PostSubjectSyncCondition.java +++ b/api/src/main/java/run/ikaros/api/core/subject/vo/PostSubjectSyncCondition.java @@ -2,14 +2,17 @@ import lombok.Builder; import lombok.Data; -import run.ikaros.api.core.subject.SubjectSyncAction; +import org.springframework.lang.Nullable; import run.ikaros.api.store.enums.SubjectSyncPlatform; @Data @Builder public class PostSubjectSyncCondition { + /** + * 为空则是拉取创建新的,不为空则是先查询数据库,存在则更新,不存在则新增. + */ + @Nullable private Long subjectId; private SubjectSyncPlatform platform; private String platformId; - private SubjectSyncAction subjectSyncAction; } diff --git a/gradle.properties b/gradle.properties index 638ce27d6..2804593a3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=0.17.7 +version=0.18.0 diff --git a/server/src/main/java/run/ikaros/server/core/subject/SubjectOperator.java b/server/src/main/java/run/ikaros/server/core/subject/SubjectOperator.java index c07c8361e..ddaaded8d 100644 --- a/server/src/main/java/run/ikaros/server/core/subject/SubjectOperator.java +++ b/server/src/main/java/run/ikaros/server/core/subject/SubjectOperator.java @@ -11,16 +11,16 @@ import run.ikaros.api.store.enums.SubjectSyncPlatform; import run.ikaros.api.wrap.PagingWrap; import run.ikaros.server.core.subject.service.SubjectService; -import run.ikaros.server.core.subject.service.SubjectSyncPlatformService; +import run.ikaros.server.core.subject.service.SubjectSyncService; @Slf4j @Component public class SubjectOperator implements SubjectOperate { private final SubjectService subjectService; - private final SubjectSyncPlatformService syncPlatformService; + private final SubjectSyncService syncPlatformService; public SubjectOperator(SubjectService subjectService, - SubjectSyncPlatformService syncPlatformService) { + SubjectSyncService syncPlatformService) { this.subjectService = subjectService; this.syncPlatformService = syncPlatformService; } @@ -50,7 +50,7 @@ public Mono update(Subject subject) { } @Override - public Mono syncByPlatform(@Nullable Long subjectId, SubjectSyncPlatform platform, + public Mono syncByPlatform(@Nullable Long subjectId, SubjectSyncPlatform platform, String platformId) { return syncPlatformService.sync(subjectId, platform, platformId); } diff --git a/server/src/main/java/run/ikaros/server/core/subject/SubjectSyncPlatformOperator.java b/server/src/main/java/run/ikaros/server/core/subject/SubjectSyncOperator.java similarity index 70% rename from server/src/main/java/run/ikaros/server/core/subject/SubjectSyncPlatformOperator.java rename to server/src/main/java/run/ikaros/server/core/subject/SubjectSyncOperator.java index 3d667c56e..f86ba3e30 100644 --- a/server/src/main/java/run/ikaros/server/core/subject/SubjectSyncPlatformOperator.java +++ b/server/src/main/java/run/ikaros/server/core/subject/SubjectSyncOperator.java @@ -5,31 +5,25 @@ import org.springframework.stereotype.Component; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import run.ikaros.api.core.subject.Subject; import run.ikaros.api.core.subject.SubjectSync; -import run.ikaros.api.core.subject.SubjectSyncPlatformOperate; -import run.ikaros.api.core.subject.vo.PostSubjectSyncCondition; +import run.ikaros.api.core.subject.SubjectSyncOperate; import run.ikaros.api.store.enums.SubjectSyncPlatform; -import run.ikaros.server.core.subject.service.SubjectSyncPlatformService; +import run.ikaros.server.core.subject.service.SubjectSyncService; @Slf4j @Component -public class SubjectSyncPlatformOperator implements SubjectSyncPlatformOperate { - private final SubjectSyncPlatformService service; +public class SubjectSyncOperator implements SubjectSyncOperate { + private final SubjectSyncService service; - public SubjectSyncPlatformOperator(SubjectSyncPlatformService service) { + public SubjectSyncOperator(SubjectSyncService service) { this.service = service; } - @Override - public Mono sync(@Nullable Long subjectId, SubjectSyncPlatform platform, - String platformId) { - return service.sync(subjectId, platform, platformId); - } @Override - public Mono sync(PostSubjectSyncCondition condition) { - return service.sync(condition); + public Mono sync(@Nullable Long subjectId, SubjectSyncPlatform platform, + String platformId) { + return service.sync(subjectId, platform, platformId); } @Override diff --git a/server/src/main/java/run/ikaros/server/core/subject/endpoint/SubjectSyncPlatformEndpoint.java b/server/src/main/java/run/ikaros/server/core/subject/endpoint/SubjectSyncEndpoint.java similarity index 73% rename from server/src/main/java/run/ikaros/server/core/subject/endpoint/SubjectSyncPlatformEndpoint.java rename to server/src/main/java/run/ikaros/server/core/subject/endpoint/SubjectSyncEndpoint.java index ab6b0d06a..f2c963882 100644 --- a/server/src/main/java/run/ikaros/server/core/subject/endpoint/SubjectSyncPlatformEndpoint.java +++ b/server/src/main/java/run/ikaros/server/core/subject/endpoint/SubjectSyncEndpoint.java @@ -18,26 +18,24 @@ import run.ikaros.api.constant.OpenApiConst; import run.ikaros.api.core.subject.Subject; import run.ikaros.api.core.subject.SubjectSync; -import run.ikaros.api.core.subject.SubjectSyncAction; -import run.ikaros.api.core.subject.vo.PostSubjectSyncCondition; import run.ikaros.api.infra.exception.subject.NoAvailableSubjectPlatformSynchronizerException; import run.ikaros.api.store.enums.SubjectSyncPlatform; -import run.ikaros.server.core.subject.service.SubjectSyncPlatformService; +import run.ikaros.server.core.subject.service.SubjectSyncService; import run.ikaros.server.endpoint.CoreEndpoint; @Slf4j @Component -public class SubjectSyncPlatformEndpoint implements CoreEndpoint { - private final SubjectSyncPlatformService service; +public class SubjectSyncEndpoint implements CoreEndpoint { + private final SubjectSyncService service; - public SubjectSyncPlatformEndpoint(SubjectSyncPlatformService service) { + public SubjectSyncEndpoint(SubjectSyncService service) { this.service = service; } @Override public RouterFunction endpoint() { - var tag = OpenApiConst.CORE_VERSION + "/subject/sync/platform"; + var tag = OpenApiConst.CORE_VERSION + "/subject/sync"; return SpringdocRouteBuilder.route() .POST("/subject/sync/platform", this::sync, builder -> builder.operationId("SyncSubjectAndPlatform") @@ -60,13 +58,6 @@ public RouterFunction endpoint() { .description("Platform id") .required(true) .implementation(String.class)) - .parameter(parameterBuilder() - .name("action") - .description("Sync action, such as PULL or MERGE, " - + "default is PULL " - + "PULL will override all subject meta info, " - + "MERGE will update meta info that absent.") - .implementation(SubjectSyncAction.class)) .response(Builder.responseBuilder() .implementation(Subject.class)) ) @@ -85,6 +76,25 @@ public RouterFunction endpoint() { .implementationArray(SubjectSync.class)) ) + .GET("/subject/syncs/platform", this::getSubjectSyncsByPlatformAndPlatformId, + builder -> builder.operationId("GetSubjectSyncsByPlatformAndPlatformId") + .tag(tag).description("Get subject syncs by subject id.") + .parameter(parameterBuilder() + .name("platform") + .description("Platform.") + .required(true) + .implementation(SubjectSyncPlatform.class)) + .parameter(parameterBuilder() + .name("platformId") + .description("Platform id") + .in(ParameterIn.QUERY) + .required(true) + .implementation(Long.class)) + .response(Builder.responseBuilder() + .description("Subject syncs by subject id.") + .implementationArray(SubjectSync.class)) + ) + .build(); } @@ -103,16 +113,8 @@ private Mono sync(ServerRequest request) { "'platformId' must has text."); String platformId = platformIdOp.get(); - Optional actionOp = request.queryParam("action"); - SubjectSyncAction subjectSyncAction = - SubjectSyncAction.valueOf(actionOp.orElse(SubjectSyncAction.PULL.name())); - PostSubjectSyncCondition condition = PostSubjectSyncCondition.builder() - .subjectId(subjectId).platform(platform) - .platformId(platformId).subjectSyncAction(subjectSyncAction) - .build(); - - return service.sync(condition) + return service.sync(subjectId, platform, platformId) .flatMap(subject -> ServerResponse.ok().bodyValue(subject)) .onErrorResume(NoAvailableSubjectPlatformSynchronizerException.class, err -> ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE) @@ -129,4 +131,13 @@ private Mono findSubjectSyncsBySubjectId(ServerRequest request) .collectList() .flatMap(list -> ServerResponse.ok().bodyValue(list)); } + + private Mono getSubjectSyncsByPlatformAndPlatformId(ServerRequest request) { + SubjectSyncPlatform platform = SubjectSyncPlatform.valueOf( + request.queryParam("platform").orElse(SubjectSyncPlatform.BGM_TV.name())); + Long platformId = Long.valueOf(request.queryParam("platformId").orElse("-1L")); + return service.findSubjectSyncsByPlatformAndPlatformId(platform, String.valueOf(platformId)) + .collectList() + .flatMap(list -> ServerResponse.ok().bodyValue(list)); + } } diff --git a/server/src/main/java/run/ikaros/server/core/subject/service/SubjectSyncPlatformService.java b/server/src/main/java/run/ikaros/server/core/subject/service/SubjectSyncService.java similarity index 66% rename from server/src/main/java/run/ikaros/server/core/subject/service/SubjectSyncPlatformService.java rename to server/src/main/java/run/ikaros/server/core/subject/service/SubjectSyncService.java index c0c946efe..6c676be74 100644 --- a/server/src/main/java/run/ikaros/server/core/subject/service/SubjectSyncPlatformService.java +++ b/server/src/main/java/run/ikaros/server/core/subject/service/SubjectSyncService.java @@ -4,24 +4,20 @@ import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import run.ikaros.api.core.subject.Subject; import run.ikaros.api.core.subject.SubjectSync; -import run.ikaros.api.core.subject.vo.PostSubjectSyncCondition; import run.ikaros.api.store.enums.SubjectSyncPlatform; -public interface SubjectSyncPlatformService { - Mono sync(@Nullable Long subjectId, SubjectSyncPlatform platform, String platformId); - - Mono sync(PostSubjectSyncCondition condition); - +public interface SubjectSyncService { /** - * 同步对应平台ID条目及其所有的相关条目. + * 从第三方平台同步条目,需要插件提供底层的数据获取支持. * - * @param platform 平台 - * @param platformId 平台ID - * @return 第一个(起点)条目 + * @param subjectId 可以为空,为空则是拉取创建新的,不为空则是先查询数据库,存在则更新,不存在则新增. + * @param platform 对应的平台 + * @param platformId 对应的平台ID */ - Mono syncSelfAndRelations(SubjectSyncPlatform platform, String platformId); + @Transactional + Mono sync(@Nullable Long subjectId, SubjectSyncPlatform platform, String platformId); + @Transactional Mono save(SubjectSync subjectSync); diff --git a/server/src/main/java/run/ikaros/server/core/subject/service/impl/SubjectSyncPlatformServiceImpl.java b/server/src/main/java/run/ikaros/server/core/subject/service/impl/SubjectSyncPlatformServiceImpl.java deleted file mode 100644 index 88563e8f5..000000000 --- a/server/src/main/java/run/ikaros/server/core/subject/service/impl/SubjectSyncPlatformServiceImpl.java +++ /dev/null @@ -1,264 +0,0 @@ -package run.ikaros.server.core.subject.service.impl; - -import static run.ikaros.api.infra.utils.ReactiveBeanUtils.copyProperties; - -import jakarta.annotation.Nullable; -import jakarta.validation.constraints.NotNull; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.stereotype.Service; -import org.springframework.util.Assert; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; -import run.ikaros.api.core.subject.Subject; -import run.ikaros.api.core.subject.SubjectSync; -import run.ikaros.api.core.subject.SubjectSyncAction; -import run.ikaros.api.core.subject.SubjectSynchronizer; -import run.ikaros.api.core.subject.vo.PostSubjectSyncCondition; -import run.ikaros.api.infra.exception.subject.NoAvailableSubjectPlatformSynchronizerException; -import run.ikaros.api.store.enums.SubjectSyncPlatform; -import run.ikaros.server.core.subject.service.SubjectService; -import run.ikaros.server.core.subject.service.SubjectSyncPlatformService; -import run.ikaros.server.plugin.ExtensionComponentsFinder; -import run.ikaros.server.store.entity.SubjectSyncEntity; -import run.ikaros.server.store.repository.SubjectSyncRepository; - -@Slf4j -@Service -public class SubjectSyncPlatformServiceImpl implements SubjectSyncPlatformService, - ApplicationContextAware { - private final ExtensionComponentsFinder extensionComponentsFinder; - private final SubjectService subjectService; - private ApplicationContext applicationContext; - private final SubjectSyncRepository subjectSyncRepository; - private final Set pullingPlatformIdSet = new CopyOnWriteArraySet<>(); - - /** - * Construct. - */ - public SubjectSyncPlatformServiceImpl(ExtensionComponentsFinder extensionComponentsFinder, - SubjectService subjectService, - SubjectSyncRepository subjectSyncRepository) { - this.extensionComponentsFinder = extensionComponentsFinder; - this.subjectService = subjectService; - this.subjectSyncRepository = subjectSyncRepository; - } - - @Override - public synchronized Mono sync(@Nullable Long subjectId, SubjectSyncPlatform platform, - String platformId) { - Assert.notNull(platform, "'platform' must not null."); - Assert.hasText(platformId, "'platformId' must has text."); - // 查询是否已经同步过了,如果已经同步过则返回对应的条目信息 - return subjectSyncRepository.findByPlatformAndPlatformId(platform, platformId) - .collectList() - .filter(subjectSyncEntities -> !subjectSyncEntities.isEmpty()) - .map(subjectSyncEntities -> subjectSyncEntities.get(0)) - .map(SubjectSyncEntity::getSubjectId) - .flatMap(subjectService::findById) - .switchIfEmpty(syncBySubjectSynchronizer(subjectId, platform, platformId)); - } - - @Override - public Mono sync(PostSubjectSyncCondition condition) { - Assert.notNull(condition, "'condition' must not null."); - Long subjectId = condition.getSubjectId(); - SubjectSyncPlatform platform = condition.getPlatform(); - String platformId = condition.getPlatformId(); - SubjectSyncAction action = condition.getSubjectSyncAction(); - - if (SubjectSyncAction.PULL.equals(action)) { - return sync(subjectId, platform, platformId); - } - - Assert.isTrue(subjectId > 0, - "'subjectId' must not null and gt 0 when action is MERGE."); - - return subjectService.findById(subjectId) - .flatMapMany(subject -> - Flux.fromStream(extensionComponentsFinder.getExtensions(SubjectSynchronizer.class) - .stream()) - ) - .filter(subjectSynchronizer -> platform.equals(subjectSynchronizer.getSyncPlatform())) - .collectList() - .filter(subjectSynchronizes -> !subjectSynchronizes.isEmpty()) - .switchIfEmpty(Mono.error(new NoAvailableSubjectPlatformSynchronizerException( - "No found available subject platform synchronizer for platform-id: " - + platform.name() + "-" + platformId))) - .map(subjectSynchronizes -> subjectSynchronizes.get(0)) - .flatMap(subjectSynchronizer -> subjectService.findById(subjectId) - .flatMap(subject -> subjectSynchronizer.merge(subject, platformId))) - .onErrorResume(Exception.class, e -> { - String msg = - "Operate has exception " - + "for platform-id: " - + platform.name() + "-" + platformId - + ", plugin exception msg: " + e.getMessage(); - log.error(msg, e); - return Mono.error(new NoAvailableSubjectPlatformSynchronizerException(msg)); - }) - .flatMap(subject -> subjectService.update(subject) - .then(Mono.defer(() -> subjectService.findById(subjectId)))) - .subscribeOn(Schedulers.boundedElastic()); - - } - - @Override - public Mono syncSelfAndRelations(SubjectSyncPlatform platform, String platformId) { - Assert.notNull(platform, "'platform' must not null."); - Assert.hasText(platformId, "'platformId' must has text."); - // 查询是否已经同步过了,如果已经同步过则返回对应的条目信息 - return subjectSyncRepository.findByPlatformAndPlatformId(platform, platformId) - .collectList() - .filter(subjectSyncEntities -> !subjectSyncEntities.isEmpty()) - .map(subjectSyncEntities -> subjectSyncEntities.get(0)) - .map(SubjectSyncEntity::getSubjectId) - .flatMap(subjectService::findById) - .switchIfEmpty(doSyncSelfAndRelations(platform, platformId)); - } - - private Mono doSyncSelfAndRelations(SubjectSyncPlatform platform, String platformId) { - Assert.notNull(platform, "'platform' must not null."); - Assert.hasText(platformId, "'platformId' must has text."); - return Flux.fromStream(extensionComponentsFinder.getExtensions(SubjectSynchronizer.class) - .stream()) - .filter(subjectSynchronizer -> platform.equals(subjectSynchronizer.getSyncPlatform())) - .collectList() - .filter(subjectSynchronizes -> !subjectSynchronizes.isEmpty()) - .switchIfEmpty(Mono.error(new NoAvailableSubjectPlatformSynchronizerException( - "No found available subject platform synchronizer for platform-id: " - + platform.name() + "-" + platformId))) - .map(subjectSynchronizes -> subjectSynchronizes.get(0)) - .flatMap(subjectSynchronizer -> subjectSynchronizer.pullSelfAndRelations(platformId)) - .onErrorResume(Exception.class, e -> { - String msg = - "Operate has exception " - + "for platform-id: " - + platform.name() + "-" + platformId - + ", plugin exception msg: " + e.getMessage(); - log.error(msg, e); - return Mono.error(new NoAvailableSubjectPlatformSynchronizerException(msg)); - }).subscribeOn(Schedulers.boundedElastic()); - } - - @Override - public Mono save(SubjectSync subjectSync) { - log.debug("save: {}", subjectSync); - return subjectSyncRepository.findBySubjectIdAndPlatformAndPlatformId( - subjectSync.getSubjectId(), subjectSync.getPlatform(), subjectSync.getPlatformId()) - .switchIfEmpty(Mono.just(new SubjectSyncEntity() - .setSubjectId(subjectSync.getSubjectId()) - .setPlatform(subjectSync.getPlatform()) - .setPlatformId(subjectSync.getPlatformId()) - .setSyncTime(subjectSync.getSyncTime())) - .doOnSuccess(e -> log.debug("create new subject sync record: [{}].", e))) - .map(entity -> entity.setSubjectId(subjectSync.getSubjectId()) - .setPlatform(subjectSync.getPlatform()) - .setPlatformId(subjectSync.getPlatformId()) - .setSyncTime(subjectSync.getSyncTime())) - .flatMap(subjectSyncRepository::save) - .map(entity -> subjectSync - .setSubjectId(entity.getSubjectId()) - .setPlatform(entity.getPlatform()) - .setPlatformId(entity.getPlatformId()) - .setSyncTime(entity.getSyncTime())); - } - - @Override - public Mono remove(SubjectSync subjectSync) { - return copyProperties(subjectSync, SubjectSyncEntity.builder().build()) - .flatMap(subjectSyncRepository::delete); - } - - @Override - public Flux findSubjectSyncsBySubjectId(long subjectId) { - Assert.isTrue(subjectId > 0, "'subjectId' must gt 0."); - return subjectSyncRepository.findAllBySubjectId(subjectId) - .flatMap(subjectSyncEntity -> copyProperties(subjectSyncEntity, - SubjectSync.builder().build())); - } - - @Override - public Mono findSubjectSyncBySubjectIdAndPlatform(long subjectId, - SubjectSyncPlatform platform) { - Assert.isTrue(subjectId > 0, "'subjectId' must gt 0."); - Assert.notNull(platform, "'platform' must not null."); - return subjectSyncRepository.findBySubjectIdAndPlatform(subjectId, platform) - .flatMap(subjectSyncEntity -> copyProperties(subjectSyncEntity, - SubjectSync.builder().build())); - } - - @Override - public Flux findSubjectSyncsByPlatformAndPlatformId(SubjectSyncPlatform platform, - String platformId) { - Assert.notNull(platform, "'platform' must not null."); - Assert.hasText(platformId, "'platformId' must has text."); - return subjectSyncRepository.findByPlatformAndPlatformId(platform, platformId) - .flatMap(subjectSyncEntity -> copyProperties(subjectSyncEntity, - SubjectSync.builder().build())); - } - - @Override - public Mono findBySubjectIdAndPlatformAndPlatformId(Long subjectId, - SubjectSyncPlatform platform, - String platformId) { - Assert.isTrue(subjectId > 0, "'subjectId' must gt 0."); - Assert.notNull(platform, "'platform' must not null."); - Assert.hasText(platformId, "'platformId' must has text."); - return subjectSyncRepository.findBySubjectIdAndPlatformAndPlatformId( - subjectId, platform, platformId) - .flatMap(subjectSyncEntity -> copyProperties(subjectSyncEntity, - SubjectSync.builder().build())); - } - - - private Mono syncBySubjectSynchronizer(@Nullable Long subjectId, - SubjectSyncPlatform platform, - String platformId) { - if (pullingPlatformIdSet.contains(platformId)) { - return Mono.empty(); - } - pullingPlatformIdSet.add(platformId); - return Flux.fromStream(extensionComponentsFinder.getExtensions(SubjectSynchronizer.class) - .stream()) - .filter(subjectSynchronizer -> platform.equals(subjectSynchronizer.getSyncPlatform())) - .collectList() - .filter(subjectSynchronizes -> !subjectSynchronizes.isEmpty()) - .switchIfEmpty(Mono.error(new NoAvailableSubjectPlatformSynchronizerException( - "No found available subject platform synchronizer for platform-id: " - + platform.name() + "-" + platformId))) - .map(subjectSynchronizes -> subjectSynchronizes.get(0)) - .subscribeOn(Schedulers.boundedElastic()) - .flatMap(subjectSynchronizer -> subjectSynchronizer.pull(platformId)) - .onErrorResume(Exception.class, e -> { - String msg = - "Operate has exception " - + "for platform-id: " - + platform.name() + "-" + platformId - + ", plugin exception msg: " + e.getMessage(); - log.error(msg, e); - return Mono.error(new NoAvailableSubjectPlatformSynchronizerException(msg)); - }) - .flatMap(subject -> Objects.isNull(subjectId) - ? subjectService.create(subject) - .onErrorResume(DuplicateKeyException.class, e -> Mono.just(subject) - .map(sub -> sub.setId(null)).flatMap(subjectService::create)) - : subjectService.update(subject) - .then(Mono.defer(() -> subjectService.findById(subjectId)))) - .doFinally(signalType -> pullingPlatformIdSet.remove(platformId)); - } - - - @Override - public void setApplicationContext(@NotNull ApplicationContext applicationContext) - throws BeansException { - this.applicationContext = applicationContext; - } -} diff --git a/server/src/main/java/run/ikaros/server/core/subject/service/impl/SubjectSyncServiceImpl.java b/server/src/main/java/run/ikaros/server/core/subject/service/impl/SubjectSyncServiceImpl.java new file mode 100644 index 000000000..49f08e781 --- /dev/null +++ b/server/src/main/java/run/ikaros/server/core/subject/service/impl/SubjectSyncServiceImpl.java @@ -0,0 +1,360 @@ +package run.ikaros.server.core.subject.service.impl; + +import static run.ikaros.api.infra.utils.ReactiveBeanUtils.copyProperties; + +import jakarta.annotation.Nullable; +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import run.ikaros.api.core.character.Character; +import run.ikaros.api.core.person.Person; +import run.ikaros.api.core.subject.SubjectSync; +import run.ikaros.api.core.subject.SubjectSynchronizer; +import run.ikaros.api.infra.exception.subject.NoAvailableSubjectPlatformSynchronizerException; +import run.ikaros.api.store.enums.SubjectSyncPlatform; +import run.ikaros.api.store.enums.TagType; +import run.ikaros.server.core.subject.service.SubjectService; +import run.ikaros.server.core.subject.service.SubjectSyncService; +import run.ikaros.server.plugin.ExtensionComponentsFinder; +import run.ikaros.server.store.entity.BaseEntity; +import run.ikaros.server.store.entity.CharacterEntity; +import run.ikaros.server.store.entity.EpisodeEntity; +import run.ikaros.server.store.entity.PersonEntity; +import run.ikaros.server.store.entity.SubjectCharacterEntity; +import run.ikaros.server.store.entity.SubjectEntity; +import run.ikaros.server.store.entity.SubjectPersonEntity; +import run.ikaros.server.store.entity.SubjectSyncEntity; +import run.ikaros.server.store.entity.TagEntity; +import run.ikaros.server.store.repository.CharacterRepository; +import run.ikaros.server.store.repository.EpisodeRepository; +import run.ikaros.server.store.repository.PersonRepository; +import run.ikaros.server.store.repository.SubjectCharacterRepository; +import run.ikaros.server.store.repository.SubjectPersonRepository; +import run.ikaros.server.store.repository.SubjectRepository; +import run.ikaros.server.store.repository.SubjectSyncRepository; +import run.ikaros.server.store.repository.TagRepository; + +@Slf4j +@Service +public class SubjectSyncServiceImpl implements SubjectSyncService, + ApplicationContextAware { + private final ExtensionComponentsFinder extensionComponentsFinder; + private final SubjectService subjectService; + private final SubjectRepository subjectRepository; + private final EpisodeRepository episodeRepository; + private final TagRepository tagRepository; + private final CharacterRepository characterRepository; + private final SubjectCharacterRepository subjectCharacterRepository; + private final PersonRepository personRepository; + private final SubjectPersonRepository subjectPersonRepository; + private ApplicationContext applicationContext; + private final SubjectSyncRepository subjectSyncRepository; + + /** + * Construct. + */ + public SubjectSyncServiceImpl(ExtensionComponentsFinder extensionComponentsFinder, + SubjectService subjectService, + SubjectSyncRepository subjectSyncRepository, + SubjectRepository subjectRepository, + EpisodeRepository episodeRepository, TagRepository tagRepository, + CharacterRepository characterRepository, + SubjectCharacterRepository subjectCharacterRepository, + PersonRepository personRepository, + SubjectPersonRepository subjectPersonRepository) { + this.extensionComponentsFinder = extensionComponentsFinder; + this.subjectService = subjectService; + this.subjectSyncRepository = subjectSyncRepository; + this.subjectRepository = subjectRepository; + this.episodeRepository = episodeRepository; + this.tagRepository = tagRepository; + this.characterRepository = characterRepository; + this.subjectCharacterRepository = subjectCharacterRepository; + this.personRepository = personRepository; + this.subjectPersonRepository = subjectPersonRepository; + } + + class SyncTargetExistsException extends RuntimeException { + } + + @Override + public Mono sync(@Nullable Long subjectId, SubjectSyncPlatform platform, + String platformId) { + Assert.notNull(platform, "'platform' must not null."); + Assert.hasText(platformId, "'platformId' must has text."); + Assert.isTrue(Long.parseLong(platformId) > 0, "'platformId' must gt 0."); + + final SubjectSynchronizer synchronizer = getSubjectSynchronizerWithPlatform(platform); + + Mono findExistsMono; + if (subjectId == null) { + findExistsMono = + subjectSyncRepository.existsByPlatformAndPlatformId(platform, platformId); + } else { + findExistsMono = subjectSyncRepository + .existsBySubjectIdAndPlatformAndPlatformId(subjectId, platform, platformId); + } + + // 查询是否已经同步过,同步过则直接返回 + Mono existsMono = findExistsMono.flatMap(exists -> { + // id为空时操作为PULL,并且已经存在了,则直接退出,不执行下方流程 + if (exists && subjectId == null) { + return Mono.error(new SyncTargetExistsException()); + } + // id为空时操作为PULL,但是不存在 + // id不为空时操作为MERGE,存在则更新,不存在则新增 + return Mono.just(exists); + }); + + // 保存条目信息获取ID + final AtomicReference subjectIdA = + new AtomicReference<>(subjectId == null ? -1 : subjectId); + Mono subjectEntityMono = + existsMono.then(Mono.just(synchronizer)) + .map(synchronizer1 -> synchronizer1.fetchSubjectWithPlatformId(platformId)) + .filter(Objects::nonNull) + .switchIfEmpty(Mono.error(new SyncTargetExistsException())) + .map(subject -> { + log.debug("fetch subject {} from platform-id: {}-{}", + subject, platform, platformId); + subjectIdA.set(subject.getId()); + return subject; + }) + .flatMap(subject -> { + if (subjectId == null) { + return Mono.just(new SubjectEntity()) + .flatMap(entity -> copyProperties(subject, entity, "id")); + } else { + return subjectRepository.findById(subjectId) + .flatMap(entity -> copyProperties(subject, entity, "id")); + } + }) + .flatMap(subjectRepository::save) + .map(entity -> { + subjectIdA.set(entity.getId()); + return entity; + }); + + // 保存三方同步信息 + Mono syncEntityMono = + subjectEntityMono.map(BaseEntity::getId) + .flatMap(sid -> subjectSyncRepository.findBySubjectIdAndPlatformAndPlatformId( + sid, platform, platformId) + .switchIfEmpty(Mono.just(SubjectSyncEntity.builder() + .syncTime(LocalDateTime.now()) + .platform(platform) + .platformId(platformId) + .subjectId(sid) + .build()))) + .flatMap(subjectSyncRepository::save); + + // 保存剧集信息 + Mono> episodesMono = + syncEntityMono.then(Mono.just(synchronizer)) + .map(synchronizer1 -> synchronizer1.fetchEpisodesWithPlatformId(platformId)) + .map(episodes -> { + if (episodes == null) { + episodes = Collections.emptyList(); + } + log.debug("fetch episodes size {} from platform-id: {}-{}", + episodes.size(), platform, platformId); + return episodes; + }) + .flatMapMany(Flux::fromIterable) + .flatMap(episode -> episodeRepository.findBySubjectIdAndGroupAndSequence( + subjectIdA.get(), episode.getGroup(), episode.getSequence()) + .collectList() + .filter(es -> !es.isEmpty()) + .map(es -> es.get(0)) + .switchIfEmpty(Mono.just(new EpisodeEntity())) + .flatMap(entity -> copyProperties(episode, entity, "id"))) + .map(entity -> { + if (entity.getCreateTime() == null) { + entity.setCreateTime(LocalDateTime.now()); + } + entity.setSubjectId(subjectIdA.get()) + .setUpdateTime(LocalDateTime.now()); + return entity; + }) + .flatMap(episodeRepository::save) + .collectList(); + + + // 保存标签信息 + Mono> tagsMono = + episodesMono.then(Mono.just(synchronizer)) + .map(synchronizer1 -> synchronizer1.fetchTagsWithPlatformId(platformId)) + .map(tags -> { + if (tags == null) { + tags = Collections.emptyList(); + } + log.debug("fetch tags {} from platform-id: {}-{}", tags, platform, platformId); + return tags; + }) + .flatMapMany(Flux::fromIterable) + .flatMap(tag -> tagRepository.findByTypeAndMasterIdAndName( + TagType.SUBJECT, subjectIdA.get(), tag.getName()) + .switchIfEmpty(Mono.just(new TagEntity())) + .flatMap(entity -> copyProperties(tag, entity, "id"))) + .map(entity -> entity.setUserId(-1L) + .setCreateTime(LocalDateTime.now()) + .setType(TagType.SUBJECT) + .setMasterId(subjectIdA.get())) + .flatMap(tagRepository::save) + .collectList(); + + // 保存角色信息 + Mono> scMono = + tagsMono.then(Mono.just(synchronizer)) + .map(synchronizer1 -> { + List characters = + synchronizer1.fetchCharactersWithPlatformId(platformId); + log.debug("fetch characters {} from platform-id: {}-{}", characters, platform, + platformId); + return characters; + }) + .filter(Objects::nonNull) + .flatMapMany(Flux::fromIterable) + .flatMap(character -> characterRepository.findByName(character.getName()) + .switchIfEmpty(Mono.just(new CharacterEntity())) + .flatMap(entity -> copyProperties(character, entity, "id"))) + .flatMap(characterRepository::save) + .map(BaseEntity::getId) + .flatMap(cid -> subjectCharacterRepository.findBySubjectIdAndCharacterId( + subjectIdA.get(), cid) + .switchIfEmpty(Mono.just(SubjectCharacterEntity.builder() + .characterId(cid) + .subjectId(subjectIdA.get()) + .build()))) + .flatMap(subjectCharacterRepository::save) + .collectList(); + + // 保存人物信息 + + Mono> spMono = + scMono.then(Mono.just(synchronizer)) + .map(synchronizer1 -> { + List people = synchronizer1.fetchPersonsWithPlatformId(platformId); + log.debug("fetch persons {} from platform-id: {}-{}", people, platform, + platformId); + return people; + }) + .filter(Objects::nonNull) + .flatMapMany(Flux::fromIterable) + .flatMap(person -> personRepository.findByName(person.getName()) + .switchIfEmpty(Mono.just(new PersonEntity())) + .flatMap(entity -> copyProperties(person, entity, "id"))) + .flatMap(personRepository::save) + .map(BaseEntity::getId) + .flatMap(pid -> subjectPersonRepository.findBySubjectIdAndPersonId( + subjectIdA.get(), pid + ).switchIfEmpty(Mono.just(SubjectPersonEntity.builder() + .personId(pid) + .subjectId(subjectIdA.get()) + .build()))) + .flatMap(subjectPersonRepository::save) + .collectList(); + + return spMono.then() + .onErrorResume(SyncTargetExistsException.class, e -> Mono.empty()); + } + + @Override + public Mono save(SubjectSync subjectSync) { + log.debug("save: {}", subjectSync); + return subjectSyncRepository.findBySubjectIdAndPlatformAndPlatformId( + subjectSync.getSubjectId(), subjectSync.getPlatform(), subjectSync.getPlatformId()) + .switchIfEmpty(Mono.just(new SubjectSyncEntity() + .setSubjectId(subjectSync.getSubjectId()) + .setPlatform(subjectSync.getPlatform()) + .setPlatformId(subjectSync.getPlatformId()) + .setSyncTime(subjectSync.getSyncTime())) + .doOnSuccess(e -> log.debug("create new subject sync record: [{}].", e))) + .map(entity -> entity.setSubjectId(subjectSync.getSubjectId()) + .setPlatform(subjectSync.getPlatform()) + .setPlatformId(subjectSync.getPlatformId()) + .setSyncTime(subjectSync.getSyncTime())) + .flatMap(subjectSyncRepository::save) + .map(entity -> subjectSync + .setSubjectId(entity.getSubjectId()) + .setPlatform(entity.getPlatform()) + .setPlatformId(entity.getPlatformId()) + .setSyncTime(entity.getSyncTime())); + } + + @Override + public Mono remove(SubjectSync subjectSync) { + return copyProperties(subjectSync, SubjectSyncEntity.builder().build()) + .flatMap(subjectSyncRepository::delete); + } + + @Override + public Flux findSubjectSyncsBySubjectId(long subjectId) { + Assert.isTrue(subjectId > 0, "'subjectId' must gt 0."); + return subjectSyncRepository.findAllBySubjectId(subjectId) + .flatMap(subjectSyncEntity -> copyProperties(subjectSyncEntity, + SubjectSync.builder().build())); + } + + @Override + public Mono findSubjectSyncBySubjectIdAndPlatform(long subjectId, + SubjectSyncPlatform platform) { + Assert.isTrue(subjectId > 0, "'subjectId' must gt 0."); + Assert.notNull(platform, "'platform' must not null."); + return subjectSyncRepository.findBySubjectIdAndPlatform(subjectId, platform) + .flatMap(subjectSyncEntity -> copyProperties(subjectSyncEntity, + SubjectSync.builder().build())); + } + + @Override + public Flux findSubjectSyncsByPlatformAndPlatformId(SubjectSyncPlatform platform, + String platformId) { + Assert.notNull(platform, "'platform' must not null."); + Assert.hasText(platformId, "'platformId' must has text."); + return subjectSyncRepository.findByPlatformAndPlatformId(platform, platformId) + .flatMap(subjectSyncEntity -> copyProperties(subjectSyncEntity, + SubjectSync.builder().build())); + } + + @Override + public Mono findBySubjectIdAndPlatformAndPlatformId(Long subjectId, + SubjectSyncPlatform platform, + String platformId) { + Assert.isTrue(subjectId > 0, "'subjectId' must gt 0."); + Assert.notNull(platform, "'platform' must not null."); + Assert.hasText(platformId, "'platformId' must has text."); + return subjectSyncRepository.findBySubjectIdAndPlatformAndPlatformId( + subjectId, platform, platformId) + .flatMap(subjectSyncEntity -> copyProperties(subjectSyncEntity, + SubjectSync.builder().build())); + } + + + private SubjectSynchronizer getSubjectSynchronizerWithPlatform(SubjectSyncPlatform platform) { + return extensionComponentsFinder.getExtensions(SubjectSynchronizer.class) + .stream() + .filter(synchronizer -> platform.equals(synchronizer.getSyncPlatform())) + .findFirst() + .orElseThrow(() -> new NoAvailableSubjectPlatformSynchronizerException( + "No found available subject platform synchronizer for platform: " + + platform.name() + )); + } + + @Override + public void setApplicationContext(@NotNull ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } +} diff --git a/server/src/main/java/run/ikaros/server/store/repository/CharacterRepository.java b/server/src/main/java/run/ikaros/server/store/repository/CharacterRepository.java index 714f97c8b..92b385681 100644 --- a/server/src/main/java/run/ikaros/server/store/repository/CharacterRepository.java +++ b/server/src/main/java/run/ikaros/server/store/repository/CharacterRepository.java @@ -1,7 +1,9 @@ package run.ikaros.server.store.repository; import org.springframework.data.r2dbc.repository.R2dbcRepository; +import reactor.core.publisher.Mono; import run.ikaros.server.store.entity.CharacterEntity; public interface CharacterRepository extends R2dbcRepository { + Mono findByName(String name); } diff --git a/server/src/main/java/run/ikaros/server/store/repository/PersonRepository.java b/server/src/main/java/run/ikaros/server/store/repository/PersonRepository.java index 5b17c7b47..7428c4e8a 100644 --- a/server/src/main/java/run/ikaros/server/store/repository/PersonRepository.java +++ b/server/src/main/java/run/ikaros/server/store/repository/PersonRepository.java @@ -1,7 +1,9 @@ package run.ikaros.server.store.repository; import org.springframework.data.r2dbc.repository.R2dbcRepository; +import reactor.core.publisher.Mono; import run.ikaros.server.store.entity.PersonEntity; public interface PersonRepository extends R2dbcRepository { + Mono findByName(String name); } diff --git a/server/src/main/java/run/ikaros/server/store/repository/SubjectCharacterRepository.java b/server/src/main/java/run/ikaros/server/store/repository/SubjectCharacterRepository.java new file mode 100644 index 000000000..b065cd707 --- /dev/null +++ b/server/src/main/java/run/ikaros/server/store/repository/SubjectCharacterRepository.java @@ -0,0 +1,10 @@ +package run.ikaros.server.store.repository; + +import org.springframework.data.r2dbc.repository.R2dbcRepository; +import reactor.core.publisher.Mono; +import run.ikaros.server.store.entity.SubjectCharacterEntity; + +public interface SubjectCharacterRepository + extends R2dbcRepository { + Mono findBySubjectIdAndCharacterId(Long subjectId, Long characterId); +} diff --git a/server/src/main/java/run/ikaros/server/store/repository/SubjectPersonRepository.java b/server/src/main/java/run/ikaros/server/store/repository/SubjectPersonRepository.java new file mode 100644 index 000000000..0ad51d325 --- /dev/null +++ b/server/src/main/java/run/ikaros/server/store/repository/SubjectPersonRepository.java @@ -0,0 +1,10 @@ +package run.ikaros.server.store.repository; + +import org.springframework.data.r2dbc.repository.R2dbcRepository; +import reactor.core.publisher.Mono; +import run.ikaros.server.store.entity.SubjectPersonEntity; + +public interface SubjectPersonRepository + extends R2dbcRepository { + Mono findBySubjectIdAndPersonId(Long subjectId, Long personId); +} diff --git a/server/src/main/java/run/ikaros/server/store/repository/TagRepository.java b/server/src/main/java/run/ikaros/server/store/repository/TagRepository.java index ca3a54a16..cd2f16262 100644 --- a/server/src/main/java/run/ikaros/server/store/repository/TagRepository.java +++ b/server/src/main/java/run/ikaros/server/store/repository/TagRepository.java @@ -8,5 +8,7 @@ public interface TagRepository extends R2dbcRepository { + Mono findByTypeAndMasterIdAndName(TagType type, Long masterId, String name); + Mono existsByTypeAndMasterIdAndName(TagType type, Long masterId, String name); } diff --git a/server/src/test/java/run/ikaros/server/core/subject/service/SubjectSyncPlatformServiceTest.java b/server/src/test/java/run/ikaros/server/core/subject/service/SubjectSyncServiceTest.java similarity index 87% rename from server/src/test/java/run/ikaros/server/core/subject/service/SubjectSyncPlatformServiceTest.java rename to server/src/test/java/run/ikaros/server/core/subject/service/SubjectSyncServiceTest.java index db8c8947d..138337e69 100644 --- a/server/src/test/java/run/ikaros/server/core/subject/service/SubjectSyncPlatformServiceTest.java +++ b/server/src/test/java/run/ikaros/server/core/subject/service/SubjectSyncServiceTest.java @@ -12,10 +12,10 @@ import run.ikaros.server.store.repository.SubjectSyncRepository; @SpringBootTest -class SubjectSyncPlatformServiceTest { +class SubjectSyncServiceTest { @Autowired - private SubjectSyncPlatformService subjectSyncPlatformService; + private SubjectSyncService subjectSyncService; @Autowired private SubjectSyncRepository subjectSyncRepository; @@ -34,7 +34,7 @@ void save() { .platformId(String.valueOf(random.nextLong(1, 99999))) .build(); - StepVerifier.create(subjectSyncPlatformService.save(subjectSync)) + StepVerifier.create(subjectSyncService.save(subjectSync)) .expectNextMatches(subjectSync1 -> subjectSync.getSubjectId().equals(subjectSync1.getSubjectId()) && subjectSync.getPlatformId().equals(subjectSync1.getPlatformId()) @@ -43,7 +43,7 @@ void save() { .verifyComplete(); - StepVerifier.create(subjectSyncPlatformService.findBySubjectIdAndPlatformAndPlatformId( + StepVerifier.create(subjectSyncService.findBySubjectIdAndPlatformAndPlatformId( subjectSync.getSubjectId(), subjectSync.getPlatform(), subjectSync.getPlatformId() )).expectNextMatches(subjectSync1 -> subjectSync.getSubjectId().equals(subjectSync1.getSubjectId())