From 328c23b420a3d79802cc9e395c92d4abff5a9cb6 Mon Sep 17 00:00:00 2001 From: rollw <73376341+roll-w@users.noreply.github.com> Date: Fri, 13 Mar 2026 00:35:31 +0800 Subject: [PATCH 1/6] refactor(content): change content ID types from long to string for consistency This refactor updates various content-related classes and interfaces to use String instead of Long for content IDs. This change aims to standardize the handling of content identifiers across the codebase, improving consistency and potentially simplifying future integrations with external systems that may use string-based IDs. BREAKING CHANGE: The content ID type has changed from Long to String, which may affect existing implementations relying on Long IDs. --- .../lampray/content/article/Article.java | 41 +++-- .../content/article/persistence/ArticleDao.kt | 10 +- .../{ArticleDo.kt => ArticleEntity.kt} | 70 +++++--- .../article/persistence/ArticleRepository.kt | 26 ++- .../article/service/ArticleContentProvider.kt | 6 +- .../article/service/ArticleService.java | 25 +-- .../lampray/content/comment/Comment.java | 63 ++++--- .../comment/CommentDetailsMetadata.java | 6 +- .../content/comment/persistence/CommentDao.kt | 7 +- .../{CommentDo.kt => CommentEntity.kt} | 98 +++++----- .../comment/persistence/CommentRepository.kt | 30 +++- .../comment/service/CommentContentProvider.kt | 8 +- .../comment/service/CommentService.java | 38 ++-- .../lamprism/lampray/content/Content.java | 6 +- .../lampray/content/ContentDetails.java | 4 +- .../lampray/content/ContentIdentity.java | 10 +- .../lampray/content/ContentMetadata.java | 62 ++++--- .../content/ContentMetadataDetails.java | 4 +- .../lampray/content/ContentOperator.java | 6 +- .../lampray/content/ContentTrait.java | 4 +- .../lampray/content/SimpleContentDetails.java | 6 +- .../content/SimpleContentIdentity.java | 6 +- .../lampray/content/SimpleContentInfo.java | 6 +- .../content/collection/ContentCollection.java | 4 +- .../collection/ContentCollectionDetails.java | 4 +- .../collection/ContentCollectionIdentity.java | 6 +- .../collection/ContentCollectionMetadata.java | 12 +- .../SimpleContentCollectionIdentity.java | 8 +- .../content/favorite/FavoriteGroup.java | 37 ++-- .../favorite/FavoriteGroupOperator.java | 6 +- .../content/favorite/FavoriteItem.java | 74 +++++--- .../content/favorite/FavoriteProvider.java | 8 +- .../service/AbstractContentOperator.java | 4 +- .../service/ContentProvideService.java | 14 +- .../favorite/persistence/FavoriteGroupDao.kt | 6 +- ...oriteGroupDo.kt => FavoriteGroupEntity.kt} | 103 +++++------ .../persistence/FavoriteGroupRepository.kt | 24 ++- .../favorite/persistence/FavoriteItemDao.kt | 6 +- ...avoriteItemDo.kt => FavoriteItemEntity.kt} | 96 +++++----- .../persistence/FavoriteItemRepository.kt | 13 +- .../content/persistence/ContentMetadataDao.kt | 4 +- ...MetadataDo.kt => ContentMetadataEntity.kt} | 84 +++++---- .../persistence/ContentMetadataRepository.kt | 29 +-- .../service/ContentMetadataServiceImpl.kt | 10 +- .../content/service/ContentService.java | 24 ++- .../lampray/content/review/ReviewJob.java | 12 +- .../content/review/feedback/ReviewCategory.kt | 22 +-- .../review/persistence/ReviewJobEntity.kt | 14 +- .../review/persistence/ReviewJobRepository.kt | 8 +- .../review/persistence/ReviewTaskEntity.kt | 4 +- .../RegisterVerificationToken.java | 32 ++-- .../repository/RegisterTokenDao.kt | 6 +- ...isterTokenDo.kt => RegisterTokenEntity.kt} | 73 +++++--- .../repository/RegisterTokenRepository.kt | 14 +- .../service/LoginRegisterService.java | 23 ++- .../system/resource/LocalizedMessage.kt | 29 ++- .../resource/data/LocalizedMessageDao.kt | 6 +- ...MessageDo.kt => LocalizedMessageEntity.kt} | 37 ++-- .../data/LocalizedMessageRepository.kt | 24 +-- .../LocalizedMessageResourceService.kt | 32 ++-- .../lampray/setting/SystemSetting.java | 38 ++-- .../lampray/setting/data/SystemSettingDao.kt | 6 +- ...temSettingDo.kt => SystemSettingEntity.kt} | 45 +++-- .../setting/data/SystemSettingRepository.kt | 14 +- .../service/SystemSettingConfigProvider.kt | 17 +- .../user/details/UserPersonalData.java | 8 +- .../persistence/UserPersonalDataDao.kt | 9 +- ...nalDataDo.kt => UserPersonalDataEntity.kt} | 78 +++++--- .../persistence/UserPersonalDataRepository.kt | 17 +- .../service/UserPersonalDataServiceImpl.java | 25 ++- .../lampray/user/details/service/Utils.kt | 6 +- .../LiquibaseInitializeConfiguration.java | 69 ++++++- .../article/ArticleManageController.java | 8 +- .../article/model/ArticleInfoView.java | 4 +- .../article/model/ArticleMetaVo.java | 4 +- .../controller/article/model/ArticleVo.java | 4 +- .../controller/comment/CommentController.kt | 8 +- .../comment/model/CommentRequest.java | 4 +- .../web/controller/comment/model/CommentVo.kt | 12 +- .../controller/content/ContentController.java | 20 +-- .../content/ContentManageController.java | 4 +- .../web/controller/content/vo/ContentVo.java | 4 +- .../review/model/ReviewJobContentView.kt | 6 +- .../review/model/ReviewJobDetailsView.kt | 4 +- .../controller/review/model/ReviewJobView.kt | 4 +- .../db/baseline/baseline-0.1.0-alpha1.yaml | 50 +++++- .../src/main/resources/db/changelog.yaml | 3 + .../0.1.0/0.1.0-alpha1-01-changelog.yaml | 2 +- ...0.1.0-alpha1-02-resource-id-migration.yaml | 168 ++++++++++++++++++ 89 files changed, 1342 insertions(+), 753 deletions(-) rename lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/{ArticleDo.kt => ArticleEntity.kt} (72%) rename lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/{CommentDo.kt => CommentEntity.kt} (71%) rename lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/{FavoriteGroupDo.kt => FavoriteGroupEntity.kt} (72%) rename lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/{FavoriteItemDo.kt => FavoriteItemEntity.kt} (67%) rename lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/{ContentMetadataDo.kt => ContentMetadataEntity.kt} (67%) rename lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/{RegisterTokenDo.kt => RegisterTokenEntity.kt} (67%) rename lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/{LocalizedMessageDo.kt => LocalizedMessageEntity.kt} (75%) rename lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/{SystemSettingDo.kt => SystemSettingEntity.kt} (65%) rename lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/{UserPersonalDataDo.kt => UserPersonalDataEntity.kt} (77%) create mode 100644 lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml diff --git a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/Article.java b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/Article.java index 4cecd985..00561ec4 100644 --- a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/Article.java +++ b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/Article.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import space.lingu.NonNull; import space.lingu.Nullable; import tech.lamprism.lampray.DataEntity; -import tech.lamprism.lampray.LongEntityBuilder; +import tech.lamprism.lampray.EntityBuilder; import tech.lamprism.lampray.content.ContentDetails; import tech.lamprism.lampray.content.ContentDetailsMetadata; import tech.lamprism.lampray.content.ContentType; @@ -30,8 +30,9 @@ /** * @author RollW */ -public class Article implements DataEntity, ContentDetails { +public class Article implements DataEntity, ContentDetails { private final Long id; + private final String resourceId; private final long userId; private final String title; private final String cover; @@ -39,9 +40,10 @@ public class Article implements DataEntity, ContentDetails { private final OffsetDateTime createTime; private final OffsetDateTime updateTime; - public Article(Long id, long userId, String title, String cover, + public Article(Long id, String resourceId, long userId, String title, String cover, StructuralText content, OffsetDateTime createTime, OffsetDateTime updateTime) { this.id = id; + this.resourceId = resourceId; this.userId = userId; this.title = title; this.cover = cover; @@ -55,14 +57,14 @@ public Long getId() { } @Override - public Long getEntityId() { - return id; + public String getEntityId() { + return resourceId; } @Override @NonNull - public Long getResourceId() { - return DataEntity.super.getResourceId(); + public String getResourceId() { + return resourceId; } @Override @@ -103,8 +105,8 @@ public String getCover() { return cover; } - public Article fork(Long id) { - return new Article(id, userId, title, cover, + public Article fork(Long id, String resourceId) { + return new Article(id, resourceId, userId, title, cover, content, createTime, updateTime ); } @@ -118,7 +120,7 @@ public static Builder builder() { } @Override - public long getContentId() { + public String getContentId() { return getEntityId(); } @@ -128,8 +130,9 @@ public ContentType getContentType() { return ContentType.ARTICLE; } - public final static class Builder implements LongEntityBuilder
{ + public final static class Builder implements EntityBuilder { private Long id; + private String resourceId; private long userId; private String title; private String cover; @@ -143,6 +146,7 @@ public Builder() { public Builder(Article article) { this.id = article.id; + this.resourceId = article.resourceId; this.userId = article.userId; this.title = article.title; this.cover = article.cover; @@ -152,11 +156,20 @@ public Builder(Article article) { } @Override - public Builder setEntityId(Long id) { + public Builder setEntityId(String id) { + return setResourceId(id); + } + + public Builder setId(Long id) { this.id = id; return this; } + public Builder setResourceId(String resourceId) { + this.resourceId = resourceId; + return this; + } + public Builder setUserId(long userId) { this.userId = userId; return this; @@ -189,7 +202,7 @@ public Builder setCover(String cover) { @Override public Article build() { - return new Article(id, userId, title, cover, content, + return new Article(id, resourceId, userId, title, cover, content, createTime, updateTime); } } diff --git a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleDao.kt b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleDao.kt index 0e200491..c9228779 100644 --- a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleDao.kt +++ b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleDao.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface ArticleDao : CommonDao { - @Query("FROM ArticleDo a WHERE a.userId = :userId") - fun findAllByUserId(@Param("userId") userId: Long): List -} \ No newline at end of file +interface ArticleDao : CommonDao { + @Query("FROM ArticleEntity a WHERE a.userId = :userId") + fun findAllByUserId(@Param("userId") userId: Long): List +} diff --git a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleDo.kt b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleEntity.kt similarity index 72% rename from lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleDo.kt rename to lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleEntity.kt index 13e2e889..7f401344 100644 --- a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleDo.kt +++ b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,13 +18,13 @@ package tech.lamprism.lampray.content.article.persistence import jakarta.persistence.Column import jakarta.persistence.Entity -import jakarta.persistence.GeneratedValue -import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Lob import jakarta.persistence.Table import jakarta.persistence.Temporal import jakarta.persistence.TemporalType +import org.hibernate.annotations.Generated +import org.hibernate.generator.EventType import tech.lamprism.lampray.DataEntity import tech.lamprism.lampray.content.ContentDetails import tech.lamprism.lampray.content.ContentDetailsMetadata @@ -39,12 +39,15 @@ import java.time.OffsetDateTime */ @Entity @Table(name = "article") -class ArticleDo( - @Column(name = "id", nullable = false) - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) +class ArticleEntity( + @Column(name = "id", nullable = false, insertable = false, updatable = false) + @Generated(event = [EventType.INSERT]) var id: Long? = null, + @Id + @Column(name = "resource_id", nullable = false, length = 64, unique = true) + private var persistedResourceId: String, + @Column(name = "user_id", nullable = false) private var userId: Long = 0, @@ -59,16 +62,16 @@ class ArticleDo( private var content: StructuralText = StructuralText.EMPTY, @Temporal(TemporalType.TIMESTAMP) - @Column(name = "createTime", nullable = false) + @Column(name = "create_time", nullable = false) private var createTime: OffsetDateTime = OffsetDateTime.now(), @Temporal(TemporalType.TIMESTAMP) @Column(name = "update_time", nullable = false) private var updateTime: OffsetDateTime = OffsetDateTime.now() -) : DataEntity, ContentDetails { - override fun getEntityId(): Long? = id +) : DataEntity, ContentDetails { + override fun getEntityId(): String = persistedResourceId - override fun getResourceId(): Long = id!! + override fun getResourceId(): String = persistedResourceId override fun getCreateTime(): OffsetDateTime = createTime @@ -82,7 +85,7 @@ class ArticleDo( this.updateTime = updateTime } - override fun getContentId(): Long = id!! + override fun getContentId(): String = persistedResourceId override fun getContentType(): ContentType = ContentType.ARTICLE @@ -108,14 +111,15 @@ class ArticleDo( ArticleDetailsMetadata(cover) fun lock(): Article = Article( - id!!, userId, title, cover, content, createTime, updateTime + id, persistedResourceId, userId, title, cover, content, createTime, updateTime ) fun toBuilder(): Builder = Builder(this) override fun toString(): String { - return "ArticleDo(" + + return "ArticleEntity(" + "id=$id, " + + "resourceId='$persistedResourceId', " + "userId=$userId, " + "title='$title', " + "cover='$cover', " + @@ -127,6 +131,7 @@ class ArticleDo( class Builder { private var id: Long? = null + private var resourceId: String? = null private var userId: Long = 0 private var title: String? = null private var cover: String = "" @@ -136,8 +141,9 @@ class ArticleDo( constructor() - constructor(other: ArticleDo) { + constructor(other: ArticleEntity) { this.id = other.id + this.resourceId = other.getResourceId() this.userId = other.userId this.title = other.title this.cover = other.cover @@ -150,6 +156,10 @@ class ArticleDo( this.id = id } + fun setResourceId(resourceId: String) = apply { + this.resourceId = resourceId + } + fun setUserId(userId: Long) = apply { this.userId = userId } @@ -174,20 +184,32 @@ class ArticleDo( this.updateTime = updateTime } - fun build(): ArticleDo { - return ArticleDo( - id, userId, title!!, cover, - content!!, createTime!!, updateTime!! + fun build(): ArticleEntity { + return ArticleEntity( + id = id, + persistedResourceId = resourceId!!, + userId = userId, + title = title!!, + cover = cover, + content = content!!, + createTime = createTime!!, + updateTime = updateTime!! ) } } companion object { @JvmStatic - fun Article.toDo(): ArticleDo { - return ArticleDo( - entityId, userId, title, cover, content, - createTime, updateTime + fun Article.toEntity(): ArticleEntity { + return ArticleEntity( + id = id, + persistedResourceId = resourceId, + userId = userId, + title = title, + cover = cover, + content = content, + createTime = createTime, + updateTime = updateTime ) } @@ -196,4 +218,4 @@ class ArticleDo( return Builder() } } -} \ No newline at end of file +} diff --git a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleRepository.kt b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleRepository.kt index 541fb283..577580e5 100644 --- a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleRepository.kt +++ b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,24 +27,32 @@ import java.util.Optional @Repository class ArticleRepository( private val articleDao: ArticleDao -) : CommonRepository(articleDao) { - fun findAllByUserId(userId: Long): List { +) : CommonRepository(articleDao) { + override fun save(entity: S): S { + return articleDao.saveAndFlush(entity) + } + + override fun saveAll(entities: Iterable): List { + return articleDao.saveAllAndFlush(entities) + } + + fun findAllByUserId(userId: Long): List { return articleDao.findAllByUserId(userId) } - fun findByTitle(title: String, userId: Long): Optional { + fun findByTitle(title: String, userId: Long): Optional { return articleDao.findOne(createTitleSpecification(title, userId)) } private fun createTitleSpecification( title: String, userId: Long - ): Specification { - return Specification { root, query, criteriaBuilder -> + ): Specification { + return Specification { root, _, criteriaBuilder -> criteriaBuilder.and( - criteriaBuilder.equal(root.get(ArticleDo_.title), title), - criteriaBuilder.equal(root.get(ArticleDo_.userId), userId) + criteriaBuilder.equal(root.get(ArticleEntity_.title), title), + criteriaBuilder.equal(root.get(ArticleEntity_.userId), userId) ) } } -} \ No newline at end of file +} diff --git a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/service/ArticleContentProvider.kt b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/service/ArticleContentProvider.kt index 02e3f1a3..c2f729fd 100644 --- a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/service/ArticleContentProvider.kt +++ b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/service/ArticleContentProvider.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ import tech.lamprism.lampray.content.ContentProvider import tech.lamprism.lampray.content.ContentTrait import tech.lamprism.lampray.content.ContentType import tech.lamprism.lampray.content.article.Article -import tech.lamprism.lampray.content.article.persistence.ArticleDo.Companion.toDo +import tech.lamprism.lampray.content.article.persistence.ArticleEntity.Companion.toEntity import tech.lamprism.lampray.content.article.persistence.ArticleRepository import tech.lamprism.lampray.content.common.ContentErrorCode import tech.lamprism.lampray.content.common.ContentException @@ -69,6 +69,6 @@ class ArticleContentProvider( } override fun updateArticle(article: Article) { - articleRepository.save(article.toDo()) + articleRepository.save(article.toEntity()) } } diff --git a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/service/ArticleService.java b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/service/ArticleService.java index eecff3fe..5c9ac66d 100644 --- a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/service/ArticleService.java +++ b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/service/ArticleService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,12 +21,13 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import space.lingu.NonNull; +import tech.lamprism.lampray.common.data.ResourceIdGenerator; import tech.lamprism.lampray.content.ContentDetails; import tech.lamprism.lampray.content.ContentPublisher; import tech.lamprism.lampray.content.ContentType; import tech.lamprism.lampray.content.UncreatedContent; import tech.lamprism.lampray.content.article.Article; -import tech.lamprism.lampray.content.article.persistence.ArticleDo; +import tech.lamprism.lampray.content.article.persistence.ArticleEntity; import tech.lamprism.lampray.content.article.persistence.ArticleRepository; import tech.lamprism.lampray.content.collection.ContentCollectionIdentity; import tech.lamprism.lampray.content.collection.ContentCollectionProvider; @@ -47,9 +48,12 @@ public class ArticleService implements ContentPublisher, ContentCollectionProvid private static final Logger logger = LoggerFactory.getLogger(ArticleService.class); private final ArticleRepository articleRepository; + private final ResourceIdGenerator resourceIdGenerator; - public ArticleService(ArticleRepository articleRepository) { + public ArticleService(ArticleRepository articleRepository, + ResourceIdGenerator resourceIdGenerator) { this.articleRepository = articleRepository; + this.resourceIdGenerator = resourceIdGenerator; } @Override @@ -60,7 +64,7 @@ public ContentDetails publish(@NonNull UncreatedContent uncreatedContent, throw new IllegalArgumentException("Content type not supported: " + uncreatedContent.getContentType()); } - String title = Validate.notEmpty(uncreatedContent.getTitle()); + String title = Validate.notEmpty(Objects.requireNonNull(uncreatedContent.getTitle())); StructuralText content = Objects.requireNonNull(uncreatedContent.getContent()); long userId = uncreatedContent.getOperator().getUserId(); @@ -69,7 +73,8 @@ public ContentDetails publish(@NonNull UncreatedContent uncreatedContent, throw new ContentException(ContentErrorCode.ERROR_CONTENT_EXISTED); } - ArticleDo article = ArticleDo.builder() + ArticleEntity article = ArticleEntity.builder() + .setResourceId(resourceIdGenerator.nextId(ContentType.ARTICLE.getSystemResourceKind())) .setUserId(userId) .setTitle(title) .setContent(content) @@ -77,9 +82,9 @@ public ContentDetails publish(@NonNull UncreatedContent uncreatedContent, .setCreateTime(timestamp) .setUpdateTime(timestamp) .build(); - ArticleDo created = articleRepository.save(article); + ArticleEntity created = articleRepository.save(article); logger.trace("Article({}) title={} created by user({})", - created.getEntityId(), created.getTitle(), created.getUserId()); + created.getResourceId(), created.getTitle(), created.getUserId()); return created; } @@ -91,14 +96,14 @@ public boolean supports(@NonNull ContentType contentType) { private List
getUserArticles(long id) { return articleRepository.findAllByUserId(id) .stream() - .map(ArticleDo::lock) + .map(ArticleEntity::lock) .toList(); } private List
getArticles() { return articleRepository.findAll() .stream() - .map(ArticleDo::lock) + .map(ArticleEntity::lock) .toList(); } @@ -117,7 +122,7 @@ public List getContents( return switch (contentCollectionIdentity.getContentCollectionType()) { case ARTICLES -> getArticles(); case USER_ARTICLES -> getUserArticles( - contentCollectionIdentity.getContentCollectionId() + Long.parseLong(contentCollectionIdentity.getContentCollectionId()) ); default -> throw new UnsupportedOperationException("Unsupported collection type: " + contentCollectionIdentity.getContentCollectionType()); diff --git a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/Comment.java b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/Comment.java index 688b6052..8201073c 100644 --- a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/Comment.java +++ b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/Comment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import space.lingu.NonNull; import space.lingu.Nullable; import tech.lamprism.lampray.DataEntity; -import tech.lamprism.lampray.LongEntityBuilder; +import tech.lamprism.lampray.EntityBuilder; import tech.lamprism.lampray.content.ContentAssociated; import tech.lamprism.lampray.content.ContentDetails; import tech.lamprism.lampray.content.ContentDetailsMetadata; @@ -33,18 +33,19 @@ /** * @author RollW */ -public class Comment implements DataEntity, ContentDetails, ContentAssociated { - public static final int COMMENT_ROOT_ID = 0; +public class Comment implements DataEntity, ContentDetails, ContentAssociated { + public static final String COMMENT_ROOT_ID = ""; private final Long id; + private final String resourceId; private final long userId; /** * Parent comment id. *

* If the comment is a top-level comment, - * the parent id is 0. + * the parent id is an empty string. */ - private final long parentId; + private final String parentId; private final StructuralText content; private final OffsetDateTime createTime; private final OffsetDateTime updateTime; @@ -53,7 +54,7 @@ public class Comment implements DataEntity, ContentDetails, ContentAssocia * Comment on which type of content. */ private final ContentType commentOnType; - private final long commentOnId; + private final String commentOnId; @NonNull private final CommentStatus commentStatus; @@ -61,11 +62,12 @@ public class Comment implements DataEntity, ContentDetails, ContentAssocia private final ContentIdentity associatedContent; private final CommentDetailsMetadata commentDetailsMetadata; - public Comment(Long id, long userId, long parentId, StructuralText content, + public Comment(Long id, String resourceId, long userId, String parentId, StructuralText content, OffsetDateTime createTime, OffsetDateTime updateTime, - ContentType commentOnType, long commentOnId, + ContentType commentOnType, String commentOnId, @NonNull CommentStatus commentStatus) { this.id = id; + this.resourceId = resourceId; this.userId = userId; this.commentOnId = commentOnId; this.parentId = parentId; @@ -79,19 +81,19 @@ public Comment(Long id, long userId, long parentId, StructuralText content, } @Override - public Long getEntityId() { - return id; + public String getEntityId() { + return resourceId; } - @NonNull @Override - public Long getResourceId() { - return id; + @NonNull + public String getResourceId() { + return resourceId; } @Override - public long getContentId() { - return id; + public String getContentId() { + return resourceId; } @NonNull @@ -117,11 +119,11 @@ public StructuralText getContent() { return content; } - public long getParentId() { + public String getParentId() { return parentId; } - public long getCommentOnId() { + public String getCommentOnId() { return commentOnId; } @@ -171,11 +173,12 @@ public SystemResourceKind getSystemResourceKind() { return CommentResourceKind.INSTANCE; } - public static class Builder implements LongEntityBuilder { + public static class Builder implements EntityBuilder { private Long id; + private String resourceId; private long userId; - private long commentOn; - private long parentId; + private String commentOn; + private String parentId = COMMENT_ROOT_ID; private StructuralText content; private OffsetDateTime createTime; private OffsetDateTime updateTime; @@ -185,6 +188,7 @@ public static class Builder implements LongEntityBuilder { public Builder(Comment comment) { this.id = comment.id; + this.resourceId = comment.resourceId; this.userId = comment.userId; this.commentOn = comment.commentOnId; this.parentId = comment.parentId; @@ -200,22 +204,31 @@ public Builder() { } @Override - public Builder setEntityId(Long id) { + public Builder setEntityId(String id) { + return setResourceId(id); + } + + public Builder setId(Long id) { this.id = id; return this; } + public Builder setResourceId(String resourceId) { + this.resourceId = resourceId; + return this; + } + public Builder setUserId(long userId) { this.userId = userId; return this; } - public Builder setCommentOn(long commentOn) { + public Builder setCommentOn(String commentOn) { this.commentOn = commentOn; return this; } - public Builder setParentId(long parentId) { + public Builder setParentId(String parentId) { this.parentId = parentId; return this; } @@ -248,7 +261,7 @@ public Builder setCommentStatus(@NonNull CommentStatus commentStatus) { @Override public Comment build() { return new Comment( - id, userId, parentId, + id, resourceId, userId, parentId, content, createTime, updateTime, type, commentOn, commentStatus diff --git a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/CommentDetailsMetadata.java b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/CommentDetailsMetadata.java index 6a96d6b0..6435a2bb 100644 --- a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/CommentDetailsMetadata.java +++ b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/CommentDetailsMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,10 +26,10 @@ */ public record CommentDetailsMetadata( ContentType contentType, - long contentId, + String contentId, @Nullable - Long parentId + String parentId ) implements ContentDetailsMetadata { public ContentIdentity getCommentOn() { return ContentIdentity.of(contentId, contentType); diff --git a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentDao.kt b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentDao.kt index c77d33e9..78b2ad1a 100644 --- a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentDao.kt +++ b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentDao.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,5 +23,6 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface CommentDao : CommonDao { -} \ No newline at end of file +interface CommentDao : CommonDao { + fun findAllByUserId(userId: Long): List +} diff --git a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentDo.kt b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentEntity.kt similarity index 71% rename from lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentDo.kt rename to lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentEntity.kt index 672c279b..7beacdb3 100644 --- a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentDo.kt +++ b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +20,12 @@ import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated -import jakarta.persistence.GeneratedValue -import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Lob import jakarta.persistence.Table +import org.hibernate.annotations.Generated import org.hibernate.annotations.JdbcTypeCode +import org.hibernate.generator.EventType import org.hibernate.type.SqlTypes import tech.lamprism.lampray.DataEntity import tech.lamprism.lampray.content.ContentAssociated @@ -45,17 +45,20 @@ import java.time.OffsetDateTime */ @Entity @Table(name = "comment") -class CommentDo( - @Id - @Column(name = "id", nullable = false) - @GeneratedValue(strategy = GenerationType.IDENTITY) +class CommentEntity( + @Column(name = "id", nullable = false, insertable = false, updatable = false) + @Generated(event = [EventType.INSERT]) private var id: Long? = null, + @Id + @Column(name = "resource_id", nullable = false, length = 64, unique = true) + private var persistedResourceId: String, + @Column(name = "user_id", nullable = false) private var userId: Long = 0, @Column(name = "parent_id", nullable = false) - var parentId: Long = 0, + var parentId: String = Comment.COMMENT_ROOT_ID, @Lob @Column(name = "content", nullable = false, length = 1000000) @@ -72,22 +75,22 @@ class CommentDo( @JdbcTypeCode(SqlTypes.VARCHAR) var commentOnType: ContentType = ContentType.COMMENT, - @Column(name = "comment_on_id", nullable = false) - var commentOnId: Long = 0, + @Column(name = "comment_on_id", nullable = false, length = 64) + var commentOnId: String = "", @Column(name = "comment_status", nullable = false, length = 40) @Enumerated(EnumType.STRING) @JdbcTypeCode(SqlTypes.VARCHAR) var commentStatus: CommentStatus = CommentStatus.NONE -) : DataEntity, ContentDetails, ContentAssociated { - override fun getEntityId(): Long? = id +) : DataEntity, ContentDetails, ContentAssociated { + override fun getEntityId(): String = persistedResourceId + + override fun getResourceId(): String = persistedResourceId - fun setId(id: Long) { + fun setId(id: Long?) { this.id = id } - override fun getResourceId(): Long = id!! - override fun getCreateTime(): OffsetDateTime = createTime fun setCreateTime(createTime: OffsetDateTime) { @@ -100,7 +103,7 @@ class CommentDo( this.updateTime = updateTime } - override fun getContentId(): Long = id!! + override fun getContentId(): String = persistedResourceId override fun getContentType(): ContentType = ContentType.COMMENT @@ -132,13 +135,14 @@ class CommentDo( } fun lock(): Comment = Comment( - id!!, userId, parentId, content, createTime, + id, persistedResourceId, userId, parentId, content, createTime, updateTime, commentOnType, commentOnId, commentStatus ) override fun toString(): String { - return "CommentDo(" + + return "CommentEntity(" + "id=$id, " + + "resourceId='$persistedResourceId', " + "userId=$userId, " + "parentId=$parentId, " + "content='$content', " + @@ -152,19 +156,21 @@ class CommentDo( class Builder { private var id: Long? = null + private var resourceId: String? = null private var userId: Long = 0 - private var parentId: Long = 0 + private var parentId: String = Comment.COMMENT_ROOT_ID private var content: StructuralText? = null private var createTime: OffsetDateTime? = null private var updateTime: OffsetDateTime? = null private var commentOnType: ContentType? = null - private var commentOnId: Long = 0 + private var commentOnId: String = "" private var commentStatus: CommentStatus? = null constructor() - constructor(other: CommentDo) { + constructor(other: CommentEntity) { this.id = other.id + this.resourceId = other.getResourceId() this.userId = other.userId this.parentId = other.parentId this.content = other.content @@ -179,11 +185,15 @@ class CommentDo( this.id = id } + fun setResourceId(resourceId: String) = apply { + this.resourceId = resourceId + } + fun setUserId(userId: Long) = apply { this.userId = userId } - fun setParentId(parentId: Long) = apply { + fun setParentId(parentId: String) = apply { this.parentId = parentId } @@ -203,7 +213,7 @@ class CommentDo( this.commentOnType = commentOnType } - fun setCommentOnId(commentOnId: Long) = apply { + fun setCommentOnId(commentOnId: String) = apply { this.commentOnId = commentOnId } @@ -211,17 +221,18 @@ class CommentDo( this.commentStatus = commentStatus } - fun build(): CommentDo { - return CommentDo( - id, - userId, - parentId, - content!!, - createTime!!, - updateTime!!, - commentOnType!!, - commentOnId, - commentStatus!! + fun build(): CommentEntity { + return CommentEntity( + id = id, + persistedResourceId = resourceId!!, + userId = userId, + parentId = parentId, + content = content!!, + createTime = createTime!!, + updateTime = updateTime!!, + commentOnType = commentOnType!!, + commentOnId = commentOnId, + commentStatus = commentStatus!! ) } } @@ -231,12 +242,19 @@ class CommentDo( fun builder(): Builder = Builder() @JvmStatic - fun Comment.toDo(): CommentDo { - return CommentDo( - entityId, userId, parentId, content, - createTime, updateTime, - commentOnType, commentOnId, commentStatus + fun Comment.toEntity(): CommentEntity { + return CommentEntity( + id = null, + persistedResourceId = resourceId, + userId = userId, + parentId = parentId, + content = content, + createTime = createTime, + updateTime = updateTime, + commentOnType = commentOnType, + commentOnId = commentOnId, + commentStatus = commentStatus ) } } -} \ No newline at end of file +} diff --git a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentRepository.kt b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentRepository.kt index 773c9bce..45e5486a 100644 --- a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentRepository.kt +++ b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,21 +27,33 @@ import tech.lamprism.lampray.content.ContentType @Repository class CommentRepository( private val commentDao: CommentDao -) : CommonRepository(commentDao) { +) : CommonRepository(commentDao) { + override fun save(entity: S): S { + return commentDao.saveAndFlush(entity) + } + + override fun saveAll(entities: Iterable): List { + return commentDao.saveAllAndFlush(entities) + } + + fun findAllByUserId(userId: Long): List { + return commentDao.findAllByUserId(userId) + } + fun findByContent( - contentId: Long, + contentId: String, contentType: ContentType - ): List { + ): List { return findAll(createContentSpecification(contentId, contentType)) } private fun createContentSpecification( - contentId: Long, + contentId: String, contentType: ContentType - ): Specification = Specification { root, query, criteriaBuilder -> + ): Specification = Specification { root, query, criteriaBuilder -> criteriaBuilder.and( - criteriaBuilder.equal(root.get(CommentDo_.commentOnId), contentId), - criteriaBuilder.equal(root.get(CommentDo_.commentOnType), contentType) + criteriaBuilder.equal(root.get(CommentEntity_.commentOnId), contentId), + criteriaBuilder.equal(root.get(CommentEntity_.commentOnType), contentType) ) } -} \ No newline at end of file +} diff --git a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/service/CommentContentProvider.kt b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/service/CommentContentProvider.kt index 06205416..1453fb3e 100644 --- a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/service/CommentContentProvider.kt +++ b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/service/CommentContentProvider.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ import tech.lamprism.lampray.content.ContentProvider import tech.lamprism.lampray.content.ContentTrait import tech.lamprism.lampray.content.ContentType import tech.lamprism.lampray.content.comment.Comment -import tech.lamprism.lampray.content.comment.persistence.CommentDo.Companion.toDo +import tech.lamprism.lampray.content.comment.persistence.CommentEntity.Companion.toEntity import tech.lamprism.lampray.content.comment.persistence.CommentRepository import tech.lamprism.lampray.content.common.ContentErrorCode import tech.lamprism.lampray.content.common.ContentException @@ -65,6 +65,6 @@ class CommentContentProvider( } override fun updateComment(comment: Comment) { - commentRepository.save(comment.toDo()) + commentRepository.save(comment.toEntity()) } -} \ No newline at end of file +} diff --git a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/service/CommentService.java b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/service/CommentService.java index 3d445f58..16ccecd9 100644 --- a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/service/CommentService.java +++ b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/service/CommentService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,8 @@ import org.springframework.stereotype.Service; import space.lingu.NonNull; -import tech.lamprism.lampray.content.Content; +import space.lingu.Nullable; +import tech.lamprism.lampray.common.data.ResourceIdGenerator; import tech.lamprism.lampray.content.ContentDetails; import tech.lamprism.lampray.content.ContentDetailsMetadata; import tech.lamprism.lampray.content.ContentIdentity; @@ -34,7 +35,7 @@ import tech.lamprism.lampray.content.comment.Comment; import tech.lamprism.lampray.content.comment.CommentDetailsMetadata; import tech.lamprism.lampray.content.comment.CommentStatus; -import tech.lamprism.lampray.content.comment.persistence.CommentDo; +import tech.lamprism.lampray.content.comment.persistence.CommentEntity; import tech.lamprism.lampray.content.comment.persistence.CommentRepository; import tech.lamprism.lampray.content.common.ContentErrorCode; import tech.lamprism.lampray.content.common.ContentException; @@ -51,11 +52,14 @@ public class CommentService implements ContentPublisher, ContentCollectionProvider { private final CommentRepository commentRepository; private final ContentMetadataService contentMetadataService; + private final ResourceIdGenerator resourceIdGenerator; public CommentService(CommentRepository commentRepository, - ContentMetadataService contentMetadataService) { + ContentMetadataService contentMetadataService, + ResourceIdGenerator resourceIdGenerator) { this.commentRepository = commentRepository; this.contentMetadataService = contentMetadataService; + this.resourceIdGenerator = resourceIdGenerator; } @Override @@ -74,10 +78,11 @@ public ContentDetails publish(@NonNull UncreatedContent uncreatedContent, commentDetailsMetadata.contentType() ); checkCommentOnContent(commentOn, operator); - long parentId = checkParentId(commentDetailsMetadata.parentId(), operator); + String parentId = checkParentId(commentDetailsMetadata.parentId(), operator); - CommentDo comment = CommentDo + CommentEntity comment = CommentEntity .builder() + .setResourceId(resourceIdGenerator.nextId(ContentType.COMMENT.getSystemResourceKind())) .setUserId(operator.getUserId()) .setParentId(parentId) .setContent(uncreatedContent.getContent()) @@ -107,26 +112,22 @@ private void checkCommentOnContent( throw new ContentException(ContentErrorCode.ERROR_CONTENT_NOT_FOUND); } - private long checkParentId(Long parentId, UserIdentity operator) { - if (parentId == null || parentId == Comment.COMMENT_ROOT_ID) { + private String checkParentId(@Nullable String parentId, UserIdentity operator) { + if (parentId == null || Comment.COMMENT_ROOT_ID.equals(parentId)) { return Comment.COMMENT_ROOT_ID; } - if (parentId < 0) { - throw new IllegalArgumentException("Parent id must be greater than 0"); - } - CommentDo comment = commentRepository.findById(parentId).orElseThrow( + commentRepository.findById(parentId).orElseThrow( () -> new ContentException(ContentErrorCode.ERROR_CONTENT_NOT_FOUND) ); ContentMetadata metadata = contentMetadataService.getMetadata( new SimpleContentIdentity(parentId, ContentType.COMMENT)); - if (!checkCanCommentOn(comment, metadata, operator)) { + if (!checkCanCommentOn(metadata, operator)) { throw new ContentException(ContentErrorCode.ERROR_CONTENT_NOT_FOUND); } return parentId; } private boolean checkCanCommentOn( - Content content, ContentMetadata contentMetadata, UserIdentity operator) { if (contentMetadata.getContentStatus() != ContentStatus.PUBLISHED) { @@ -149,12 +150,12 @@ public List getContents( ContentCollectionIdentity contentCollectionIdentity) { return getCommentsBy(contentCollectionIdentity) .stream() - .map(CommentDo::lock) + .map(CommentEntity::lock) .toList(); } @NonNull - public List getCommentsBy( + public List getCommentsBy( ContentCollectionIdentity contentCollectionIdentity) { return switch (contentCollectionIdentity.getContentCollectionType()) { case COMMENTS -> commentRepository.findAll(); @@ -162,6 +163,9 @@ public List getCommentsBy( contentCollectionIdentity.getContentCollectionId(), ContentType.ARTICLE ); + case USER_COMMENTS -> commentRepository.findAllByUserId( + Long.parseLong(contentCollectionIdentity.getContentCollectionId()) + ); default -> throw new UnsupportedOperationException("Unsupported collection type: " + contentCollectionIdentity.getContentCollectionType()); }; @@ -170,7 +174,7 @@ public List getCommentsBy( @Override public boolean supportsCollection(@NonNull ContentCollectionType contentCollectionType) { return switch (contentCollectionType) { - case COMMENTS, ARTICLE_COMMENTS -> true; + case COMMENTS, ARTICLE_COMMENTS, USER_COMMENTS -> true; default -> false; }; } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/Content.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/Content.java index c0779ec6..049a6597 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/Content.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/Content.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ public interface Content extends ContentIdentity { * @return content id */ @Override - long getContentId(); + String getContentId(); /** * Get the type of the content. @@ -48,7 +48,7 @@ public interface Content extends ContentIdentity { @Override ContentType getContentType(); - static Content of(long userId, long contentId, + static Content of(long userId, @NonNull String contentId, @NonNull ContentType contentType) { return new SimpleContentInfo(userId, contentId, contentType); } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentDetails.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentDetails.java index 35696115..97823dbf 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentDetails.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentDetails.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ public interface ContentDetails extends Content, TimeAttributed { * @inheritDoc */ @Override - long getContentId(); + String getContentId(); /** * @inheritDoc diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentIdentity.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentIdentity.java index ff043a93..ab1f03f4 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentIdentity.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentIdentity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,16 +25,16 @@ /** * @author RollW */ -public interface ContentIdentity extends ContentTrait, SystemResource, Serializable { +public interface ContentIdentity extends ContentTrait, SystemResource, Serializable { @Override - long getContentId(); + String getContentId(); @Override ContentType getContentType(); @Override @NonNull - default Long getResourceId() { + default String getResourceId() { return getContentId(); } @@ -44,7 +44,7 @@ default SystemResourceKind getSystemResourceKind() { return getContentType().getSystemResourceKind(); } - static ContentIdentity of(long contentId, @NonNull ContentType contentType) { + static ContentIdentity of(@NonNull String contentId, @NonNull ContentType contentType) { return new SimpleContentIdentity(contentId, contentType); } } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentMetadata.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentMetadata.java index cb98f800..3c99250b 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentMetadata.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import space.lingu.NonNull; import tech.lamprism.lampray.DataEntity; -import tech.lamprism.lampray.LongEntityBuilder; +import tech.lamprism.lampray.EntityBuilder; import tech.rollw.common.web.system.SystemResourceKind; import java.time.OffsetDateTime; @@ -27,11 +27,11 @@ * @author RollW */ @SuppressWarnings({"ClassCanBeRecord"}) -public class ContentMetadata implements DataEntity, ContentTrait { - // only maintains the metadata of the content. +public class ContentMetadata implements DataEntity, ContentTrait { private final Long id; + private final String resourceId; private final long userId; - private final long contentId; + private final String contentId; @NonNull private final ContentType contentType; @@ -42,12 +42,13 @@ public class ContentMetadata implements DataEntity, ContentTrait { @NonNull private final ContentAccessAuthType contentAccessAuthType; - public ContentMetadata(Long id, long userId, - long contentId, + public ContentMetadata(Long id, String resourceId, long userId, + @NonNull String contentId, @NonNull ContentType contentType, @NonNull ContentStatus contentStatus, @NonNull ContentAccessAuthType contentAccessAuthType) { this.id = id; + this.resourceId = resourceId; this.userId = userId; this.contentId = contentId; this.contentType = contentType; @@ -58,6 +59,12 @@ public ContentMetadata(Long id, long userId, @SuppressWarnings("all") private void checkForNull() { + if (resourceId == null) { + throw new NullPointerException("resourceId is null"); + } + if (contentId == null) { + throw new NullPointerException("contentId is null"); + } if (contentType == null) { throw new NullPointerException("contentType is null"); } @@ -69,27 +76,21 @@ private void checkForNull() { } } - @Override - public Long getEntityId() { + public Long getId() { return id; } - /** - * ContentMetadata does not have create time. - * - * @return {@code NONE_TIME} only. - */ + @Override + public String getEntityId() { + return resourceId; + } + @NonNull @Override public OffsetDateTime getCreateTime() { return NONE_TIME; } - /** - * ContentMetadata does not have update time. - * - * @return {@code NONE_TIME} only. - */ @NonNull @Override public OffsetDateTime getUpdateTime() { @@ -101,7 +102,7 @@ public long getUserId() { } @Override - public long getContentId() { + public String getContentId() { return contentId; } @@ -135,10 +136,11 @@ public SystemResourceKind getSystemResourceKind() { return ContentMetadataResourceKind.INSTANCE; } - public static class Builder implements LongEntityBuilder { + public static class Builder implements EntityBuilder { private Long id; + private String resourceId; private long userId; - private long contentId; + private String contentId; private ContentType contentType; private ContentStatus contentStatus; private ContentAccessAuthType contentAccessAuthType; @@ -148,6 +150,7 @@ public Builder() { public Builder(ContentMetadata contentMetadata) { this.id = contentMetadata.id; + this.resourceId = contentMetadata.resourceId; this.userId = contentMetadata.userId; this.contentId = contentMetadata.contentId; this.contentType = contentMetadata.contentType; @@ -156,17 +159,26 @@ public Builder(ContentMetadata contentMetadata) { } @Override - public Builder setEntityId(Long id) { + public Builder setEntityId(String id) { + return setResourceId(id); + } + + public Builder setId(Long id) { this.id = id; return this; } + public Builder setResourceId(String resourceId) { + this.resourceId = resourceId; + return this; + } + public Builder setUserId(long userId) { this.userId = userId; return this; } - public Builder setContentId(long contentId) { + public Builder setContentId(@NonNull String contentId) { this.contentId = contentId; return this; } @@ -188,7 +200,7 @@ public Builder setContentAccessAuthType(@NonNull ContentAccessAuthType contentAc @Override public ContentMetadata build() { - return new ContentMetadata(id, userId, contentId, + return new ContentMetadata(id, resourceId, userId, contentId, contentType, contentStatus, contentAccessAuthType); } } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentMetadataDetails.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentMetadataDetails.java index a2e87359..f339038b 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentMetadataDetails.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentMetadataDetails.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,7 +53,7 @@ public long getUserId() { } @Override - public long getContentId() { + public String getContentId() { return contentMetadata.getContentId(); } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentOperator.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentOperator.java index ce8a4bbb..2c45a390 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentOperator.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentOperator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ /** * @author RollW */ -public interface ContentOperator extends SystemResourceOperator, ContentDetails { +public interface ContentOperator extends SystemResourceOperator, ContentDetails { @Override ContentOperator update() throws CommonRuntimeException; @@ -68,7 +68,7 @@ default ContentOperator getSystemResource() { @NonNull @Override - default Long getResourceId() { + default String getResourceId() { return getContentId(); } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentTrait.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentTrait.java index 57ed1631..8a2519b7 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentTrait.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentTrait.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ * @author RollW */ public interface ContentTrait { - long getContentId(); + String getContentId(); ContentType getContentType(); } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/SimpleContentDetails.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/SimpleContentDetails.java index 881640b2..74455839 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/SimpleContentDetails.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/SimpleContentDetails.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ * @author RollW */ public record SimpleContentDetails( - long contentId, + @NonNull String contentId, @NonNull ContentType contentType, long userId, @Nullable String title, @@ -36,7 +36,7 @@ public record SimpleContentDetails( @Nullable ContentDetailsMetadata contentDetailsMetadata ) implements ContentDetails { @Override - public long getContentId() { + public String getContentId() { return contentId; } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/SimpleContentIdentity.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/SimpleContentIdentity.java index b5a799eb..05e247e3 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/SimpleContentIdentity.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/SimpleContentIdentity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,11 +22,11 @@ * @author RollW */ public record SimpleContentIdentity( - long contentId, + @NonNull String contentId, @NonNull ContentType contentType ) implements ContentIdentity { @Override - public long getContentId() { + public String getContentId() { return contentId; } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/SimpleContentInfo.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/SimpleContentInfo.java index 72556f6e..8535609d 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/SimpleContentInfo.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/SimpleContentInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ */ public record SimpleContentInfo( long userId, - long contentId, + @NonNull String contentId, ContentType contentType ) implements Content { @@ -39,7 +39,7 @@ public long getUserId() { } @Override - public long getContentId() { + public String getContentId() { return contentId; } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollection.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollection.java index f72f6649..f21fc84d 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollection.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollection.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ public interface ContentCollection extends ContentCollectionIdentity { long getUserId(); - long getContentCollectionId(); + String getContentCollectionId(); ContentCollectionType getContentCollectionType(); } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollectionDetails.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollectionDetails.java index 12028171..de4ee444 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollectionDetails.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollectionDetails.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,7 +69,7 @@ public long getUserId() { } @Override - public long getContentId() { + public String getContentId() { return contentMetadata.getContentId(); } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollectionIdentity.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollectionIdentity.java index cb32efa8..c3264fec 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollectionIdentity.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollectionIdentity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +20,12 @@ * @author RollW */ public interface ContentCollectionIdentity { - long getContentCollectionId(); + String getContentCollectionId(); ContentCollectionType getContentCollectionType(); static ContentCollectionIdentity of( - long contentCollectionId, + String contentCollectionId, ContentCollectionType contentCollectionType) { return SimpleContentCollectionIdentity.of( contentCollectionId, diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollectionMetadata.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollectionMetadata.java index 92cfe5cd..6542468c 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollectionMetadata.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/collection/ContentCollectionMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ public class ContentCollectionMetadata implements DataEntity { private final ContentCollectionType type; - private final long contentId; + private final String contentId; @Nullable private final ContentType contentType; @@ -54,7 +54,7 @@ public class ContentCollectionMetadata implements DataEntity { public ContentCollectionMetadata(@Nullable Long id, long collectionId, ContentCollectionType type, - long contentId, + String contentId, @Nullable ContentType contentType, @Nullable Integer order) { this.id = id; @@ -91,7 +91,7 @@ public ContentCollectionType getType() { return type; } - public long getContentId() { + public String getContentId() { return contentId; } @@ -144,7 +144,7 @@ public static final class Builder implements LongEntityBuilder { +public class FavoriteGroup implements DataEntity { private final Long id; + private final String resourceId; private final String name; private final long userId; private final FavoriteGroupType type; - private final boolean isPublic; // add 'is' to avoid conflict with keyword + private final boolean isPublic; private final String description; private final String icon; private final OffsetDateTime createTime; private final OffsetDateTime updateTime; private final boolean deleted; - public FavoriteGroup(Long id, String name, long userId, FavoriteGroupType type, + public FavoriteGroup(Long id, String resourceId, String name, long userId, FavoriteGroupType type, boolean isPublic, String description, String icon, OffsetDateTime createTime, OffsetDateTime updateTime, boolean deleted) { this.id = id; + this.resourceId = resourceId; this.name = name; this.userId = userId; this.type = type; @@ -54,11 +56,15 @@ public FavoriteGroup(Long id, String name, long userId, FavoriteGroupType type, this.deleted = deleted; } - @Override - public Long getEntityId() { + public Long getId() { return id; } + @Override + public String getEntityId() { + return resourceId; + } + public String getName() { return name; } @@ -113,8 +119,9 @@ public static Builder builder() { return new Builder(); } - public final static class Builder implements LongEntityBuilder { + public static final class Builder implements EntityBuilder { private Long id; + private String resourceId; private String name; private long userId; private FavoriteGroupType type; @@ -130,6 +137,7 @@ public Builder() { public Builder(FavoriteGroup favoriteGroup) { this.id = favoriteGroup.id; + this.resourceId = favoriteGroup.resourceId; this.name = favoriteGroup.name; this.userId = favoriteGroup.userId; this.type = favoriteGroup.type; @@ -142,11 +150,20 @@ public Builder(FavoriteGroup favoriteGroup) { } @Override - public Builder setEntityId(Long id) { + public Builder setEntityId(String id) { + return setResourceId(id); + } + + public Builder setId(Long id) { this.id = id; return this; } + public Builder setResourceId(String resourceId) { + this.resourceId = resourceId; + return this; + } + public Builder setName(String name) { this.name = name; return this; @@ -194,7 +211,7 @@ public Builder setDeleted(boolean deleted) { @Override public FavoriteGroup build() { - return new FavoriteGroup(id, name, userId, type, isPublic, description, + return new FavoriteGroup(id, resourceId, name, userId, type, isPublic, description, icon, createTime, updateTime, deleted); } } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/favorite/FavoriteGroupOperator.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/favorite/FavoriteGroupOperator.java index 5b1adbc6..8799c965 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/favorite/FavoriteGroupOperator.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/favorite/FavoriteGroupOperator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ /** * @author RollW */ -public interface FavoriteGroupOperator extends SystemResourceOperator, SystemResource { +public interface FavoriteGroupOperator extends SystemResourceOperator, SystemResource { @Override void setCheckDeleted(boolean checkDeleted); @@ -56,7 +56,7 @@ public interface FavoriteGroupOperator extends SystemResourceOperator, Sys FavoriteGroup getFavoriteGroup(); @Override - default Long getResourceId() { + default String getResourceId() { return getFavoriteGroup().getEntityId(); } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/favorite/FavoriteItem.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/favorite/FavoriteItem.java index 4624f659..d66e4f44 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/favorite/FavoriteItem.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/favorite/FavoriteItem.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import space.lingu.NonNull; import tech.lamprism.lampray.DataEntity; -import tech.lamprism.lampray.LongEntityBuilder; +import tech.lamprism.lampray.EntityBuilder; import tech.lamprism.lampray.content.ContentAssociated; import tech.lamprism.lampray.content.ContentIdentity; import tech.lamprism.lampray.content.ContentType; @@ -29,22 +29,24 @@ /** * @author RollW */ -public class FavoriteItem implements DataEntity, ContentAssociated { +public class FavoriteItem implements DataEntity, ContentAssociated { private final Long id; - private final long groupId; + private final String resourceId; + private final String groupId; private final long userId; - private final long contentId; + private final String contentId; private final ContentType contentType; private final OffsetDateTime createTime; private final OffsetDateTime updateTime; private final boolean deleted; private final ContentIdentity associatedContent; - public FavoriteItem(Long id, long groupId, long userId, - long contentId, ContentType contentType, + public FavoriteItem(Long id, String resourceId, String groupId, long userId, + String contentId, ContentType contentType, OffsetDateTime createTime, OffsetDateTime updateTime, boolean deleted) { this.id = id; + this.resourceId = resourceId; this.groupId = groupId; this.userId = userId; this.contentId = contentId; @@ -55,12 +57,16 @@ public FavoriteItem(Long id, long groupId, long userId, this.associatedContent = ContentIdentity.of(contentId, contentType); } - @Override - public Long getEntityId() { + public Long getId() { return id; } - public long getGroupId() { + @Override + public String getEntityId() { + return resourceId; + } + + public String getGroupId() { return groupId; } @@ -68,7 +74,7 @@ public long getUserId() { return userId; } - public long getContentId() { + public String getContentId() { return contentId; } @@ -111,11 +117,12 @@ public static Builder builder() { return new Builder(); } - public static final class Builder implements LongEntityBuilder { + public static final class Builder implements EntityBuilder { private Long id; - private long groupId; + private String resourceId; + private String groupId; private long userId; - private long contentId; + private String contentId; private ContentType contentType; private OffsetDateTime createTime; private OffsetDateTime updateTime; @@ -124,24 +131,34 @@ public static final class Builder implements LongEntityBuilder { private Builder() { } - private Builder(FavoriteItem favoriteitem) { - this.id = favoriteitem.id; - this.groupId = favoriteitem.groupId; - this.userId = favoriteitem.userId; - this.contentId = favoriteitem.contentId; - this.contentType = favoriteitem.contentType; - this.createTime = favoriteitem.createTime; - this.updateTime = favoriteitem.updateTime; - this.deleted = favoriteitem.deleted; + private Builder(FavoriteItem favoriteItem) { + this.id = favoriteItem.id; + this.resourceId = favoriteItem.resourceId; + this.groupId = favoriteItem.groupId; + this.userId = favoriteItem.userId; + this.contentId = favoriteItem.contentId; + this.contentType = favoriteItem.contentType; + this.createTime = favoriteItem.createTime; + this.updateTime = favoriteItem.updateTime; + this.deleted = favoriteItem.deleted; } @Override - public Builder setEntityId(Long id) { + public Builder setEntityId(String id) { + return setResourceId(id); + } + + public Builder setId(Long id) { this.id = id; return this; } - public Builder setGroupId(long groupId) { + public Builder setResourceId(String resourceId) { + this.resourceId = resourceId; + return this; + } + + public Builder setGroupId(String groupId) { this.groupId = groupId; return this; } @@ -151,7 +168,7 @@ public Builder setUserId(long userId) { return this; } - public Builder setContentId(long contentId) { + public Builder setContentId(String contentId) { this.contentId = contentId; return this; } @@ -176,9 +193,10 @@ public Builder setDeleted(boolean deleted) { return this; } + @Override public FavoriteItem build() { - return new FavoriteItem(id, groupId, userId, contentId, + return new FavoriteItem(id, resourceId, groupId, userId, contentId, contentType, createTime, updateTime, deleted); } } -} \ No newline at end of file +} diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/favorite/FavoriteProvider.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/favorite/FavoriteProvider.java index 9ffc71ed..6bb82adf 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/favorite/FavoriteProvider.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/favorite/FavoriteProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,9 +28,9 @@ public interface FavoriteProvider { List getFavoriteGroups(UserIdentity userIdentity); - List getFavoriteItems(long favoriteGroupId); + List getFavoriteItems(String favoriteGroupId); - FavoriteGroup getFavoriteGroup(long favoriteGroupId); + FavoriteGroup getFavoriteGroup(String favoriteGroupId); - FavoriteItem getFavoriteItem(long favoriteItemId); + FavoriteItem getFavoriteItem(String favoriteItemId); } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/service/AbstractContentOperator.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/service/AbstractContentOperator.java index ffab1d55..b9bb075a 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/service/AbstractContentOperator.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/service/AbstractContentOperator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,7 +67,7 @@ public final OffsetDateTime getUpdateTime() { } @Override - public final long getContentId() { + public final String getContentId() { return content.getContentId(); } diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/service/ContentProvideService.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/service/ContentProvideService.java index 360af7f0..d294116b 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/service/ContentProvideService.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/service/ContentProvideService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,8 +37,8 @@ * @author RollW */ @Service -public class ContentProvideService implements SystemResourceProvider, - SystemResourceOperatorFactory, ContentProviderFactory { +public class ContentProvideService implements SystemResourceProvider, + SystemResourceOperatorFactory, ContentProviderFactory { private final List contentProviders; public ContentProvideService(List contentProviders) { @@ -52,8 +52,8 @@ public boolean supports(@NonNull SystemResourceKind systemResourceKind) { @NonNull @Override - public SystemResourceOperator createResourceOperator( - SystemResource systemResource, + public SystemResourceOperator createResourceOperator( + SystemResource systemResource, boolean checkDelete) { ContentType contentType = ContentType.from(systemResource.getSystemResourceKind()); if (contentType == null) { @@ -67,7 +67,7 @@ public SystemResourceOperator createResourceOperator( @NonNull @Override - public ContentDetails provide(@NonNull Long resourceId, + public ContentDetails provide(@NonNull String resourceId, @NonNull SystemResourceKind systemResourceKind) throws CommonRuntimeException, UnsupportedKindException { ContentType contentType = ContentType.from(systemResourceKind); @@ -82,7 +82,7 @@ public ContentDetails provide(@NonNull Long resourceId, @NonNull @Override - public ContentDetails provide(@NonNull SystemResource rawSystemResource) + public ContentDetails provide(@NonNull SystemResource rawSystemResource) throws CommonRuntimeException, UnsupportedKindException { if (rawSystemResource instanceof ContentDetails contentDetails) { return contentDetails; diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupDao.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupDao.kt index a968ab62..043d68cc 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupDao.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupDao.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,5 +23,5 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface FavoriteGroupDao : CommonDao { -} \ No newline at end of file +interface FavoriteGroupDao : CommonDao { +} diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupDo.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupEntity.kt similarity index 72% rename from lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupDo.kt rename to lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupEntity.kt index 4c076e04..66cccf7b 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupDo.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,15 +20,12 @@ import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated -import jakarta.persistence.GeneratedValue -import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Lob import jakarta.persistence.Table -import jakarta.persistence.Temporal -import jakarta.persistence.TemporalType +import org.hibernate.annotations.Generated import org.hibernate.annotations.JdbcTypeCode -import org.hibernate.proxy.HibernateProxy +import org.hibernate.generator.EventType import org.hibernate.type.SqlTypes import tech.lamprism.lampray.DataEntity import tech.lamprism.lampray.content.favorite.FavoriteGroup @@ -41,12 +38,15 @@ import java.time.OffsetDateTime */ @Entity @Table(name = "favorite_group") -class FavoriteGroupDo( - @Id - @Column(name = "id", nullable = false) - @GeneratedValue(strategy = GenerationType.IDENTITY) +class FavoriteGroupEntity( + @Column(name = "id", nullable = false, insertable = false, updatable = false) + @Generated(event = [EventType.INSERT]) private var id: Long? = null, + @Id + @Column(name = "resource_id", nullable = false, length = 64, unique = true) + private var resourceId: String, + @Column(name = "name", nullable = false) var name: String = "", @@ -68,18 +68,18 @@ class FavoriteGroupDo( @Column(name = "icon", nullable = false) private var icon: String = "", - @Temporal(TemporalType.TIMESTAMP) @Column(name = "create_time", nullable = false) private var createTime: OffsetDateTime = OffsetDateTime.now(), - @Temporal(TemporalType.TIMESTAMP) @Column(name = "update_time", nullable = false) private var updateTime: OffsetDateTime = OffsetDateTime.now(), @Column(name = "deleted", nullable = false) var deleted: Boolean = false -) : DataEntity { - override fun getEntityId(): Long = id!! +) : DataEntity { + override fun getEntityId(): String = resourceId + + fun getId(): Long? = id override fun getSystemResourceKind() = FavoriteGroupResourceKind @@ -87,13 +87,10 @@ class FavoriteGroupDo( override fun getUpdateTime(): OffsetDateTime = updateTime - fun setId(id: Long) { - this.id = id - } - fun lock(): FavoriteGroup = FavoriteGroup( id, + resourceId, name, userId, type, @@ -109,6 +106,7 @@ class FavoriteGroupDo( class Builder { private var id: Long? = null + private var resourceId: String? = null private var name: String = "" private var userId: Long = 0L private var type: FavoriteGroupType = FavoriteGroupType.USER @@ -121,8 +119,9 @@ class FavoriteGroupDo( constructor() - constructor(favoriteGroup: FavoriteGroupDo) { + constructor(favoriteGroup: FavoriteGroupEntity) { this.id = favoriteGroup.id + this.resourceId = favoriteGroup.resourceId this.name = favoriteGroup.name this.userId = favoriteGroup.userId this.type = favoriteGroup.type @@ -138,6 +137,10 @@ class FavoriteGroupDo( this.id = id } + fun setResourceId(resourceId: String) = apply { + this.resourceId = resourceId + } + fun setName(name: String) = apply { this.name = name } @@ -174,17 +177,18 @@ class FavoriteGroupDo( this.deleted = deleted } - fun build() = FavoriteGroupDo( - id, - name, - userId, - type, - isPublic, - description, - icon, - createTime, - updateTime, - deleted + fun build() = FavoriteGroupEntity( + id = id, + resourceId = resourceId!!, + name = name, + userId = userId, + type = type, + isPublic = isPublic, + description = description, + icon = icon, + createTime = createTime, + updateTime = updateTime, + deleted = deleted ) } @@ -193,33 +197,18 @@ class FavoriteGroupDo( fun builder() = Builder() @JvmStatic - fun FavoriteGroup.toDo() = FavoriteGroupDo( - entityId, - name, - userId, - type, - isPublic, - description, - icon, - createTime, - updateTime, - isDeleted + fun FavoriteGroup.toEntity() = FavoriteGroupEntity( + id = id, + resourceId = entityId, + name = name, + userId = userId, + type = type, + isPublic = isPublic, + description = description, + icon = icon, + createTime = createTime, + updateTime = updateTime, + deleted = isDeleted ) } - - final override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null) return false - val oEffectiveClass = - if (other is HibernateProxy) other.hibernateLazyInitializer.persistentClass else other.javaClass - val thisEffectiveClass = - if (this is HibernateProxy) this.hibernateLazyInitializer.persistentClass else this.javaClass - if (thisEffectiveClass != oEffectiveClass) return false - other as FavoriteGroupDo - - return id != null && id == other.id - } - - final override fun hashCode(): Int = - if (this is HibernateProxy) this.hibernateLazyInitializer.persistentClass.hashCode() else javaClass.hashCode() } diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupRepository.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupRepository.kt index 16f20df2..254075e7 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupRepository.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,19 +27,27 @@ import java.util.Optional @Repository class FavoriteGroupRepository( private val favoriteGroupDao: FavoriteGroupDao -) : CommonRepository(favoriteGroupDao) { - fun findByName(name: String, operator: Operator): Optional = +) : CommonRepository(favoriteGroupDao) { + override fun save(entity: S): S { + return favoriteGroupDao.saveAndFlush(entity) + } + + override fun saveAll(entities: Iterable): List { + return favoriteGroupDao.saveAllAndFlush(entities) + } + + fun findByName(name: String, operator: Operator): Optional = findOne { root, _, builder -> builder.and( - builder.equal(root.get(FavoriteGroupDo_.name), name), - builder.equal(root.get(FavoriteGroupDo_.userId), operator.operatorId) + builder.equal(root.get("name"), name), + builder.equal(root.get("userId"), operator.operatorId) ) } - fun findByUser(userId: Long): List = + fun findByUser(userId: Long): List = findAll { root, _, criteriaBuilder -> criteriaBuilder.and( - criteriaBuilder.equal(root.get(FavoriteGroupDo_.userId), userId) + criteriaBuilder.equal(root.get("userId"), userId) ) } -} \ No newline at end of file +} diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemDao.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemDao.kt index 415325f8..9cd6ddbb 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemDao.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemDao.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,5 +23,5 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface FavoriteItemDao : CommonDao { -} \ No newline at end of file +interface FavoriteItemDao : CommonDao { +} diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemDo.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemEntity.kt similarity index 67% rename from lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemDo.kt rename to lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemEntity.kt index 874aefe7..64ebab5c 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemDo.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,13 +20,11 @@ import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated -import jakarta.persistence.GeneratedValue -import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Table -import jakarta.persistence.Temporal -import jakarta.persistence.TemporalType +import org.hibernate.annotations.Generated import org.hibernate.annotations.JdbcTypeCode +import org.hibernate.generator.EventType import org.hibernate.type.SqlTypes import tech.lamprism.lampray.DataEntity import tech.lamprism.lampray.content.ContentType @@ -39,42 +37,41 @@ import java.time.OffsetDateTime */ @Entity @Table(name = "favorite_item") -class FavoriteItemDo( - @Id - @Column(name = "id", nullable = false) - @GeneratedValue(strategy = GenerationType.IDENTITY) +class FavoriteItemEntity( + @Column(name = "id", nullable = false, insertable = false, updatable = false) + @Generated(event = [EventType.INSERT]) private var id: Long? = null, + @Id + @Column(name = "resource_id", nullable = false, length = 64, unique = true) + private var resourceId: String, + @Column(name = "user_id", nullable = false) var userId: Long, - @Column(name = "group_id", nullable = false) - var groupId: Long, + @Column(name = "group_id", nullable = false, length = 64) + var groupId: String, - @Column(name = "content_id", nullable = false) - var contentId: Long = 0, + @Column(name = "content_id", nullable = false, length = 64) + var contentId: String = "", @Column(name = "content_type", nullable = false, length = 40) @Enumerated(EnumType.STRING) @JdbcTypeCode(SqlTypes.VARCHAR) var contentType: ContentType = ContentType.ARTICLE, - @Temporal(TemporalType.TIMESTAMP) @Column(name = "create_time", nullable = false) private var createTime: OffsetDateTime = OffsetDateTime.now(), - @Temporal(TemporalType.TIMESTAMP) @Column(name = "update_time", nullable = false) private var updateTime: OffsetDateTime = OffsetDateTime.now(), @Column(name = "deleted", nullable = false) var deleted: Boolean = false -) : DataEntity { - override fun getEntityId(): Long = id!! +) : DataEntity { + override fun getEntityId(): String = resourceId - fun setId(id: Long) { - this.id = id - } + fun getId(): Long? = id override fun getSystemResourceKind() = FavoriteItemResourceKind @@ -85,6 +82,7 @@ class FavoriteItemDo( fun lock(): FavoriteItem = FavoriteItem( id, + resourceId, groupId, userId, contentId, @@ -98,9 +96,10 @@ class FavoriteItemDo( class Builder { private var id: Long? = null + private var resourceId: String? = null private var userId: Long = 0 - private var groupId: Long = 0 - private var contentId: Long = 0 + private var groupId: String = "" + private var contentId: String = "" private var contentType: ContentType = ContentType.ARTICLE private var createTime: OffsetDateTime = OffsetDateTime.now() private var updateTime: OffsetDateTime = OffsetDateTime.now() @@ -108,8 +107,9 @@ class FavoriteItemDo( constructor() - constructor(favoriteItem: FavoriteItemDo) { + constructor(favoriteItem: FavoriteItemEntity) { this.id = favoriteItem.id + this.resourceId = favoriteItem.resourceId this.userId = favoriteItem.userId this.groupId = favoriteItem.groupId this.contentId = favoriteItem.contentId @@ -123,15 +123,19 @@ class FavoriteItemDo( this.id = id } + fun setResourceId(resourceId: String) = apply { + this.resourceId = resourceId + } + fun setUserId(userId: Long) = apply { this.userId = userId } - fun setGroupId(groupId: Long) = apply { + fun setGroupId(groupId: String) = apply { this.groupId = groupId } - fun setContentId(contentId: Long) = apply { + fun setContentId(contentId: String) = apply { this.contentId = contentId } @@ -151,34 +155,36 @@ class FavoriteItemDo( this.deleted = deleted } - fun build(): FavoriteItemDo { - return FavoriteItemDo( - id, - userId, - groupId, - contentId, - contentType, - createTime, - updateTime, - deleted + fun build(): FavoriteItemEntity { + return FavoriteItemEntity( + id = id, + resourceId = resourceId!!, + userId = userId, + groupId = groupId, + contentId = contentId, + contentType = contentType, + createTime = createTime, + updateTime = updateTime, + deleted = deleted ) } } companion object { @JvmStatic - fun FavoriteItem.toDo() = FavoriteItemDo( - entityId, - userId, - groupId, - contentId, - contentType, - createTime, - updateTime, - isDeleted + fun FavoriteItem.toEntity() = FavoriteItemEntity( + id = id, + resourceId = entityId, + userId = userId, + groupId = groupId, + contentId = contentId, + contentType = contentType, + createTime = createTime, + updateTime = updateTime, + deleted = isDeleted ) @JvmStatic fun builder(): Builder = Builder() } -} \ No newline at end of file +} diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemRepository.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemRepository.kt index e5e8cb0d..575d55ad 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemRepository.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,5 +25,12 @@ import tech.lamprism.lampray.common.data.CommonRepository @Repository class FavoriteItemRepository( private val favoriteItemDao: FavoriteItemDao -) : CommonRepository(favoriteItemDao) { -} \ No newline at end of file +) : CommonRepository(favoriteItemDao) { + override fun save(entity: S): S { + return favoriteItemDao.saveAndFlush(entity) + } + + override fun saveAll(entities: Iterable): List { + return favoriteItemDao.saveAllAndFlush(entities) + } +} diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataDao.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataDao.kt index ce099e37..8f5ba44b 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataDao.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataDao.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,4 +23,4 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface ContentMetadataDao : CommonDao \ No newline at end of file +interface ContentMetadataDao : CommonDao diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataDo.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataEntity.kt similarity index 67% rename from lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataDo.kt rename to lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataEntity.kt index c2c3d24a..bbfad17d 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataDo.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +20,12 @@ import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated -import jakarta.persistence.GeneratedValue -import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Table import jakarta.persistence.UniqueConstraint +import org.hibernate.annotations.Generated import org.hibernate.annotations.JdbcTypeCode +import org.hibernate.generator.EventType import org.hibernate.type.SqlTypes import tech.lamprism.lampray.DataEntity import tech.lamprism.lampray.TimeAttributed @@ -47,17 +47,20 @@ import java.time.OffsetDateTime UniqueConstraint(columnNames = ["content_id", "type"], name = "index__content_id_type") ] ) -class ContentMetadataDo( - @Column(name = "id") - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) +class ContentMetadataEntity( + @Column(name = "id", nullable = false, insertable = false, updatable = false) + @Generated(event = [EventType.INSERT]) private var id: Long? = null, + @Id + @Column(name = "resource_id", nullable = false, length = 64, unique = true) + private var resourceId: String, + @Column(name = "user_id", nullable = false) var userId: Long = 0, - @Column(name = "content_id", nullable = false) - private var contentId: Long = 0, + @Column(name = "content_id", nullable = false, length = 64) + private var contentId: String = "", @Column(name = "type", nullable = false, length = 40) @Enumerated(EnumType.STRING) @@ -73,12 +76,10 @@ class ContentMetadataDo( @Enumerated(EnumType.STRING) @JdbcTypeCode(SqlTypes.VARCHAR) var contentAccessAuthType: ContentAccessAuthType = ContentAccessAuthType.PUBLIC -) : DataEntity, ContentTrait { - override fun getEntityId(): Long? = id +) : DataEntity, ContentTrait { + override fun getEntityId(): String = resourceId - fun setId(id: Long?) { - this.id = id - } + fun getId(): Long? = id override fun getCreateTime(): OffsetDateTime = TimeAttributed.NONE_TIME @@ -87,9 +88,9 @@ class ContentMetadataDo( override fun getSystemResourceKind(): SystemResourceKind = ContentMetadataResourceKind - override fun getContentId(): Long = contentId + override fun getContentId(): String = contentId - fun setContentId(contentId: Long) { + fun setContentId(contentId: String) { this.contentId = contentId } @@ -100,7 +101,7 @@ class ContentMetadataDo( } fun lock(): ContentMetadata = ContentMetadata( - id, userId, contentId, contentType, contentStatus, contentAccessAuthType + id, resourceId, userId, contentId, contentType, contentStatus, contentAccessAuthType ) fun toBuilder(): Builder { @@ -112,29 +113,35 @@ class ContentMetadataDo( fun builder(): Builder = Builder() @JvmStatic - fun ContentMetadata.toDo() = - ContentMetadataDo( - entityId, userId, contentId, contentType, - contentStatus, contentAccessAuthType + fun ContentMetadata.toEntity() = + ContentMetadataEntity( + id = id, + resourceId = entityId, + userId = userId, + contentId = contentId, + contentType = contentType, + contentStatus = contentStatus, + contentAccessAuthType = contentAccessAuthType ) - } class Builder { private var id: Long? = null + private var resourceId: String? = null private var userId: Long = 0 - private var contentId: Long = 0 + private var contentId: String = "" private var contentType: ContentType? = null private var contentStatus: ContentStatus? = null private var contentAccessAuthType: ContentAccessAuthType? = null constructor() - constructor(other: ContentMetadataDo) { - this.id = other.getEntityId() + constructor(other: ContentMetadataEntity) { + this.id = other.id + this.resourceId = other.resourceId this.userId = other.userId - this.contentId = other.getContentId() - this.contentType = other.getContentType() + this.contentId = other.contentId + this.contentType = other.contentType this.contentStatus = other.contentStatus this.contentAccessAuthType = other.contentAccessAuthType } @@ -143,11 +150,15 @@ class ContentMetadataDo( this.id = id } + fun setResourceId(resourceId: String) = apply { + this.resourceId = resourceId + } + fun setUserId(userId: Long) = apply { this.userId = userId } - fun setContentId(contentId: Long) = apply { + fun setContentId(contentId: String) = apply { this.contentId = contentId } @@ -163,13 +174,16 @@ class ContentMetadataDo( this.contentAccessAuthType = contentAccessAuthType } - fun build(): ContentMetadataDo { - return ContentMetadataDo( - id, userId, - contentId, - contentType!!, contentStatus!!, - contentAccessAuthType!! + fun build(): ContentMetadataEntity { + return ContentMetadataEntity( + id = id, + resourceId = resourceId!!, + userId = userId, + contentId = contentId, + contentType = contentType!!, + contentStatus = contentStatus!!, + contentAccessAuthType = contentAccessAuthType!! ) } } -} \ No newline at end of file +} diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataRepository.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataRepository.kt index 4fd5beda..c395f508 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataRepository.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package tech.lamprism.lampray.content.persistence import org.springframework.stereotype.Repository import tech.lamprism.lampray.common.data.CommonRepository import tech.lamprism.lampray.content.ContentTrait +import tech.lamprism.lampray.content.ContentType import java.util.Optional /** @@ -27,28 +28,36 @@ import java.util.Optional @Repository class ContentMetadataRepository( private val contentMetadataDao: ContentMetadataDao -) : CommonRepository(contentMetadataDao) { - fun findByContent(content: ContentTrait): Optional { - return findOne { root, query, criteriaBuilder -> +) : CommonRepository(contentMetadataDao) { + override fun save(entity: S): S { + return contentMetadataDao.saveAndFlush(entity) + } + + override fun saveAll(entities: Iterable): List { + return contentMetadataDao.saveAllAndFlush(entities) + } + + fun findByContent(content: ContentTrait): Optional { + return findOne { root, _, criteriaBuilder -> criteriaBuilder.and( - criteriaBuilder.equal(root.get(ContentMetadataDo_.contentId), content.contentId), - criteriaBuilder.equal(root.get(ContentMetadataDo_.contentType), content.contentType) + criteriaBuilder.equal(root.get("contentId"), content.contentId), + criteriaBuilder.equal(root.get("contentType"), content.contentType) ) } } - fun findByContents(contents: List): List { + fun findByContents(contents: List): List { if (contents.isEmpty()) { return emptyList() } return findAll { root, _, builder -> val predicates = contents.map { builder.and( - builder.equal(root.get(ContentMetadataDo_.contentId), it.contentId), - builder.equal(root.get(ContentMetadataDo_.contentType), it.contentType) + builder.equal(root.get("contentId"), it.contentId), + builder.equal(root.get("contentType"), it.contentType) ) } builder.or(*predicates.toTypedArray()) } } -} \ No newline at end of file +} diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/service/ContentMetadataServiceImpl.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/service/ContentMetadataServiceImpl.kt index 31562495..7bbffdec 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/service/ContentMetadataServiceImpl.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/service/ContentMetadataServiceImpl.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import tech.lamprism.lampray.content.ContentMetadata import tech.lamprism.lampray.content.ContentTrait import tech.lamprism.lampray.content.common.ContentErrorCode import tech.lamprism.lampray.content.common.ContentException -import tech.lamprism.lampray.content.persistence.ContentMetadataDo.Companion.toDo +import tech.lamprism.lampray.content.persistence.ContentMetadataEntity.Companion.toEntity import tech.lamprism.lampray.content.persistence.ContentMetadataRepository /** @@ -44,13 +44,13 @@ class ContentMetadataServiceImpl( } fun createMetadata(metadata: ContentMetadata) { - contentMetadataRepository.save(metadata.toDo()) + contentMetadataRepository.save(metadata.toEntity()) } override fun updateMetadata(metadata: ContentMetadata) { - contentMetadataRepository.save(metadata.toDo()) + contentMetadataRepository.save(metadata.toEntity()) } fun deleteMetadata(contentTrait: ContentTrait) { } -} \ No newline at end of file +} diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/service/ContentService.java b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/service/ContentService.java index 0aff6c62..016e97b9 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/service/ContentService.java +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/service/ContentService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +import tech.lamprism.lampray.common.data.ResourceIdGenerator; import tech.lamprism.lampray.content.Content; import tech.lamprism.lampray.content.ContentAccessAuthType; import tech.lamprism.lampray.content.ContentAccessCredentials; @@ -27,6 +28,7 @@ import tech.lamprism.lampray.content.ContentDetails; import tech.lamprism.lampray.content.ContentMetadata; import tech.lamprism.lampray.content.ContentMetadataDetails; +import tech.lamprism.lampray.content.ContentMetadataResourceKind; import tech.lamprism.lampray.content.ContentProvider; import tech.lamprism.lampray.content.ContentProviderFactory; import tech.lamprism.lampray.content.ContentPublishProvider; @@ -44,7 +46,7 @@ import tech.lamprism.lampray.content.common.ContentException; import tech.lamprism.lampray.content.permit.ContentPermitChecker; import tech.lamprism.lampray.content.permit.ContentPermitResult; -import tech.lamprism.lampray.content.persistence.ContentMetadataDo; +import tech.lamprism.lampray.content.persistence.ContentMetadataEntity; import tech.lamprism.lampray.content.persistence.ContentMetadataRepository; import tech.lamprism.lampray.content.publish.ContentPublishListener; import tech.rollw.common.web.CommonErrorCode; @@ -72,6 +74,7 @@ public class ContentService implements ContentAccessService, private final ContentProviderFactory contentProviderFactory; private final ContentPermitChecker contentPermitChecker; private final ContentMetadataRepository contentMetadataRepository; + private final ResourceIdGenerator resourceIdGenerator; public ContentService(List contentPublishers, List uncreatedContentPreCheckers, @@ -79,7 +82,8 @@ public ContentService(List contentPublishers, List contentPublishListeners, ContentProviderFactory contentProviderFactory, ContentPermitChecker contentPermitChecker, - ContentMetadataRepository contentMetadataRepository) { + ContentMetadataRepository contentMetadataRepository, + ResourceIdGenerator resourceIdGenerator) { this.contentPublishers = contentPublishers; this.uncreatedContentPreCheckers = uncreatedContentPreCheckers; this.contentCollectionProviders = contentCollectionProviders; @@ -87,6 +91,7 @@ public ContentService(List contentPublishers, this.contentProviderFactory = contentProviderFactory; this.contentPermitChecker = contentPermitChecker; this.contentMetadataRepository = contentMetadataRepository; + this.resourceIdGenerator = resourceIdGenerator; } /** @@ -95,7 +100,7 @@ public ContentService(List contentPublishers, @Override public ContentDetails openContent(ContentTrait contentTrait, ContentAccessCredentials contentAccessCredentials) throws ContentException { - ContentMetadataDo metadata = contentMetadataRepository + ContentMetadataEntity metadata = contentMetadataRepository .findByContent(contentTrait) .orElseThrow(() -> new ContentException(ContentErrorCode.ERROR_CONTENT_NOT_FOUND)); ErrorCode errorCode = fromContentStatus(metadata.getContentStatus()); @@ -129,7 +134,7 @@ public ContentDetails getContentDetails(ContentTrait contentTrait) @Override public ContentMetadataDetails getContentMetadataDetails(ContentTrait contentTrait) throws ContentException { - ContentMetadataDo metadata = contentMetadataRepository + ContentMetadataEntity metadata = contentMetadataRepository .findByContent(contentTrait) .orElse(null); if (metadata == null) { @@ -147,7 +152,7 @@ public ContentMetadataDetails getContentMetadataDetails(ContentTrait contentT */ public ContentStatus getContentStatus(ContentTrait contentTrait) throws ContentException { - ContentMetadataDo metadata = contentMetadataRepository + ContentMetadataEntity metadata = contentMetadataRepository .findByContent(contentTrait) .orElse(null); if (metadata == null) { @@ -187,8 +192,9 @@ public ContentDetails publishContent(UncreatedContent uncreatedContent) throws C uncreatedContent, timestamp ); - ContentMetadataDo.Builder contentMetadataBuilder = ContentMetadataDo + ContentMetadataEntity.Builder contentMetadataBuilder = ContentMetadataEntity .builder() + .setResourceId(resourceIdGenerator.nextId(ContentMetadataResourceKind.INSTANCE)) .setContentId(contentDetails.getContentId()) .setContentType(contentDetails.getContentType()) .setUserId(contentDetails.getUserId()) @@ -210,7 +216,7 @@ private List> pairWith( List contentMetadata) { return contentDetails.stream().map(details -> { ContentMetadata metadata = contentMetadata.stream() - .filter(m -> m.getContentId() == details.getContentId()) + .filter(m -> m.getContentId().equals(details.getContentId())) .findFirst() .orElseThrow(() -> new ContentException(ContentErrorCode.ERROR_CONTENT_NOT_FOUND)); return new ContentMetadataDetails<>(details, metadata); @@ -246,7 +252,7 @@ public List> getContents( List contentMetadatas = contentMetadataRepository .findByContents(contents) .stream() - .map(ContentMetadataDo::lock) + .map(ContentMetadataEntity::lock) .toList(); return pairWith(contents, contentMetadatas); } diff --git a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/ReviewJob.java b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/ReviewJob.java index 03aef15c..edcdd007 100644 --- a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/ReviewJob.java +++ b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/ReviewJob.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ public class ReviewJob implements DataEntity, ContentAssociated, ReviewJobSummary { private final Long id; private final String jobId; - private final long reviewContentId; + private final String reviewContentId; private final ContentType reviewContentType; private final ReviewStatus status; @@ -44,7 +44,7 @@ public class ReviewJob implements DataEntity, ContentAssociated, ReviewJ private final ReviewMark reviewMark; private final ContentIdentity associatedContent; - public ReviewJob(Long id, String jobId, long reviewContentId, + public ReviewJob(Long id, String jobId, String reviewContentId, ContentType reviewContentType, ReviewStatus status, OffsetDateTime createTime, OffsetDateTime updateTime, @@ -73,7 +73,7 @@ public long getId() { return id; } - public long getReviewContentId() { + public String getReviewContentId() { return reviewContentId; } @@ -161,7 +161,7 @@ public String getJobId() { public final static class Builder implements EntityBuilder { private Long id = null; private String jobId; - private long reviewContentId; + private String reviewContentId; private ReviewStatus status; private ContentType reviewContentType; private OffsetDateTime assignedTime; @@ -196,7 +196,7 @@ public Builder setJobId(String jobId) { return this; } - public Builder setReviewContentId(long reviewContentId) { + public Builder setReviewContentId(String reviewContentId) { this.reviewContentId = reviewContentId; return this; } diff --git a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/feedback/ReviewCategory.kt b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/feedback/ReviewCategory.kt index 15a9e003..cf53e252 100644 --- a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/feedback/ReviewCategory.kt +++ b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/feedback/ReviewCategory.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,21 +21,6 @@ package tech.lamprism.lampray.content.review.feedback * @author RollW */ enum class ReviewCategory { - /** - * Content quality issues. - */ - CONTENT_QUALITY, - - /** - * Grammar or spelling issues. - */ - GRAMMAR, - - /** - * Formatting or structure issues. - */ - FORMAT, - /** * Policy or guideline violations. */ @@ -51,11 +36,6 @@ enum class ReviewCategory { */ COPYRIGHT, - /** - * Technical or implementation issues. - */ - TECHNICAL, - /** * Other unclassified issues. */ diff --git a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewJobEntity.kt b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewJobEntity.kt index 557e8ede..579bc523 100644 --- a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewJobEntity.kt +++ b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewJobEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,8 +55,8 @@ class ReviewJobEntity( @Column(name = "resource_id", nullable = false, length = 64, unique = true) var resourceId: String = "", - @Column(name = "content_id", nullable = false) - var reviewContentId: Long = 0, + @Column(name = "content_id", nullable = false, length = 64) + var reviewContentId: String = "", @Column(name = "content_type", nullable = false, length = 40) @Enumerated(EnumType.STRING) @@ -123,7 +123,7 @@ class ReviewJobEntity( class Builder { private var id: Long? = null private var resourceId: String? = null - private var reviewContentId: Long = 0 + private var reviewContentId: String = "" private var reviewContentType: ContentType = ContentType.ARTICLE private var status: ReviewStatus = ReviewStatus.PENDING private var createTime: OffsetDateTime = OffsetDateTime.now() @@ -151,7 +151,7 @@ class ReviewJobEntity( this.resourceId = resourceId } - fun setReviewContentId(reviewContentId: Long) = apply { + fun setReviewContentId(reviewContentId: String) = apply { this.reviewContentId = reviewContentId } @@ -191,7 +191,7 @@ class ReviewJobEntity( companion object { @JvmStatic - fun ReviewJob.toDo(): ReviewJobEntity = ReviewJobEntity( + fun ReviewJob.toEntity(): ReviewJobEntity = ReviewJobEntity( id, jobId, reviewContentId = reviewContentId, reviewContentType = reviewContentType, @@ -204,4 +204,4 @@ class ReviewJobEntity( @JvmStatic fun builder(): Builder = Builder() } -} \ No newline at end of file +} diff --git a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewJobRepository.kt b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewJobRepository.kt index 7a9f3ff3..f62c1f72 100644 --- a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewJobRepository.kt +++ b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewJobRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ class ReviewJobRepository( } fun findByContent( - contentId: Long, + contentId: String, contentType: ContentType, ): List = findAll { root, _, criteriaBuilder -> criteriaBuilder.and( @@ -47,7 +47,7 @@ class ReviewJobRepository( } fun findByContentAndStatus( - contentId: Long, + contentId: String, contentType: ContentType, reviewStatus: ReviewStatus ): List = findAll { root, _, criteriaBuilder -> @@ -111,4 +111,4 @@ class ReviewJobRepository( jobPredicate ) } -} \ No newline at end of file +} diff --git a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewTaskEntity.kt b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewTaskEntity.kt index 7c26cdf1..84b7c185 100644 --- a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewTaskEntity.kt +++ b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewTaskEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -221,7 +221,7 @@ class ReviewTaskEntity( * Converts immutable [ReviewTask] to mutable entity. */ @JvmStatic - fun ReviewTask.toDo(): ReviewTaskEntity = ReviewTaskEntity( + fun ReviewTask.toEntity(): ReviewTaskEntity = ReviewTaskEntity( id, resourceId = taskId, reviewJobId = reviewJobId, diff --git a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/RegisterVerificationToken.java b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/RegisterVerificationToken.java index e277900f..215f7e7e 100644 --- a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/RegisterVerificationToken.java +++ b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/RegisterVerificationToken.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ import space.lingu.NonNull; import tech.lamprism.lampray.DataEntity; -import tech.lamprism.lampray.LongEntityBuilder; +import tech.lamprism.lampray.EntityBuilder; import tech.lamprism.lampray.security.authentication.VerifiableToken; import tech.rollw.common.web.system.SystemResourceKind; @@ -32,17 +32,18 @@ */ public record RegisterVerificationToken( Long id, + String resourceId, String token, long userId, - long expiryTime,// timestamp + long expiryTime, boolean used -) implements VerifiableToken, DataEntity { +) implements VerifiableToken, DataEntity { public boolean isExpired() { return System.currentTimeMillis() > expiryTime; } - private static final int EXPIRATION = 60 * 24;// min + private static final int EXPIRATION = 60 * 24; public static long calculateExpiryDate(int expiryTimeInMinutes) { long now = System.currentTimeMillis(); @@ -59,8 +60,8 @@ public Date toDate() { } @Override - public Long getEntityId() { - return id; + public String getEntityId() { + return resourceId; } @NonNull @@ -103,8 +104,9 @@ public boolean isUsable() { return !used && !isExpired(); } - public static final class Builder implements LongEntityBuilder { + public static final class Builder implements EntityBuilder { private Long id; + private String resourceId; private String token; private long userId; private long expiryTime; @@ -115,6 +117,7 @@ public Builder() { public Builder(RegisterVerificationToken source) { this.id = source.id; + this.resourceId = source.resourceId; this.token = source.token; this.userId = source.userId; this.expiryTime = source.expiryTime; @@ -122,11 +125,20 @@ public Builder(RegisterVerificationToken source) { } @Override - public Builder setEntityId(Long id) { + public Builder setEntityId(String id) { + return setResourceId(id); + } + + public Builder setId(Long id) { this.id = id; return this; } + public Builder setResourceId(String resourceId) { + this.resourceId = resourceId; + return this; + } + public Builder setToken(String token) { this.token = token; return this; @@ -149,7 +161,7 @@ public Builder setUsed(boolean used) { @Override public RegisterVerificationToken build() { - return new RegisterVerificationToken(id, token, userId, expiryTime, used); + return new RegisterVerificationToken(id, resourceId, token, userId, expiryTime, used); } } } diff --git a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenDao.kt b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenDao.kt index f641b5d1..b25488b9 100644 --- a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenDao.kt +++ b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenDao.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,5 +23,5 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface RegisterTokenDao : CommonDao { -} \ No newline at end of file +interface RegisterTokenDao : CommonDao { +} diff --git a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenDo.kt b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenEntity.kt similarity index 67% rename from lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenDo.kt rename to lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenEntity.kt index 038bcd11..d1b998de 100644 --- a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenDo.kt +++ b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ package tech.lamprism.lampray.security.authentication.registration.repository import jakarta.persistence.Column import jakarta.persistence.Entity -import jakarta.persistence.GeneratedValue -import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Table import jakarta.persistence.UniqueConstraint +import org.hibernate.annotations.Generated +import org.hibernate.generator.EventType import tech.lamprism.lampray.DataEntity import tech.lamprism.lampray.TimeAttributed import tech.lamprism.lampray.security.authentication.VerifiableToken @@ -41,12 +41,15 @@ import java.time.OffsetDateTime UniqueConstraint(columnNames = ["token"], name = "index__token") ] ) -class RegisterTokenDo( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id") +class RegisterTokenEntity( + @Column(name = "id", nullable = false, insertable = false, updatable = false) + @Generated(event = [EventType.INSERT]) private var id: Long? = null, + @Id + @Column(name = "resource_id", nullable = false, length = 64, unique = true) + private var resourceId: String, + @Column(name = "token") var token: String = "", @@ -58,13 +61,13 @@ class RegisterTokenDo( @Column(name = "used") var used: Boolean = false -) : VerifiableToken, DataEntity { +) : VerifiableToken, DataEntity { override fun token(): String { return token } override fun getUserId(): Long { - return userId + return userId } fun setUserId(userId: Long) { @@ -75,12 +78,10 @@ class RegisterTokenDo( return !used && !isExpired() } - fun setId(id: Long?) { - this.id = id - } + fun getId(): Long? = id - override fun getEntityId(): Long? { - return id + override fun getEntityId(): String { + return resourceId } override fun getCreateTime(): OffsetDateTime = TimeAttributed.NONE_TIME @@ -91,8 +92,7 @@ class RegisterTokenDo( RegisterTokenResourceKind fun lock(): RegisterVerificationToken = RegisterVerificationToken( - id, token, userId, - expiryTime, used + id, resourceId, token, userId, expiryTime, used ) fun toBuilder(): Builder { @@ -109,8 +109,13 @@ class RegisterTokenDo( companion object { @JvmStatic - fun RegisterVerificationToken.toDo(): RegisterTokenDo = RegisterTokenDo( - entityId, token, userId, expiryTime, used + fun RegisterVerificationToken.toEntity(): RegisterTokenEntity = RegisterTokenEntity( + id = id, + resourceId = entityId, + token = token, + userId = userId, + expiryTime = expiryTime, + used = used ) @JvmStatic @@ -121,6 +126,7 @@ class RegisterTokenDo( class Builder { private var id: Long? = null + private var resourceId: String? = null private var token: String = "" private var userId: Long = 0 private var expiryTime: Long = 0 @@ -128,12 +134,13 @@ class RegisterTokenDo( internal constructor() - internal constructor(registerTokenDo: RegisterTokenDo) { - id = registerTokenDo.id - token = registerTokenDo.token - userId = registerTokenDo.userId - expiryTime = registerTokenDo.expiryTime - used = registerTokenDo.used + internal constructor(registerTokenEntity: RegisterTokenEntity) { + id = registerTokenEntity.id + resourceId = registerTokenEntity.resourceId + token = registerTokenEntity.token + userId = registerTokenEntity.userId + expiryTime = registerTokenEntity.expiryTime + used = registerTokenEntity.used } fun setId(id: Long?): Builder { @@ -141,6 +148,11 @@ class RegisterTokenDo( return this } + fun setResourceId(resourceId: String): Builder { + this.resourceId = resourceId + return this + } + fun setToken(token: String): Builder { this.token = token return this @@ -160,5 +172,16 @@ class RegisterTokenDo( this.used = used return this } + + fun build(): RegisterTokenEntity { + return RegisterTokenEntity( + id = id, + resourceId = resourceId!!, + token = token, + userId = userId, + expiryTime = expiryTime, + used = used + ) + } } -} \ No newline at end of file +} diff --git a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenRepository.kt b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenRepository.kt index 7612099d..560498d8 100644 --- a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenRepository.kt +++ b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,10 +26,14 @@ import java.util.Optional @Repository class RegisterTokenRepository( private val registerTokenDao: RegisterTokenDao -) : CommonRepository(registerTokenDao) { - fun findByToken(token: String): Optional { +) : CommonRepository(registerTokenDao) { + override fun save(entity: S): S { + return registerTokenDao.saveAndFlush(entity) + } + + fun findByToken(token: String): Optional { return findOne { root, _, cb -> - cb.equal(root.get(RegisterTokenDo_.token), token) + cb.equal(root.get("token"), token) } } -} \ No newline at end of file +} diff --git a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/service/LoginRegisterService.java b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/service/LoginRegisterService.java index 3f500b1a..03abd67d 100644 --- a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/service/LoginRegisterService.java +++ b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/service/LoginRegisterService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ import space.lingu.NonNull; import tech.lamprism.lampray.LampException; import tech.lamprism.lampray.RequestMetadata; +import tech.lamprism.lampray.common.data.ResourceIdGenerator; import tech.lamprism.lampray.security.authentication.UserInfoSignature; import tech.lamprism.lampray.security.authentication.VerifiableToken; import tech.lamprism.lampray.security.authentication.adapter.PreUserAuthenticationToken; @@ -39,10 +40,11 @@ import tech.lamprism.lampray.security.authentication.registration.OnUserRegistrationEvent; import tech.lamprism.lampray.security.authentication.registration.RegisterProvider; import tech.lamprism.lampray.security.authentication.registration.RegisterTokenProvider; +import tech.lamprism.lampray.security.authentication.registration.RegisterTokenResourceKind; import tech.lamprism.lampray.security.authentication.registration.RegisterVerificationToken; import tech.lamprism.lampray.security.authentication.registration.Registration; import tech.lamprism.lampray.security.authentication.registration.RegistrationInterceptor; -import tech.lamprism.lampray.security.authentication.registration.repository.RegisterTokenDo; +import tech.lamprism.lampray.security.authentication.registration.repository.RegisterTokenEntity; import tech.lamprism.lampray.security.authentication.registration.repository.RegisterTokenRepository; import tech.lamprism.lampray.security.firewall.FirewallAccessRequest; import tech.lamprism.lampray.security.firewall.FirewallRegistry; @@ -78,6 +80,7 @@ public class LoginRegisterService implements LoginProvider, RegisterTokenProvide private final RegisterTokenRepository registerTokenRepository; private final FirewallRegistry firewallRegistry; private final SystemResourceOperatorProvider systemResourceOperatorProvider; + private final ResourceIdGenerator resourceIdGenerator; private final UserProvider userProvider; private final UserManageService userManageService; private final ApplicationEventPublisher eventPublisher; @@ -91,6 +94,7 @@ public LoginRegisterService(List strategies, RegisterTokenRepository registerTokenRepository, FirewallRegistry firewallRegistry, SystemResourceOperatorProvider systemResourceOperatorProvider, + ResourceIdGenerator resourceIdGenerator, UserProvider userProvider, UserManageService userManageService, ApplicationEventPublisher eventPublisher, @@ -100,6 +104,7 @@ public LoginRegisterService(List strategies, this.registerTokenRepository = registerTokenRepository; this.firewallRegistry = firewallRegistry; this.systemResourceOperatorProvider = systemResourceOperatorProvider; + this.resourceIdGenerator = resourceIdGenerator; this.userProvider = userProvider; this.userManageService = userManageService; this.eventPublisher = eventPublisher; @@ -236,16 +241,20 @@ public VerifiableToken createRegisterToken(UserIdentity userIdentity) { UUID uuid = UUID.randomUUID(); String token = uuid.toString(); long expiryTime = RegisterVerificationToken.calculateExpiryDate(); - RegisterTokenDo registerVerificationToken = new RegisterTokenDo( - null, token, userIdentity.getUserId(), expiryTime, false - ); + RegisterTokenEntity registerVerificationToken = RegisterTokenEntity.Companion.builder() + .setResourceId(resourceIdGenerator.nextId(RegisterTokenResourceKind.INSTANCE)) + .setToken(token) + .setUserId(userIdentity.getUserId()) + .setExpiryTime(expiryTime) + .setUsed(false) + .build(); registerVerificationToken = registerTokenRepository.save(registerVerificationToken); return registerVerificationToken.lock(); } @Override public RegisterVerificationToken getRegisterToken(String token) { - RegisterTokenDo registerTokenDo = registerTokenRepository.findByToken(token) + RegisterTokenEntity registerTokenDo = registerTokenRepository.findByToken(token) .orElseThrow(() -> new AuthenticationException(AuthErrorCode.ERROR_TOKEN_NOT_EXIST)); if (registerTokenDo.isExpired() || registerTokenDo.getUsed()) { throw new AuthenticationException(AuthErrorCode.ERROR_TOKEN_NOT_EXIST); @@ -271,7 +280,7 @@ private AttributedUser retrieveUser(UserTrait user) { @Override public void verifyRegisterToken(String token) { - RegisterTokenDo registerTokenDo = registerTokenRepository.findByToken(token) + RegisterTokenEntity registerTokenDo = registerTokenRepository.findByToken(token) .orElseThrow(() -> new AuthenticationException(AuthErrorCode.ERROR_TOKEN_NOT_EXIST)); if (registerTokenDo.getUsed()) { throw new AuthenticationException(AuthErrorCode.ERROR_TOKEN_USED); diff --git a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/LocalizedMessage.kt b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/LocalizedMessage.kt index 460e1f42..95c112cc 100644 --- a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/LocalizedMessage.kt +++ b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/LocalizedMessage.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package tech.lamprism.lampray.system.resource import space.lingu.NonNull import tech.lamprism.lampray.DataEntity +import tech.lamprism.lampray.EntityBuilder import tech.lamprism.lampray.TimeAttributed import tech.rollw.common.web.system.SystemResourceKind import java.time.OffsetDateTime @@ -32,12 +33,15 @@ import java.util.Locale */ data class LocalizedMessage( private val id: Long?, + private val resourceId: String, override val key: String, override val value: String, override val locale: Locale, private val updateTime: OffsetDateTime -) : LocalizedMessageResource, DataEntity { - override fun getEntityId(): Long? = id +) : LocalizedMessageResource, DataEntity { + override fun getEntityId(): String = resourceId + + fun getId(): Long? = id override fun getCreateTime(): OffsetDateTime = TimeAttributed.NONE_TIME @@ -50,8 +54,9 @@ data class LocalizedMessage( fun toBuilder() = Builder(this) - class Builder { + class Builder : EntityBuilder { private var id: Long? = null + private var resourceId: String? = null private var key: String? = null private var value: String? = null private var locale: Locale? = null @@ -61,6 +66,7 @@ data class LocalizedMessage( constructor(localizedMessage: LocalizedMessage) { this.id = localizedMessage.id + this.resourceId = localizedMessage.resourceId this.key = localizedMessage.key this.value = localizedMessage.value this.locale = localizedMessage.locale @@ -71,6 +77,14 @@ data class LocalizedMessage( this.id = id } + override fun setEntityId(id: String) = apply { + this.resourceId = id + } + + fun setResourceId(resourceId: String) = apply { + this.resourceId = resourceId + } + fun setKey(key: String) = apply { this.key = key } @@ -87,8 +101,8 @@ data class LocalizedMessage( this.updateTime = updateTime } - fun build(): LocalizedMessage = - LocalizedMessage(id, key!!, value!!, locale!!, updateTime!!) + override fun build(): LocalizedMessage = + LocalizedMessage(id, resourceId!!, key!!, value!!, locale!!, updateTime!!) } companion object { @@ -100,10 +114,13 @@ data class LocalizedMessage( key: String, value: String, locale: Locale ): LocalizedMessage { + val now = OffsetDateTime.now() return builder() + .setResourceId("") .setKey(key) .setValue(value) .setLocale(locale) + .setUpdateTime(now) .build() } } diff --git a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageDao.kt b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageDao.kt index 7c0a9590..1c27617b 100644 --- a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageDao.kt +++ b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageDao.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,5 +23,5 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface LocalizedMessageDao : CommonDao { -} \ No newline at end of file +interface LocalizedMessageDao : CommonDao { +} diff --git a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageDo.kt b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageEntity.kt similarity index 75% rename from lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageDo.kt rename to lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageEntity.kt index ff9f031d..8cdec0d3 100644 --- a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageDo.kt +++ b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,14 +19,14 @@ package tech.lamprism.lampray.system.resource.data import jakarta.persistence.Column import jakarta.persistence.Convert import jakarta.persistence.Entity -import jakarta.persistence.GeneratedValue -import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Lob import jakarta.persistence.Table import jakarta.persistence.Temporal import jakarta.persistence.TemporalType import jakarta.persistence.UniqueConstraint +import org.hibernate.annotations.Generated +import org.hibernate.generator.EventType import tech.lamprism.lampray.DataEntity import tech.lamprism.lampray.TimeAttributed import tech.lamprism.lampray.common.data.LocaleAttributeConverter @@ -46,12 +46,15 @@ import java.util.Locale UniqueConstraint(columnNames = ["key", "locale"], name = "index__key_locale") ] ) -class LocalizedMessageDo( - @Id - @Column(name = "id") - @GeneratedValue(strategy = GenerationType.IDENTITY) +class LocalizedMessageEntity( + @Column(name = "id", nullable = false, insertable = false, updatable = false) + @Generated(event = [EventType.INSERT]) private var id: Long? = null, + @Id + @Column(name = "resource_id", nullable = false, length = 64, unique = true) + private var resourceId: String = "", + @Column(name = "key", nullable = false, length = 255) override var key: String = "", @@ -66,15 +69,21 @@ class LocalizedMessageDo( @Temporal(TemporalType.TIMESTAMP) @Column(name = "update_time", nullable = false) private var updateTime: OffsetDateTime = OffsetDateTime.now() -) : LocalizedMessageResource, DataEntity { - override fun getEntityId(): Long? { - return id +) : LocalizedMessageResource, DataEntity { + override fun getEntityId(): String { + return resourceId } + fun getId(): Long? = id + fun setId(id: Long?) { this.id = id } + fun setResourceId(resourceId: String) { + this.resourceId = resourceId + } + override fun getCreateTime(): OffsetDateTime = TimeAttributed.NONE_TIME override fun getUpdateTime(): OffsetDateTime = updateTime @@ -88,12 +97,12 @@ class LocalizedMessageDo( } fun lock(): LocalizedMessage { - return LocalizedMessage(id, key, value, locale, updateTime) + return LocalizedMessage(id, resourceId, key, value, locale, updateTime) } companion object { - fun LocalizedMessage.toDo(): LocalizedMessageDo { - return LocalizedMessageDo(entityId, key, value, locale, updateTime) + fun LocalizedMessage.toEntity(): LocalizedMessageEntity { + return LocalizedMessageEntity(getId(), entityId, key, value, locale, updateTime) } } -} \ No newline at end of file +} diff --git a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageRepository.kt b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageRepository.kt index 70c1ee25..4bbce313 100644 --- a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageRepository.kt +++ b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,27 +28,31 @@ import java.util.Locale @Repository class LocalizedMessageRepository( private val localizedMessageDao: LocalizedMessageDao -) : CommonRepository(localizedMessageDao) { +) : CommonRepository(localizedMessageDao) { + override fun save(entity: S): S { + return localizedMessageDao.saveAndFlush(entity) + } + fun findByKey(key: String, locale: Locale) = findOne { root, _, criteriaBuilder -> criteriaBuilder.and( - criteriaBuilder.equal(root.get(LocalizedMessageDo_.key), key), - criteriaBuilder.equal(root.get(LocalizedMessageDo_.locale), locale) + criteriaBuilder.equal(root.get("key"), key), + criteriaBuilder.equal(root.get("locale"), locale) ) } fun findByKey( key: String, pageable: Pageable = Pageable.unpaged() - ): Page = + ): Page = findAll(pageable) { root, _, criteriaBuilder -> - criteriaBuilder.equal(root.get(LocalizedMessageDo_.key), key) + criteriaBuilder.equal(root.get("key"), key) } fun deleteByKey(key: String) { delete { root, _, criteriaBuilder -> criteriaBuilder.and( - criteriaBuilder.equal(root.get(LocalizedMessageDo_.key), key) + criteriaBuilder.equal(root.get("key"), key) ) } } @@ -56,9 +60,9 @@ class LocalizedMessageRepository( fun deleteByKey(key: String, locale: Locale) { delete { root, _, criteriaBuilder -> criteriaBuilder.and( - criteriaBuilder.equal(root.get(LocalizedMessageDo_.key), key), - criteriaBuilder.equal(root.get(LocalizedMessageDo_.locale), locale) + criteriaBuilder.equal(root.get("key"), key), + criteriaBuilder.equal(root.get("locale"), locale) ) } } -} \ No newline at end of file +} diff --git a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/service/LocalizedMessageResourceService.kt b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/service/LocalizedMessageResourceService.kt index 21d65f85..24b155ae 100644 --- a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/service/LocalizedMessageResourceService.kt +++ b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/service/LocalizedMessageResourceService.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,12 @@ package tech.lamprism.lampray.system.resource.service import org.springframework.stereotype.Service +import tech.lamprism.lampray.common.data.ResourceIdGenerator import tech.lamprism.lampray.system.resource.LocalizedMessageResource +import tech.lamprism.lampray.system.resource.LocalizedMessageResourceKind import tech.lamprism.lampray.system.resource.LocalizedMessageResourceProvider import tech.lamprism.lampray.system.resource.SimpleLocalizedMessageResource -import tech.lamprism.lampray.system.resource.data.LocalizedMessageDo +import tech.lamprism.lampray.system.resource.data.LocalizedMessageEntity import tech.lamprism.lampray.system.resource.data.LocalizedMessageRepository import java.time.OffsetDateTime import java.util.Locale @@ -31,7 +33,8 @@ import java.util.function.Supplier */ @Service class LocalizedMessageResourceService( - private val localizedMessageRepository: LocalizedMessageRepository + private val localizedMessageRepository: LocalizedMessageRepository, + private val resourceIdGenerator: ResourceIdGenerator ) : LocalizedMessageResourceProvider { private var _fallbackLocale: Locale = Locale.ROOT @@ -46,19 +49,20 @@ class LocalizedMessageResourceService( value: String, locale: Locale ) { - val localizedMessageDo = localizedMessageRepository - .findByKey(key, locale).orElse(null) ?: LocalizedMessageDo().apply { + val localizedMessageEntity = localizedMessageRepository + .findByKey(key, locale).orElse(null) ?: LocalizedMessageEntity().apply { + setResourceId(resourceIdGenerator.nextId(LocalizedMessageResourceKind)) this.key = key this.value = value this.locale = locale } - if (localizedMessageDo.entityId != null && - localizedMessageDo.value == value + if (localizedMessageEntity.getId() != null && + localizedMessageEntity.value == value ) { return } val time = OffsetDateTime.now() - localizedMessageDo.apply { + localizedMessageEntity.apply { this.value = value this.updateTime = time }.let { @@ -86,9 +90,9 @@ class LocalizedMessageResourceService( key: String, locale: Locale ): LocalizedMessageResource? { - val localizedMessageDo = localizedMessageRepository + val localizedMessageEntity = localizedMessageRepository .findByKey(key, locale).orElse(null) - return localizedMessageDo?.lock() + return localizedMessageEntity?.lock() } override fun getMessageResource( @@ -104,10 +108,10 @@ class LocalizedMessageResourceService( locale: Locale, defaultValueProvider: Supplier ): LocalizedMessageResource? { - val localizedMessageDo = localizedMessageRepository + val localizedMessageEntity = localizedMessageRepository .findByKey(key, locale).orElse(null) - if (localizedMessageDo != null) { - return localizedMessageDo.lock() + if (localizedMessageEntity != null) { + return localizedMessageEntity.lock() } return SimpleLocalizedMessageResource(key, defaultValueProvider.get(), locale) } @@ -119,4 +123,4 @@ class LocalizedMessageResourceService( override fun getMessageResources(key: String): List { return localizedMessageRepository.findByKey(key).map { it.lock() } } -} \ No newline at end of file +} diff --git a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/SystemSetting.java b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/SystemSetting.java index 2936470b..02e10522 100644 --- a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/SystemSetting.java +++ b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/SystemSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,13 +28,15 @@ * @author RollW */ @SuppressWarnings("all") -public class SystemSetting implements DataEntity { +public class SystemSetting implements DataEntity { private final Long id; + private final String resourceId; private final String key; private final String value; - public SystemSetting(Long id, String key, String value) { + public SystemSetting(Long id, String resourceId, String key, String value) { this.id = id; + this.resourceId = resourceId; this.key = key; this.value = value; } @@ -47,11 +49,15 @@ public String getValue() { return value; } - @Override - public Long getEntityId() { + public Long getId() { return id; } + @Override + public String getEntityId() { + return resourceId; + } + @NonNull @Override public OffsetDateTime getCreateTime() { @@ -95,8 +101,9 @@ public String toString() { + '}'; } - public static final class Builder implements EntityBuilder { + public static final class Builder implements EntityBuilder { private Long id; + private String resourceId; private String key; private String value; @@ -104,11 +111,17 @@ public Builder() { } public Builder(SystemSetting systemSetting) { - this.id = systemSetting.getEntityId(); + this.id = systemSetting.id; + this.resourceId = systemSetting.resourceId; this.key = systemSetting.getKey(); this.value = systemSetting.getValue(); } + public Builder setResourceId(String resourceId) { + this.resourceId = resourceId; + return this; + } + public Builder setKey(String key) { this.key = key; return this; @@ -121,13 +134,18 @@ public Builder setValue(String value) { @Override public SystemSetting build() { - return new SystemSetting(id, key, value); + return new SystemSetting(id, resourceId, key, value); } - @Override - public Builder setEntityId(Long id) { + public Builder setId(Long id) { this.id = id; return this; } + + @Override + public Builder setEntityId(String id) { + this.resourceId = id; + return this; + } } } diff --git a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingDao.kt b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingDao.kt index 367c3e39..175f6006 100644 --- a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingDao.kt +++ b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingDao.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,5 +23,5 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface SystemSettingDao: CommonDao { -} \ No newline at end of file +interface SystemSettingDao : CommonDao { +} diff --git a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingDo.kt b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingEntity.kt similarity index 65% rename from lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingDo.kt rename to lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingEntity.kt index b9369f4a..9cec2a94 100644 --- a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingDo.kt +++ b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,14 @@ package tech.lamprism.lampray.setting.data import jakarta.persistence.Column import jakarta.persistence.Entity -import jakarta.persistence.GeneratedValue -import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Table -import jakarta.persistence.Temporal -import jakarta.persistence.TemporalType import jakarta.persistence.UniqueConstraint +import org.hibernate.annotations.Generated +import org.hibernate.generator.EventType import tech.lamprism.lampray.DataEntity import tech.lamprism.lampray.TimeAttributed +import tech.lamprism.lampray.setting.SystemSetting import tech.lamprism.lampray.setting.SystemSettingResourceKind import tech.rollw.common.web.system.SystemResourceKind import java.time.OffsetDateTime @@ -38,26 +37,30 @@ import java.time.OffsetDateTime @Table(name = "system_setting", uniqueConstraints = [ UniqueConstraint(columnNames = ["key"], name = "index__key") ]) -class SystemSettingDo( - @Id - @Column(name = "id") - @GeneratedValue(strategy = GenerationType.IDENTITY) +class SystemSettingEntity( + @Column(name = "id", nullable = false, insertable = false, updatable = false) + @Generated(event = [EventType.INSERT]) private var id: Long? = null, + @Id + @Column(name = "resource_id", nullable = false, length = 64, unique = true) + private var resourceId: String = "", + @Column(name = "key") var key: String = "", @Column(name = "value") var value: String? = null, - @Temporal(TemporalType.TIMESTAMP) @Column(name = "update_time", nullable = false) private var updateTime: OffsetDateTime = OffsetDateTime.now() -) : DataEntity { - override fun getEntityId(): Long? = id +) : DataEntity { + override fun getEntityId(): String = resourceId + + fun getId(): Long? = id - fun setId(id: Long?) { - this.id = id + fun setResourceId(resourceId: String) { + this.resourceId = resourceId } fun setUpdateTime(updateTime: OffsetDateTime) { @@ -70,4 +73,16 @@ class SystemSettingDo( override fun getSystemResourceKind(): SystemResourceKind = SystemSettingResourceKind -} \ No newline at end of file + + fun lock(): SystemSetting = SystemSetting(id, resourceId, key, value) + + companion object { + @JvmStatic + fun SystemSetting.toEntity() = SystemSettingEntity( + id = getId(), + resourceId = entityId, + key = key, + value = value + ) + } +} diff --git a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingRepository.kt b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingRepository.kt index a851036a..1468c54d 100644 --- a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingRepository.kt +++ b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,14 +26,18 @@ import java.util.Optional @Repository class SystemSettingRepository( private val systemSettingDao: SystemSettingDao -) : CommonRepository(systemSettingDao) { - fun findByKey(key: String): Optional { +) : CommonRepository(systemSettingDao) { + override fun save(entity: S): S { + return systemSettingDao.saveAndFlush(entity) + } + + fun findByKey(key: String): Optional { return findOne { root, query, criteriaBuilder -> criteriaBuilder.equal(root.get("key"), key) } } - fun findByKeyIn(keys: Set): List { + fun findByKeyIn(keys: Set): List { if (keys.isEmpty()) { return emptyList() } @@ -41,4 +45,4 @@ class SystemSettingRepository( root.get("key").`in`(keys) } } -} \ No newline at end of file +} diff --git a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/service/SystemSettingConfigProvider.kt b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/service/SystemSettingConfigProvider.kt index f1b3ba44..152d9747 100644 --- a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/service/SystemSettingConfigProvider.kt +++ b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/service/SystemSettingConfigProvider.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package tech.lamprism.lampray.setting.service import org.springframework.stereotype.Service +import tech.lamprism.lampray.common.data.ResourceIdGenerator import tech.lamprism.lampray.setting.ConfigPath import tech.lamprism.lampray.setting.ConfigProvider import tech.lamprism.lampray.setting.ConfigReader @@ -28,7 +29,8 @@ import tech.lamprism.lampray.setting.SettingSpecification.Companion.keyName import tech.lamprism.lampray.setting.SettingSpecificationHelper import tech.lamprism.lampray.setting.SettingSpecificationProvider import tech.lamprism.lampray.setting.SnapshotConfigValue -import tech.lamprism.lampray.setting.data.SystemSettingDo +import tech.lamprism.lampray.setting.SystemSettingResourceKind +import tech.lamprism.lampray.setting.data.SystemSettingEntity import tech.lamprism.lampray.setting.data.SystemSettingRepository /** @@ -37,7 +39,8 @@ import tech.lamprism.lampray.setting.data.SystemSettingRepository @Service class SystemSettingConfigProvider( private val systemSettingRepository: SystemSettingRepository, - private val settingSpecificationProvider: SettingSpecificationProvider + private val settingSpecificationProvider: SettingSpecificationProvider, + private val resourceIdGenerator: ResourceIdGenerator ) : ConfigProvider { override val metadata: ConfigReader.Metadata = @@ -109,7 +112,8 @@ class SystemSettingConfigProvider( systemSettingRepository.save(setting) return SettingSource.DATABASE } - val newSetting = SystemSettingDo( + val newSetting = SystemSettingEntity( + resourceId = resourceIdGenerator.nextId(SystemSettingResourceKind), key = key, value = value ) @@ -128,7 +132,8 @@ class SystemSettingConfigProvider( systemSettingRepository.save(setting) return SettingSource.DATABASE } - val newSetting = SystemSettingDo( + val newSetting = SystemSettingEntity( + resourceId = resourceIdGenerator.nextId(SystemSettingResourceKind), key = spec.key.name, value = value ) @@ -152,4 +157,4 @@ class SystemSettingConfigProvider( false } } -} \ No newline at end of file +} diff --git a/lampray-user/user-details-api/src/main/java/tech/lamprism/lampray/user/details/UserPersonalData.java b/lampray-user/user-details-api/src/main/java/tech/lamprism/lampray/user/details/UserPersonalData.java index d814875d..74c2f631 100644 --- a/lampray-user/user-details-api/src/main/java/tech/lamprism/lampray/user/details/UserPersonalData.java +++ b/lampray-user/user-details-api/src/main/java/tech/lamprism/lampray/user/details/UserPersonalData.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -100,7 +100,7 @@ public String getWebsite() { @Override public Long getEntityId() { - return getUserId(); + return userId; } @NonNull @@ -141,7 +141,6 @@ public static UserPersonalData defaultOf(UserIdentity user) { .build(); } - public static boolean checkNecessaryFields( @NonNull UserPersonalData userPersonalData) { Preconditions.checkNotNull(userPersonalData); @@ -187,11 +186,12 @@ public Builder(UserPersonalData userPersonalData) { this.introduction = userPersonalData.introduction; this.location = userPersonalData.location; this.website = userPersonalData.website; + this.updateTime = userPersonalData.updateTime; } @Override public Builder setEntityId(Long id) { - return this; + return setUserId(Preconditions.checkNotNull(id)); } public Builder setUserId(long userId) { diff --git a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataDao.kt b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataDao.kt index b5550eef..985d7c17 100644 --- a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataDao.kt +++ b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataDao.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,5 +23,8 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface UserPersonalDataDao : CommonDao { -} \ No newline at end of file +interface UserPersonalDataDao : CommonDao { + fun findByUserId(userId: Long): UserPersonalDataEntity? + + fun findAllByUserIdIn(userIds: Iterable): List +} diff --git a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataDo.kt b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataEntity.kt similarity index 77% rename from lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataDo.kt rename to lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataEntity.kt index 4e1f5276..7c4e5e07 100644 --- a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataDo.kt +++ b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,10 +42,10 @@ import java.time.OffsetDateTime */ @Entity @Table(name = "user_personal_data") -class UserPersonalDataDo( +class UserPersonalDataEntity( @Id - @Column(name = "id", nullable = false) - private var id: Long = 0, + @Column(name = "user_id", nullable = false) + private var userId: Long = 0, @Column(name = "nickname", nullable = false, length = 120) var nickname: String = "", @@ -78,7 +78,13 @@ class UserPersonalDataDo( @Temporal(TemporalType.TIMESTAMP) private var updateTime: OffsetDateTime = OffsetDateTime.now() ) : DataEntity { - override fun getEntityId(): Long = id + override fun getEntityId(): Long = userId + + fun getUserId(): Long = userId + + fun setUserId(userId: Long) { + this.userId = userId + } override fun getSystemResourceKind(): SystemResourceKind = UserResourceKind @@ -98,14 +104,22 @@ class UserPersonalDataDo( } fun lock() = UserPersonalData( - id, nickname, avatar, cover, birthday, introduction, - gender, location, website, updateTime + userId, + nickname, + avatar, + cover, + birthday, + introduction, + gender, + location, + website, + updateTime ) fun toBuilder() = Builder(this) class Builder { - private var id: Long = 0 + private var userId: Long = 0 private var nickname: String? = null private var avatar: String? = null private var cover: String? = null @@ -118,8 +132,8 @@ class UserPersonalDataDo( constructor() - constructor(other: UserPersonalDataDo) { - this.id = other.id + constructor(other: UserPersonalDataEntity) { + this.userId = other.userId this.nickname = other.nickname this.avatar = other.avatar this.cover = other.cover @@ -131,8 +145,8 @@ class UserPersonalDataDo( this.updateTime = other.updateTime } - fun setId(id: Long) = apply { - this.id = id + fun setUserId(userId: Long) = apply { + this.userId = userId } fun setNickname(nickname: String) = apply { @@ -171,30 +185,38 @@ class UserPersonalDataDo( this.updateTime = updateTime } - fun build(): UserPersonalDataDo { - return UserPersonalDataDo( - id, - nickname!!, - avatar!!, - cover!!, - birthday!!, - introduction, - gender!!, - location, - website, - updateTime!! + fun build(): UserPersonalDataEntity { + return UserPersonalDataEntity( + userId = userId, + nickname = nickname!!, + avatar = avatar!!, + cover = cover!!, + birthday = birthday!!, + introduction = introduction, + gender = gender!!, + location = location, + website = website, + updateTime = updateTime!! ) } } companion object { @JvmStatic - fun UserPersonalData.toDo() = UserPersonalDataDo( - entityId, nickname, avatar, cover, birthday, introduction, - gender, location, website, updateTime + fun UserPersonalData.toEntity() = UserPersonalDataEntity( + userId = entityId, + nickname = nickname, + avatar = avatar, + cover = cover, + birthday = birthday, + introduction = introduction, + gender = gender, + location = location, + website = website, + updateTime = updateTime ) @JvmStatic fun builder() = Builder() } -} \ No newline at end of file +} diff --git a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataRepository.kt b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataRepository.kt index 64c1339e..1c63e940 100644 --- a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataRepository.kt +++ b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,5 +25,16 @@ import tech.lamprism.lampray.common.data.CommonRepository @Repository class UserPersonalDataRepository( private val userPersonalDataDao: UserPersonalDataDao -) : CommonRepository(userPersonalDataDao) { -} \ No newline at end of file +) : CommonRepository(userPersonalDataDao) { + override fun save(entity: S): S { + return userPersonalDataDao.saveAndFlush(entity) + } + + fun findByUserId(userId: Long): UserPersonalDataEntity? { + return userPersonalDataDao.findByUserId(userId) + } + + fun findAllByUserIdIn(userIds: Iterable): List { + return userPersonalDataDao.findAllByUserIdIn(userIds) + } +} diff --git a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/service/UserPersonalDataServiceImpl.java b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/service/UserPersonalDataServiceImpl.java index 41a9b3c8..e38e80da 100644 --- a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/service/UserPersonalDataServiceImpl.java +++ b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/service/UserPersonalDataServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ import tech.lamprism.lampray.user.details.UserDataFieldType; import tech.lamprism.lampray.user.details.UserPersonalData; import tech.lamprism.lampray.user.details.UserPersonalDataService; -import tech.lamprism.lampray.user.details.persistence.UserPersonalDataDo; +import tech.lamprism.lampray.user.details.persistence.UserPersonalDataEntity; import tech.lamprism.lampray.user.details.persistence.UserPersonalDataRepository; import java.time.OffsetDateTime; @@ -47,8 +47,7 @@ public UserPersonalDataServiceImpl(UserProvider userProvider, @Override public UserPersonalData getPersonalData(long userId) { - UserPersonalDataDo data = userPersonalDataRepository.findById(userId) - .orElse(null); + UserPersonalDataEntity data = userPersonalDataRepository.findByUserId(userId); if (data == null) { AttributedUser user = userProvider.getUser(userId); return UserPersonalData.defaultOf(user); @@ -63,8 +62,8 @@ public UserPersonalData getPersonalData(long userId) { @Override public UserPersonalData getPersonalData(UserIdentity userIdentity) { - UserPersonalDataDo data = userPersonalDataRepository.findById( - userIdentity.getUserId()).orElse(null); + UserPersonalDataEntity data = userPersonalDataRepository.findByUserId( + userIdentity.getUserId()); if (data == null) { return UserPersonalData.defaultOf(userIdentity); } @@ -96,9 +95,9 @@ public List getPersonalData(List userI @Override public List getPersonalDataByIds(List ids) { - return userPersonalDataRepository.findAllById(ids) + return userPersonalDataRepository.findAllByUserIdIn(ids) .stream() - .map(UserPersonalDataDo::lock) + .map(UserPersonalDataEntity::lock) .toList(); } @@ -113,9 +112,8 @@ public void updatePersonalData(UserTrait user, UserDataField... fields) { if (fields.length == 0) { return; } - UserPersonalDataDo exist = userPersonalDataRepository.findById(user.getUserId()) - .orElse(null); - UserPersonalDataDo.Builder builder = toBuilder(exist); + UserPersonalDataEntity exist = userPersonalDataRepository.findByUserId(user.getUserId()); + UserPersonalDataEntity.Builder builder = toBuilder(exist, user.getUserId()); for (UserDataField field : fields) { Utils.setBuilderValue(builder, field); } @@ -123,10 +121,11 @@ public void updatePersonalData(UserTrait user, UserDataField... fields) { userPersonalDataRepository.save(builder.build()); } - private UserPersonalDataDo.Builder toBuilder(UserPersonalDataDo data) { + private UserPersonalDataEntity.Builder toBuilder(UserPersonalDataEntity data, long userId) { if (data != null) { return data.toBuilder(); } - return UserPersonalDataDo.builder(); + return UserPersonalDataEntity.builder() + .setUserId(userId); } } diff --git a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/service/Utils.kt b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/service/Utils.kt index 8013c92b..72d8f04b 100644 --- a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/service/Utils.kt +++ b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/service/Utils.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,14 +20,14 @@ import tech.lamprism.lampray.user.details.Birthday import tech.lamprism.lampray.user.details.Gender import tech.lamprism.lampray.user.details.UserDataField import tech.lamprism.lampray.user.details.UserDataFieldType -import tech.lamprism.lampray.user.details.persistence.UserPersonalDataDo +import tech.lamprism.lampray.user.details.persistence.UserPersonalDataEntity /** * @author RollW */ internal object Utils { @JvmStatic - fun UserPersonalDataDo.Builder.setBuilderValue(field: UserDataField<*>) { + fun UserPersonalDataEntity.Builder.setBuilderValue(field: UserDataField<*>) { when (field.type()) { UserDataFieldType.AVATAR -> setAvatar(field.value() as String) UserDataFieldType.INTRO -> setIntroduction(field.value() as String) diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/system/database/LiquibaseInitializeConfiguration.java b/lampray-web/src/main/java/tech/lamprism/lampray/system/database/LiquibaseInitializeConfiguration.java index bd47f47d..25414302 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/system/database/LiquibaseInitializeConfiguration.java +++ b/lampray-web/src/main/java/tech/lamprism/lampray/system/database/LiquibaseInitializeConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,10 @@ import liquibase.Contexts; import liquibase.LabelExpression; import liquibase.Liquibase; +import liquibase.Scope; +import liquibase.changelog.ChangeLogHistoryService; +import liquibase.changelog.ChangeLogHistoryServiceFactory; +import liquibase.changelog.ChangeSet; import liquibase.database.Database; import liquibase.database.DatabaseFactory; import liquibase.database.jvm.JdbcConnection; @@ -35,6 +39,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; +import java.util.Locale; /** * Liquibase initialization configuration for database migration management. @@ -46,6 +51,7 @@ public class LiquibaseInitializeConfiguration { private static final Logger logger = LoggerFactory.getLogger(LiquibaseInitializeConfiguration.class); private static final String CHANGELOG_PATH = "classpath:db/changelog.yaml"; + private static final String BASELINE_CHANGELOG_PATH_PREFIX = "db/baseline/"; private static final String BASELINE_CONTEXT = "baseline"; private static final String INCREMENTAL_CONTEXT = "incremental"; @@ -78,7 +84,7 @@ public SpringLiquibase springLiquibase(DataSource dataSource) throws Exception { switch (status) { case NEW_INSTALL -> handleNewInstallationSync(dataSource, liquibase); - case NORMAL_UPGRADE -> handleNormalUpgradeSync(dataSource, liquibase); + case NORMAL_UPGRADE -> handleNormalUpgradeSync(dataSource); } return liquibase; @@ -156,8 +162,8 @@ private void handleNewInstallationSync(DataSource dataSource, SpringLiquibase sp } - private void handleNormalUpgradeSync(DataSource dataSource, SpringLiquibase springLiquibase) throws Exception { - logger.info("Normal upgrade detected: syncing baseline changeSets..."); + private void handleNormalUpgradeSync(DataSource dataSource) throws Exception { + logger.info("Normal upgrade detected: refreshing mutable baseline checksums and syncing baseline changeSets..."); try (Connection connection = dataSource.getConnection()) { Database database = DatabaseFactory.getInstance() @@ -168,12 +174,67 @@ private void handleNormalUpgradeSync(DataSource dataSource, SpringLiquibase spri new ClassLoaderResourceAccessor(), database ); + + refreshMutableBaselineChecksums(liquibase, database); syncChangeSetsByContext(liquibase, BASELINE_CONTEXT); logger.info("Normal upgrade sync completed: baseline changeSets marked as executed, " + "future incremental changeSets will be applied on upgrades."); } } + private void refreshMutableBaselineChecksums(Liquibase liquibase, Database database) throws Exception { + ChangeLogHistoryService changeLogHistoryService = Scope.getCurrentScope() + .getSingleton(ChangeLogHistoryServiceFactory.class) + .getChangeLogService(database); + int refreshedChecksums = 0; + + for (ChangeSet changeSet : liquibase.getDatabaseChangeLog().getChangeSets()) { + if (!isMutableBaselineChangeSet(changeSet)) { + continue; + } + + ChangeSet.RunStatus runStatus = database.getRunStatus(changeSet); + if (runStatus == ChangeSet.RunStatus.INVALID_MD5SUM || runStatus == ChangeSet.RunStatus.RUN_AGAIN) { + changeLogHistoryService.replaceChecksum(changeSet); + refreshedChecksums++; + logger.info("Refreshed checksum for mutable baseline changeset {}::{}::{}", + changeSet.getFilePath(), changeSet.getId(), changeSet.getAuthor()); + } + } + + if (refreshedChecksums > 0) { + logger.info("Refreshed {} mutable baseline checksum(s) before Liquibase validation.", refreshedChecksums); + } + } + + private boolean isMutableBaselineChangeSet(ChangeSet changeSet) { + return hasBaselinePath(changeSet.getFilePath()) + || hasBaselinePath(changeSet.getStoredFilePath()) + || hasBaselineContext(changeSet); + } + + private boolean hasBaselinePath(String filePath) { + if (filePath == null) { + return false; + } + + String normalizedPath = filePath.replace('\\', '/'); + if (normalizedPath.startsWith("classpath:")) { + normalizedPath = normalizedPath.substring("classpath:".length()); + } + + return normalizedPath.startsWith(BASELINE_CHANGELOG_PATH_PREFIX) + || normalizedPath.contains("/" + BASELINE_CHANGELOG_PATH_PREFIX); + } + + private boolean hasBaselineContext(ChangeSet changeSet) { + if (changeSet.getContextFilter() == null) { + return false; + } + + return changeSet.getContextFilter().toString().toLowerCase(Locale.ROOT).contains(BASELINE_CONTEXT); + } + private void handleLegacyMigrationSync(DataSource dataSource) throws Exception { logger.info("Legacy migration detected: syncing baseline changeSets..."); diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/ArticleManageController.java b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/ArticleManageController.java index 621587c8..4631ef9d 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/ArticleManageController.java +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/ArticleManageController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,8 +18,6 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; -import tech.lamprism.lampray.web.controller.AdminApi; -import tech.lamprism.lampray.web.controller.article.model.ArticleMetaVo; import tech.lamprism.lampray.content.ContentAccessService; import tech.lamprism.lampray.content.ContentIdentity; import tech.lamprism.lampray.content.ContentMetadataDetails; @@ -27,6 +25,8 @@ import tech.lamprism.lampray.content.ContentType; import tech.lamprism.lampray.content.common.ContentErrorCode; import tech.lamprism.lampray.content.common.ContentException; +import tech.lamprism.lampray.web.controller.AdminApi; +import tech.lamprism.lampray.web.controller.article.model.ArticleMetaVo; import tech.rollw.common.web.HttpResponseEntity; /** @@ -46,7 +46,7 @@ public ArticleManageController(ContentPublishProvider contentPublishProvider, @GetMapping("/users/{userId}/articles/{articleId}") public HttpResponseEntity getArticle( @PathVariable("userId") Long userId, - @PathVariable("articleId") Long articleId) { + @PathVariable("articleId") String articleId) { ContentMetadataDetails contentMetadataDetails = contentAccessService.getContentMetadataDetails( ContentIdentity.of(articleId, ContentType.ARTICLE) diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/model/ArticleInfoView.java b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/model/ArticleInfoView.java index d22c966d..9ab4be7c 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/model/ArticleInfoView.java +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/model/ArticleInfoView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ * @author RollW */ public record ArticleInfoView( - long id, + String id, long userId, String title, String cover, diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/model/ArticleMetaVo.java b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/model/ArticleMetaVo.java index 0d0f5c31..b72f4624 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/model/ArticleMetaVo.java +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/model/ArticleMetaVo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ * @author RollW */ public record ArticleMetaVo( - long id, + String id, String title, StructuralText content, long authorId, diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/model/ArticleVo.java b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/model/ArticleVo.java index bf5486e3..3c5da4d6 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/model/ArticleVo.java +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/article/model/ArticleVo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ * @author RollW */ public record ArticleVo( - long id, + String id, String title, StructuralText content, long userId, diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/CommentController.kt b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/CommentController.kt index 9a7af89c..ee60a304 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/CommentController.kt +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/CommentController.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ class CommentController( ) { @PostMapping("/{contentType}/{contentId}/comments") fun createComment( - @PathVariable("contentId") contentId: Long, + @PathVariable("contentId") contentId: String, @PathVariable("contentType") type: String, @RequestBody commentRequest: CommentRequest ): HttpResponseEntity { @@ -77,7 +77,7 @@ class CommentController( @GetMapping("/{contentType}/{contentId}/comments") fun getComments( - @PathVariable("contentId") contentId: Long, + @PathVariable("contentId") contentId: String, @PathVariable("contentType") type: String ): HttpResponseEntity> { val contentType = getContentType(type) @@ -105,7 +105,7 @@ class CommentController( .context val user = context.user!! val contents = contentCollectionProviderFactory.getContents( - ContentCollectionIdentity.of(user.userId, ContentCollectionType.USER_COMMENTS), + ContentCollectionIdentity.of(user.userId.toString(), ContentCollectionType.USER_COMMENTS), ContentAccessCredentials.NO_LIMIT ).mapNotNull { CommentVo.of(it.contentDetails) diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/model/CommentRequest.java b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/model/CommentRequest.java index f7727010..87fa282f 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/model/CommentRequest.java +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/model/CommentRequest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,6 @@ */ public record CommentRequest( StructuralText content, - Long parent + String parent ) { } diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/model/CommentVo.kt b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/model/CommentVo.kt index 9da2fadd..69f84f90 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/model/CommentVo.kt +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/model/CommentVo.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,17 +26,17 @@ import java.time.OffsetDateTime * @author RollW */ data class CommentVo( - val id: Long, + val id: String, val userId: Long, - val parent: Long, + val parent: String?, val content: StructuralText, - val contentId: Long, + val contentId: String, val contentType: ContentType, val createTime: OffsetDateTime, val updateTime: OffsetDateTime ) : ContentVo { - override fun id(): Long = id + override fun id(): String = id override fun createTime(): OffsetDateTime = createTime @@ -57,7 +57,7 @@ data class CommentVo( contentDetails.contentId, contentDetails.userId, metadata.parentId, - contentDetails.content, + contentDetails.content!!, metadata.contentId, metadata.contentType, contentDetails.createTime, diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentController.java b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentController.java index 9778eeaa..c7ac59b3 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentController.java +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,13 +55,13 @@ public class ContentController { private final ContentPublishProvider contentPublishProvider; private final ContentAccessService contentAccessService; - private final SystemResourceOperatorProvider systemResourceOperatorProvider; + private final SystemResourceOperatorProvider systemResourceOperatorProvider; private final ContentCollectionProviderFactory contentCollectionProviderFactory; private final ContextThreadAware apiContextThreadAware; public ContentController(ContentPublishProvider contentPublishProvider, ContentAccessService contentAccessService, - SystemResourceOperatorProvider systemResourceOperatorProvider, + SystemResourceOperatorProvider systemResourceOperatorProvider, ContentCollectionProviderFactory contentCollectionProviderFactory, ContextThreadAware apiContextThreadAware) { this.contentPublishProvider = contentPublishProvider; @@ -75,7 +75,7 @@ public ContentController(ContentPublishProvider contentPublishProvider, public HttpResponseEntity getContent( @PathVariable("userId") Long userId, @PathVariable("contentType") UrlContentType contentType, - @PathVariable("contentId") Long contentId) { + @PathVariable("contentId") String contentId) { ContextThread apiContextThread = apiContextThreadAware.getContextThread(); ApiContext apiContext = apiContextThread.getContext(); ContentDetails details = contentAccessService.openContent( @@ -109,7 +109,7 @@ public HttpResponseEntity> getContents( ); List> contents = contentCollectionProviderFactory.getContents( ContentCollectionIdentity.of( - user.getUserId(), + String.valueOf(user.getUserId()), userCollectionType ), contentAccessCredentials @@ -136,7 +136,7 @@ public HttpResponseEntity> getUserContents( List> contents = contentCollectionProviderFactory.getContents( ContentCollectionIdentity.of( - userId, + String.valueOf(userId), contentType.getUserCollectionType() ), contentAccessCredentials @@ -150,7 +150,7 @@ public HttpResponseEntity> getUserContents( public HttpResponseEntity deleteContent( @PathVariable("userId") Long userId, @PathVariable("contentType") UrlContentType contentType, - @PathVariable("contentId") Long contentId) { + @PathVariable("contentId") String contentId) { ContentOperator contentOperator = systemResourceOperatorProvider.getSystemResourceOperator( getSystemResource(contentId, contentType), true @@ -167,12 +167,12 @@ public HttpResponseEntity deleteContent( public HttpResponseEntity updateContent( @PathVariable("userId") Long userId, @PathVariable("contentType") UrlContentType contentType, - @PathVariable("contentId") Long contentId) { + @PathVariable("contentId") String contentId) { return HttpResponseEntity.success(); } - private SystemResource getSystemResource(Long contentId, - UrlContentType contentType) { + private SystemResource getSystemResource(String contentId, + UrlContentType contentType) { return new SimpleSystemResource<>( contentId, contentType.getContentType().getSystemResourceKind() diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentManageController.java b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentManageController.java index 480a03f0..d97391c6 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentManageController.java +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentManageController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,7 +59,7 @@ public ContentManageController(ContentAccessService contentAccessService, Conten public HttpResponseEntity getContent( @PathVariable("userId") Long userId, @PathVariable("contentType") UrlContentType contentType, - @PathVariable("contentId") Long contentId) { + @PathVariable("contentId") String contentId) { ContentMetadataDetails contentMetadataDetails = contentAccessService.getContentMetadataDetails( ContentIdentity.of(contentId, contentType.getContentType()) ); diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/vo/ContentVo.java b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/vo/ContentVo.java index 1e2a4d7f..651cf522 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/vo/ContentVo.java +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/vo/ContentVo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ * @author RollW */ public interface ContentVo { - long id(); + String id(); OffsetDateTime createTime(); diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/review/model/ReviewJobContentView.kt b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/review/model/ReviewJobContentView.kt index 48200038..f66913c9 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/review/model/ReviewJobContentView.kt +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/review/model/ReviewJobContentView.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ import java.time.OffsetDateTime * @author RollW */ data class ReviewJobContentView( - val contentId: Long, + val contentId: String, val contentType: ContentType, val title: String?, val content: StructuralText, @@ -47,4 +47,4 @@ data class ReviewJobContentView( ) } } -} \ No newline at end of file +} diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/review/model/ReviewJobDetailsView.kt b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/review/model/ReviewJobDetailsView.kt index 12e64b04..7537316a 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/review/model/ReviewJobDetailsView.kt +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/review/model/ReviewJobDetailsView.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ data class ReviewJobDetailsView( val id: String, val status: ReviewStatus, val contentType: ContentType, - val contentId: Long, + val contentId: String, val reviewMark: ReviewMark, val createTime: OffsetDateTime, val updateTime: OffsetDateTime, diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/review/model/ReviewJobView.kt b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/review/model/ReviewJobView.kt index a43afd9f..33475081 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/review/model/ReviewJobView.kt +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/review/model/ReviewJobView.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ data class ReviewJobView( val id: String, val status: ReviewStatus, val contentType: ContentType, - val contentId: Long, + val contentId: String, val reviewMark: ReviewMark, val createTime: OffsetDateTime, val updateTime: OffsetDateTime diff --git a/lampray-web/src/main/resources/db/baseline/baseline-0.1.0-alpha1.yaml b/lampray-web/src/main/resources/db/baseline/baseline-0.1.0-alpha1.yaml index 1b8cb8e4..fbf27e06 100644 --- a/lampray-web/src/main/resources/db/baseline/baseline-0.1.0-alpha1.yaml +++ b/lampray-web/src/main/resources/db/baseline/baseline-0.1.0-alpha1.yaml @@ -93,7 +93,7 @@ databaseChangeLog: constraints: nullable: false name: parent_id - type: BIGINT + type: VARCHAR(64) - column: constraints: nullable: false @@ -118,7 +118,7 @@ databaseChangeLog: constraints: nullable: false name: comment_on_id - type: BIGINT + type: VARCHAR(64) - column: constraints: nullable: false @@ -135,6 +135,12 @@ databaseChangeLog: primaryKeyName: pk_content_metadata name: id type: BIGINT + - column: + constraints: + nullable: false + unique: true + name: resource_id + type: VARCHAR(64) - column: constraints: nullable: false @@ -144,7 +150,7 @@ databaseChangeLog: constraints: nullable: false name: content_id - type: BIGINT + type: VARCHAR(64) - column: constraints: nullable: false @@ -171,6 +177,12 @@ databaseChangeLog: primaryKeyName: pk_favorite_group name: id type: BIGINT + - column: + constraints: + nullable: false + unique: true + name: resource_id + type: VARCHAR(64) - column: constraints: nullable: false @@ -227,6 +239,12 @@ databaseChangeLog: primaryKeyName: pk_favorite_item name: id type: BIGINT + - column: + constraints: + nullable: false + unique: true + name: resource_id + type: VARCHAR(64) - column: constraints: nullable: false @@ -236,12 +254,12 @@ databaseChangeLog: constraints: nullable: false name: group_id - type: BIGINT + type: VARCHAR(64) - column: constraints: nullable: false name: content_id - type: BIGINT + type: VARCHAR(64) - column: constraints: nullable: false @@ -273,6 +291,12 @@ databaseChangeLog: primaryKeyName: pk_localized_message name: id type: BIGINT + - column: + constraints: + nullable: false + unique: true + name: resource_id + type: VARCHAR(64) - column: constraints: nullable: false @@ -304,6 +328,12 @@ databaseChangeLog: primaryKeyName: pk_register_verification_token name: id type: BIGINT + - column: + constraints: + nullable: false + unique: true + name: resource_id + type: VARCHAR(64) - column: name: token type: VARCHAR(255) @@ -337,7 +367,7 @@ databaseChangeLog: constraints: nullable: false name: content_id - type: BIGINT + type: VARCHAR(64) - column: constraints: nullable: false @@ -466,6 +496,12 @@ databaseChangeLog: primaryKeyName: pk_system_setting name: id type: BIGINT + - column: + constraints: + nullable: false + unique: true + name: resource_id + type: VARCHAR(64) - column: name: key type: VARCHAR(255) @@ -546,7 +582,7 @@ databaseChangeLog: nullable: false primaryKey: true primaryKeyName: pk_user_personal_data - name: id + name: user_id type: BIGINT - column: constraints: diff --git a/lampray-web/src/main/resources/db/changelog.yaml b/lampray-web/src/main/resources/db/changelog.yaml index e14cc195..d4e29b14 100644 --- a/lampray-web/src/main/resources/db/changelog.yaml +++ b/lampray-web/src/main/resources/db/changelog.yaml @@ -5,4 +5,7 @@ databaseChangeLog: - include: file: db/changelog/0.1.0/0.1.0-alpha1-01-changelog.yaml context: incremental + - include: + file: db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml + context: incremental diff --git a/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-01-changelog.yaml b/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-01-changelog.yaml index 0a34bbc4..a147b6be 100644 --- a/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-01-changelog.yaml +++ b/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-01-changelog.yaml @@ -609,7 +609,7 @@ databaseChangeLog: nullable: false primaryKey: true primaryKeyName: pk_user_personal_data - name: id + name: user_id type: BIGINT - column: constraints: diff --git a/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml b/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml new file mode 100644 index 00000000..1cdfd5c8 --- /dev/null +++ b/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml @@ -0,0 +1,168 @@ +databaseChangeLog: + - changeSet: + id: 0.1.0-alpha1-15-resource-id-columns + author: RollW + objectQuotingStrategy: QUOTE_ONLY_RESERVED_WORDS + changes: + - addColumn: + tableName: article + columns: + - column: + name: resource_id + type: VARCHAR(64) + - sql: + sql: UPDATE article SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - addNotNullConstraint: + tableName: article + columnName: resource_id + columnDataType: VARCHAR(64) + - addUniqueConstraint: + tableName: article + columnNames: resource_id + constraintName: uc_article_resource_id + + - addColumn: + tableName: comment + columns: + - column: + name: resource_id + type: VARCHAR(64) + - sql: + sql: UPDATE comment SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - modifyDataType: + tableName: comment + columnName: parent_id + newDataType: VARCHAR(64) + - modifyDataType: + tableName: comment + columnName: comment_on_id + newDataType: VARCHAR(64) + - sql: + sql: UPDATE comment SET parent_id = '' WHERE parent_id = '0' OR parent_id IS NULL; + - addNotNullConstraint: + tableName: comment + columnName: resource_id + columnDataType: VARCHAR(64) + - addUniqueConstraint: + tableName: comment + columnNames: resource_id + constraintName: uc_comment_resource_id + + - addColumn: + tableName: content_metadata + columns: + - column: + name: resource_id + type: VARCHAR(64) + - sql: + sql: UPDATE content_metadata SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - modifyDataType: + tableName: content_metadata + columnName: content_id + newDataType: VARCHAR(64) + - addNotNullConstraint: + tableName: content_metadata + columnName: resource_id + columnDataType: VARCHAR(64) + - addUniqueConstraint: + tableName: content_metadata + columnNames: resource_id + constraintName: uc_content_metadata_resource_id + + - addColumn: + tableName: favorite_group + columns: + - column: + name: resource_id + type: VARCHAR(64) + - sql: + sql: UPDATE favorite_group SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - addNotNullConstraint: + tableName: favorite_group + columnName: resource_id + columnDataType: VARCHAR(64) + - addUniqueConstraint: + tableName: favorite_group + columnNames: resource_id + constraintName: uc_favorite_group_resource_id + + - addColumn: + tableName: favorite_item + columns: + - column: + name: resource_id + type: VARCHAR(64) + - sql: + sql: UPDATE favorite_item SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - modifyDataType: + tableName: favorite_item + columnName: group_id + newDataType: VARCHAR(64) + - modifyDataType: + tableName: favorite_item + columnName: content_id + newDataType: VARCHAR(64) + - addNotNullConstraint: + tableName: favorite_item + columnName: resource_id + columnDataType: VARCHAR(64) + - addUniqueConstraint: + tableName: favorite_item + columnNames: resource_id + constraintName: uc_favorite_item_resource_id + + - addColumn: + tableName: localized_message + columns: + - column: + name: resource_id + type: VARCHAR(64) + - sql: + sql: UPDATE localized_message SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - addNotNullConstraint: + tableName: localized_message + columnName: resource_id + columnDataType: VARCHAR(64) + - addUniqueConstraint: + tableName: localized_message + columnNames: resource_id + constraintName: uc_localized_message_resource_id + + - addColumn: + tableName: register_verification_token + columns: + - column: + name: resource_id + type: VARCHAR(64) + - sql: + sql: UPDATE register_verification_token SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - addNotNullConstraint: + tableName: register_verification_token + columnName: resource_id + columnDataType: VARCHAR(64) + - addUniqueConstraint: + tableName: register_verification_token + columnNames: resource_id + constraintName: uc_register_verification_token_resource_id + + - modifyDataType: + tableName: review_job + columnName: content_id + newDataType: VARCHAR(64) + + - addColumn: + tableName: system_setting + columns: + - column: + name: resource_id + type: VARCHAR(64) + - sql: + sql: UPDATE system_setting SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - addNotNullConstraint: + tableName: system_setting + columnName: resource_id + columnDataType: VARCHAR(64) + - addUniqueConstraint: + tableName: system_setting + columnNames: resource_id + constraintName: uc_system_setting_resource_id From da6df07f90d77f855902acd6aeab2311a41bd06c Mon Sep 17 00:00:00 2001 From: rollw <73376341+roll-w@users.noreply.github.com> Date: Sun, 29 Mar 2026 13:38:00 +0000 Subject: [PATCH 2/6] fix(content): address review feedback for resource ID migration --- .../web/controller/comment/model/CommentVo.kt | 4 +- .../controller/content/ContentController.java | 23 +++- .../content/ContentManageController.java | 6 +- .../0.1.0/0.1.0-alpha1-01-changelog.yaml | 2 +- ...0.1.0-alpha1-02-resource-id-migration.yaml | 127 +++++++++++++++--- 5 files changed, 137 insertions(+), 25 deletions(-) diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/model/CommentVo.kt b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/model/CommentVo.kt index 69f84f90..d602b25c 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/model/CommentVo.kt +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/comment/model/CommentVo.kt @@ -53,11 +53,13 @@ data class CommentVo( if (metadata !is CommentDetailsMetadata) { return null } + val content = contentDetails.content + ?: return null return CommentVo( contentDetails.contentId, contentDetails.userId, metadata.parentId, - contentDetails.content!!, + content, metadata.contentId, metadata.contentType, contentDetails.createTime, diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentController.java b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentController.java index c7ac59b3..bd39ab7d 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentController.java +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentController.java @@ -47,6 +47,7 @@ import tech.rollw.common.web.system.SystemResourceOperatorProvider; import java.util.List; +import java.util.Objects; /** * @author RollW @@ -88,7 +89,7 @@ public HttpResponseEntity getContent( return HttpResponseEntity.of(ContentErrorCode.ERROR_CONTENT_NOT_FOUND); } - return HttpResponseEntity.success(contentVoConvert(details)); + return HttpResponseEntity.success(contentVoConvertOrThrow(details)); } @@ -116,7 +117,10 @@ public HttpResponseEntity> getContents( ); return HttpResponseEntity.success( - contents.stream().map(this::contentVoConvert).toList() + contents.stream() + .map(this::contentVoConvert) + .filter(Objects::nonNull) + .toList() ); } @@ -142,7 +146,10 @@ public HttpResponseEntity> getUserContents( contentAccessCredentials ); return HttpResponseEntity.success( - contents.stream().map(this::contentVoConvert).toList() + contents.stream() + .map(this::contentVoConvert) + .filter(Objects::nonNull) + .toList() ); } @@ -180,6 +187,14 @@ private SystemResource getSystemResource(String contentId, } private ContentVo contentVoConvert(ContentDetails details) { - return ContentViewHelper.toContentView(details); + return ContentViewHelper.toContentView(details); + } + + private ContentVo contentVoConvertOrThrow(ContentDetails details) { + ContentVo contentVo = contentVoConvert(details); + if (contentVo == null) { + throw new ContentException(ContentErrorCode.ERROR_CONTENT_NOT_FOUND); + } + return contentVo; } } diff --git a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentManageController.java b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentManageController.java index d97391c6..74476d46 100644 --- a/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentManageController.java +++ b/lampray-web/src/main/java/tech/lamprism/lampray/web/controller/content/ContentManageController.java @@ -66,7 +66,11 @@ public HttpResponseEntity getContent( if (contentMetadataDetails.getUserId() != userId) { return HttpResponseEntity.of(ContentErrorCode.ERROR_CONTENT_NOT_FOUND); } - return HttpResponseEntity.success(ContentViewHelper.toContentView(contentMetadataDetails.getContentDetails())); + ContentVo contentVo = ContentViewHelper.toContentView(contentMetadataDetails.getContentDetails()); + if (contentVo == null) { + return HttpResponseEntity.of(ContentErrorCode.ERROR_CONTENT_NOT_FOUND); + } + return HttpResponseEntity.success(contentVo); } private ContentCollectionType getUserContentCollectionType(UrlContentType contentType) { diff --git a/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-01-changelog.yaml b/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-01-changelog.yaml index a147b6be..0a34bbc4 100644 --- a/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-01-changelog.yaml +++ b/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-01-changelog.yaml @@ -609,7 +609,7 @@ databaseChangeLog: nullable: false primaryKey: true primaryKeyName: pk_user_personal_data - name: user_id + name: id type: BIGINT - column: constraints: diff --git a/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml b/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml index 1cdfd5c8..30009124 100644 --- a/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml +++ b/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml @@ -10,8 +10,13 @@ databaseChangeLog: - column: name: resource_id type: VARCHAR(64) - - sql: - sql: UPDATE article SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - update: + tableName: article + columns: + - column: + name: resource_id + valueComputed: id + where: resource_id IS NULL - addNotNullConstraint: tableName: article columnName: resource_id @@ -27,8 +32,13 @@ databaseChangeLog: - column: name: resource_id type: VARCHAR(64) - - sql: - sql: UPDATE comment SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - update: + tableName: comment + columns: + - column: + name: resource_id + valueComputed: id + where: resource_id IS NULL - modifyDataType: tableName: comment columnName: parent_id @@ -37,8 +47,13 @@ databaseChangeLog: tableName: comment columnName: comment_on_id newDataType: VARCHAR(64) - - sql: - sql: UPDATE comment SET parent_id = '' WHERE parent_id = '0' OR parent_id IS NULL; + - update: + tableName: comment + columns: + - column: + name: parent_id + value: "" + where: parent_id = '0' OR parent_id IS NULL - addNotNullConstraint: tableName: comment columnName: resource_id @@ -54,8 +69,13 @@ databaseChangeLog: - column: name: resource_id type: VARCHAR(64) - - sql: - sql: UPDATE content_metadata SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - update: + tableName: content_metadata + columns: + - column: + name: resource_id + valueComputed: id + where: resource_id IS NULL - modifyDataType: tableName: content_metadata columnName: content_id @@ -75,8 +95,13 @@ databaseChangeLog: - column: name: resource_id type: VARCHAR(64) - - sql: - sql: UPDATE favorite_group SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - update: + tableName: favorite_group + columns: + - column: + name: resource_id + valueComputed: id + where: resource_id IS NULL - addNotNullConstraint: tableName: favorite_group columnName: resource_id @@ -92,8 +117,13 @@ databaseChangeLog: - column: name: resource_id type: VARCHAR(64) - - sql: - sql: UPDATE favorite_item SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - update: + tableName: favorite_item + columns: + - column: + name: resource_id + valueComputed: id + where: resource_id IS NULL - modifyDataType: tableName: favorite_item columnName: group_id @@ -117,8 +147,13 @@ databaseChangeLog: - column: name: resource_id type: VARCHAR(64) - - sql: - sql: UPDATE localized_message SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - update: + tableName: localized_message + columns: + - column: + name: resource_id + valueComputed: id + where: resource_id IS NULL - addNotNullConstraint: tableName: localized_message columnName: resource_id @@ -134,8 +169,13 @@ databaseChangeLog: - column: name: resource_id type: VARCHAR(64) - - sql: - sql: UPDATE register_verification_token SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - update: + tableName: register_verification_token + columns: + - column: + name: resource_id + valueComputed: id + where: resource_id IS NULL - addNotNullConstraint: tableName: register_verification_token columnName: resource_id @@ -156,8 +196,13 @@ databaseChangeLog: - column: name: resource_id type: VARCHAR(64) - - sql: - sql: UPDATE system_setting SET resource_id = CAST(id AS CHAR(64)) WHERE resource_id IS NULL; + - update: + tableName: system_setting + columns: + - column: + name: resource_id + valueComputed: id + where: resource_id IS NULL - addNotNullConstraint: tableName: system_setting columnName: resource_id @@ -166,3 +211,49 @@ databaseChangeLog: tableName: system_setting columnNames: resource_id constraintName: uc_system_setting_resource_id + + - changeSet: + id: 0.1.0-alpha1-16-user-personal-data-user-id + author: RollW + objectQuotingStrategy: QUOTE_ONLY_RESERVED_WORDS + dbms: mysql,mariadb,postgresql,h2,oracle,sqlserver + preConditions: + - onFail: MARK_RAN + - columnExists: + tableName: user_personal_data + columnName: id + - not: + columnExists: + tableName: user_personal_data + columnName: user_id + changes: + - dropPrimaryKey: + tableName: user_personal_data + - renameColumn: + tableName: user_personal_data + oldColumnName: id + newColumnName: user_id + columnDataType: BIGINT + - addPrimaryKey: + tableName: user_personal_data + columnNames: user_id + constraintName: pk_user_personal_data + + - changeSet: + id: 0.1.0-alpha1-18-user-personal-data-primary-key-recovery + author: RollW + objectQuotingStrategy: QUOTE_ONLY_RESERVED_WORDS + dbms: mysql,mariadb,postgresql,h2,oracle,sqlserver + preConditions: + - onFail: MARK_RAN + - columnExists: + tableName: user_personal_data + columnName: user_id + - not: + - primaryKeyExists: + tableName: user_personal_data + changes: + - addPrimaryKey: + tableName: user_personal_data + columnNames: user_id + constraintName: pk_user_personal_data From 318325b3362807bab580d6e222f05c6766526102 Mon Sep 17 00:00:00 2001 From: rollw <73376341+roll-w@users.noreply.github.com> Date: Thu, 2 Apr 2026 00:35:04 +0800 Subject: [PATCH 3/6] chore: polish code --- gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 43764 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 10 ++-- gradlew.bat | 6 ++- .../review/persistence/ReviewTaskEntity.kt | 7 ++- .../lampray/staff/persistence/StaffDao.kt | 6 +-- .../{StaffDo.kt => StaffEntity.kt} | 46 +++++++++--------- .../staff/persistence/StaffRepository.kt | 14 +++--- .../CreateStaffOnUserCreateListener.java | 6 +-- .../staff/service/StaffServiceImpl.java | 8 +-- .../lampray/user/repository/UserDao.kt | 6 +-- .../repository/{UserDo.kt => UserEntity.kt} | 16 +++--- .../lampray/user/repository/UserRepository.kt | 22 ++++----- .../user/service/UserProviderService.java | 14 +++--- .../lampray/user/service/UserServiceImpl.java | 16 +++--- 15 files changed, 94 insertions(+), 87 deletions(-) rename lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/{StaffDo.kt => StaffEntity.kt} (90%) rename lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/{UserDo.kt => UserEntity.kt} (96%) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f3d4ba8a0da8d277868979cfbc8ad796..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch delta 35073 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 delta 34912 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

Gn4F+B~IivciU9^t0j-Mgao3 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 dDuazZG^u*-) zCV{{p$D*4m=Z;!QJ0C3EBfaUv#@3CmRgS-#a4+Uk9LtR(mzdSt-?*B-Q{Jgs!F;+_ zT&Jc~{N951OskjVtnWOLnfUacmSEkQ#fQojqa=HN?aN#ubFA){K&TrfoSnBphJ<43S zS5?OUrqPYpEqf;(zc;IIty0d`-n-Ke%PlArJt^DN>ZYW)ZS5z$Rnx=cxbM1#zPRNS1N0u>c z*gpBeTvyKAjdr#Uz%$J3C+p2~l9`-P6`=_1Y83#nAp`Kd5jfb*H#u;=%w#=&g~|Q% z1!N9S7n+#|tj#t98*^$X3S9)i3I*n?N+YghXJ9Z!QN%wvV7|W$;;Lx|1{)Ol%c4L- zlz|txtAK4ozLL`xMPZR7Sm6grRj@+Db&BXF9#sG|gMywm=IO1O01imHv3 zFjX9ri>su;HX}wK85k5$R0A&?fdtA5_Q_vX$jc(elo%M~QB>A-K~#2igOjH$d^80Z zC8#NjzZarXrcVZJJEF73z+ix4=7c`5N>TJt7>~rfl$^E}UAMlP2$P)vS26{cE?ONf_&Vd@-E2?PMU+S~pB diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8937ed68..841ad098 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ # -# Copyright (C) 2023-2025 RollW +# Copyright (C) 2023-2026 RollW # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 7fd7b6f6..70874212 100644 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright (C) 2023-2025 RollW +# Copyright (C) 2023-2026 RollW # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -84,7 +84,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +112,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -203,7 +203,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -211,7 +211,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 25da30db..db3a6ac2 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @@ -68,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewTaskEntity.kt b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewTaskEntity.kt index 84b7c185..97b4db79 100644 --- a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewTaskEntity.kt +++ b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewTaskEntity.kt @@ -249,7 +249,10 @@ class ReviewTaskEntity( return resourceId == other.resourceId } - final override fun hashCode(): Int = - if (this is HibernateProxy) this.hibernateLazyInitializer.persistentClass.hashCode() else javaClass.hashCode() + final override fun hashCode(): Int { + val effectiveClass = + if (this is HibernateProxy) this.hibernateLazyInitializer.persistentClass else javaClass + return 31 * effectiveClass.hashCode() + resourceId.hashCode() + } } diff --git a/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffDao.kt b/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffDao.kt index fc4069f7..908e9134 100644 --- a/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffDao.kt +++ b/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffDao.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,5 +23,5 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface StaffDao : CommonDao { -} \ No newline at end of file +interface StaffDao : CommonDao { +} diff --git a/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffDo.kt b/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffEntity.kt similarity index 90% rename from lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffDo.kt rename to lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffEntity.kt index dd0e1075..371098fe 100644 --- a/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffDo.kt +++ b/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,7 +44,7 @@ import java.time.OffsetDateTime */ @Entity @Table(name = "staff") -class StaffDo( +class StaffEntity( @Id @Column(name = "id", nullable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -54,9 +54,11 @@ class StaffDo( override var userId: Long = 0, @ElementCollection(targetClass = StaffType::class, fetch = FetchType.EAGER) - @CollectionTable(name = "staff_types", joinColumns = [ - JoinColumn(name = "id", referencedColumnName = "id"), - ], foreignKey = ForeignKey(name = "index__type__id")) + @CollectionTable( + name = "staff_types", joinColumns = [ + JoinColumn(name = "id", referencedColumnName = "id"), + ], foreignKey = ForeignKey(name = "index__type__id") + ) @Column(name = "type", nullable = false, length = 20) @Enumerated(EnumType.STRING) @JdbcTypeCode(SqlTypes.VARCHAR) @@ -77,13 +79,13 @@ class StaffDo( var deleted: Boolean = false ) : DataEntity, AttributedStaff { override fun getEntityId(): Long? = id - + fun setId(id: Long?) { this.id = id } override fun getCreateTime(): OffsetDateTime = createTime - + fun setCreateTime(createTime: OffsetDateTime) { this.createTime = createTime } @@ -93,14 +95,14 @@ class StaffDo( fun setUpdateTime(updateTime: OffsetDateTime) { this.updateTime = updateTime } - + override val staffId: Long get() = id!! override fun getResourceId(): Long = id!! fun lock() = Staff( - id, userId, types, createTime, updateTime, + id, userId, types, createTime, updateTime, asUser, deleted ) @@ -112,10 +114,10 @@ class StaffDo( private var updateTime: OffsetDateTime? = null private var asUser = false private var deleted = false - + constructor() - - constructor(staff: StaffDo) { + + constructor(staff: StaffEntity) { this.id = staff.id this.userId = staff.userId this.types = staff.types.toMutableSet() @@ -129,7 +131,7 @@ class StaffDo( this.id = id } - fun setUserId(userId: Long) = apply{ + fun setUserId(userId: Long) = apply { this.userId = userId } @@ -166,26 +168,26 @@ class StaffDo( fun setDeleted(deleted: Boolean) = apply { this.deleted = deleted } - - fun build() = StaffDo( + + fun build() = StaffEntity( id, userId, types ?: emptySet(), - createTime!!, - updateTime!!, + createTime!!, + updateTime!!, asUser, deleted ) } companion object { @JvmStatic - fun Staff.toDo(): StaffDo { - return StaffDo( + fun Staff.toEntity(): StaffEntity { + return StaffEntity( entityId, userId, types, createTime, updateTime, isAsUser, isDeleted ) } - + @JvmStatic fun builder() = Builder() } - -} \ No newline at end of file + +} diff --git a/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffRepository.kt b/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffRepository.kt index b57f74a6..14298228 100644 --- a/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffRepository.kt +++ b/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,20 +27,20 @@ import java.util.Optional @Repository class StaffRepository( private val staffDao: StaffDao -) : CommonRepository(staffDao) { - fun findByUserId(userId: Long): Optional { +) : CommonRepository(staffDao) { + fun findByUserId(userId: Long): Optional { return findOne { root, _, criteriaBuilder -> - criteriaBuilder.equal(root.get(StaffDo_.userId), userId) + criteriaBuilder.equal(root.get("userId"), userId) } } - fun findByTypes(types: Set): List { + fun findByTypes(types: Set): List { return findAll { root, _, criteriaBuilder -> criteriaBuilder.and( *types.map { - criteriaBuilder.isMember(it, root.get(StaffDo_.types)) + criteriaBuilder.isMember(it, root.get>("types")) }.toTypedArray() ) } } -} \ No newline at end of file +} diff --git a/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/service/CreateStaffOnUserCreateListener.java b/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/service/CreateStaffOnUserCreateListener.java index 557cb489..fdd0ea1b 100644 --- a/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/service/CreateStaffOnUserCreateListener.java +++ b/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/service/CreateStaffOnUserCreateListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import org.springframework.stereotype.Component; import space.lingu.NonNull; import tech.lamprism.lampray.staff.StaffType; -import tech.lamprism.lampray.staff.persistence.StaffDo; +import tech.lamprism.lampray.staff.persistence.StaffEntity; import tech.lamprism.lampray.staff.persistence.StaffRepository; import tech.lamprism.lampray.user.AttributedUser; import tech.lamprism.lampray.user.Role; @@ -50,7 +50,7 @@ public void onApplicationEvent(@NonNull NewUserCreatedEvent event) { } OffsetDateTime now = OffsetDateTime.now(); StaffType type = StaffType.of(attributedUser.getRole()); - StaffDo staff = StaffDo.builder() + StaffEntity staff = StaffEntity.builder() .setUserId(attributedUser.getUserId()) .setCreateTime(now) .setUpdateTime(now) diff --git a/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/service/StaffServiceImpl.java b/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/service/StaffServiceImpl.java index e9df1d13..0d121fde 100644 --- a/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/service/StaffServiceImpl.java +++ b/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/service/StaffServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ import tech.lamprism.lampray.LampException; import tech.lamprism.lampray.staff.StaffInfo; import tech.lamprism.lampray.staff.StaffService; -import tech.lamprism.lampray.staff.persistence.StaffDo; +import tech.lamprism.lampray.staff.persistence.StaffEntity; import tech.lamprism.lampray.staff.persistence.StaffRepository; import tech.lamprism.lampray.user.UserIdentity; import tech.lamprism.lampray.user.UserProvider; @@ -42,7 +42,7 @@ public StaffServiceImpl(StaffRepository staffRepository, @Override public StaffInfo getStaffByUser(long userId) { - StaffDo staff = staffRepository.findByUserId(userId).orElse(null); + StaffEntity staff = staffRepository.findByUserId(userId).orElse(null); if (staff == null) { throw new LampException(DataErrorCode.ERROR_DATA_NOT_EXIST); } @@ -52,7 +52,7 @@ public StaffInfo getStaffByUser(long userId) { @Override public StaffInfo getStaff(long staffId) { - StaffDo staff = staffRepository.findById(staffId).orElse(null); + StaffEntity staff = staffRepository.findById(staffId).orElse(null); if (staff == null) { throw new LampException(DataErrorCode.ERROR_DATA_NOT_EXIST); } diff --git a/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserDao.kt b/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserDao.kt index eff57074..00832192 100644 --- a/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserDao.kt +++ b/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserDao.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,5 +21,5 @@ import tech.lamprism.lampray.common.data.Dao @Dao @JvmDefaultWithoutCompatibility -interface UserDao : CommonDao { -} \ No newline at end of file +interface UserDao : CommonDao { +} diff --git a/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserDo.kt b/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserEntity.kt similarity index 96% rename from lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserDo.kt rename to lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserEntity.kt index f1cdc3fc..f297d136 100644 --- a/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserDo.kt +++ b/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserEntity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ import java.time.OffsetDateTime UniqueConstraint(columnNames = ["username"], name = "index__username") ] ) -class UserDo( +class UserEntity( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") @@ -170,7 +170,7 @@ class UserDo( } override fun toString(): String { - return "UserDo{" + + return "UserEntity{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + @@ -210,7 +210,7 @@ class UserDo( constructor() - constructor(other: UserDo) { + constructor(other: UserEntity) { this.id = other.id this.username = other.username this.password = other.password @@ -268,8 +268,8 @@ class UserDo( this.canceled = canceled } - fun build(): UserDo { - return UserDo( + fun build(): UserEntity { + return UserEntity( id, username, password, @@ -287,8 +287,8 @@ class UserDo( companion object { @JvmStatic - fun User.toDo(): UserDo { - return UserDo( + fun User.toEntity(): UserEntity { + return UserEntity( entityId, username, password, role, registerTime, updateTime, email, phone, isEnabled, isLocked, isCanceled diff --git a/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserRepository.kt b/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserRepository.kt index e5a3f600..67e5cc3b 100644 --- a/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserRepository.kt +++ b/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,21 +27,21 @@ import java.util.Optional @Repository class UserRepository( private val userDao: UserDao -) : CommonRepository(userDao) { +) : CommonRepository(userDao) { - fun searchBy(keyword: String): List { + fun searchBy(keyword: String): List { return userDao.findAll(createSearchBySpec(keyword)) } - fun findByUsername(username: String): Optional { + fun findByUsername(username: String): Optional { return findOne { root, _, criteriaBuilder -> - criteriaBuilder.equal(root.get(UserDo_.username), username) + criteriaBuilder.equal(root.get("username"), username) } } - fun findByEmail(email: String): Optional { + fun findByEmail(email: String): Optional { return findOne { root, _, criteriaBuilder -> - criteriaBuilder.equal(root.get(UserDo_.email), email) + criteriaBuilder.equal(root.get("email"), email) } } @@ -57,8 +57,8 @@ class UserRepository( return userDao.count() > 0 } - private fun createSearchBySpec(keyword: String) = - Specification { root, _, builder -> - builder.like(root.get(UserDo_.username), keyword) + private fun createSearchBySpec(keyword: String): Specification = + Specification { root, _, builder -> + builder.like(root.get("username"), keyword) } -} \ No newline at end of file +} diff --git a/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/service/UserProviderService.java b/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/service/UserProviderService.java index 1338bfd4..b36a716e 100644 --- a/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/service/UserProviderService.java +++ b/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/service/UserProviderService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ import tech.lamprism.lampray.user.UserResourceKind; import tech.lamprism.lampray.user.UserViewException; import tech.lamprism.lampray.user.filter.UserInfoFilter; -import tech.lamprism.lampray.user.repository.UserDo; +import tech.lamprism.lampray.user.repository.UserEntity; import tech.lamprism.lampray.user.repository.UserRepository; import tech.rollw.common.web.CommonRuntimeException; import tech.rollw.common.web.UserErrorCode; @@ -82,8 +82,8 @@ public AttributedUser provide(@NonNull SystemResource rawSystemResource) return getUser(rawSystemResource.getResourceId()); } - private UserDo getUser(long id) { - UserDo user = userRepository.findById(id).orElse(null); + private UserEntity getUser(long id) { + UserEntity user = userRepository.findById(id).orElse(null); if (user == null) { throw new UserViewException(UserErrorCode.ERROR_USER_NOT_EXIST); } @@ -92,12 +92,12 @@ private UserDo getUser(long id) { @Override public void updateUser(User user) { - userRepository.save(UserDo.toDo(user)); + userRepository.save(UserEntity.toEntity(user)); } @Override public boolean checkUsernameExist(String username, long id) { - UserDo user = userRepository.findByUsername(username).orElse(null); + UserEntity user = userRepository.findByUsername(username).orElse(null); if (user == null) { return false; } @@ -109,7 +109,7 @@ public boolean checkUsernameExist(String username, long id) { @Override public boolean checkEmailExist(String email, long id) { - UserDo user = userRepository.findByEmail(email).orElse(null); + UserEntity user = userRepository.findByEmail(email).orElse(null); if (user == null) { return false; } diff --git a/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/service/UserServiceImpl.java b/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/service/UserServiceImpl.java index 3da25a1b..e197ca77 100644 --- a/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/service/UserServiceImpl.java +++ b/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/service/UserServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ import tech.lamprism.lampray.user.filter.UserFilteringInfo; import tech.lamprism.lampray.user.filter.UserFilteringInfoType; import tech.lamprism.lampray.user.filter.UserInfoFilter; -import tech.lamprism.lampray.user.repository.UserDo; +import tech.lamprism.lampray.user.repository.UserEntity; import tech.lamprism.lampray.user.repository.UserRepository; import tech.rollw.common.web.ErrorCode; import tech.rollw.common.web.UserErrorCode; @@ -66,7 +66,7 @@ public UserServiceImpl(UserRepository userRepository, @Override public String getSignature(long userId) { - UserDo user = userRepository.findById(userId).orElse(null); + UserEntity user = userRepository.findById(userId).orElse(null); if (user == null) { return null; } @@ -86,7 +86,7 @@ public AttributedUser createUser(String username, String password, validate(username, password, email); OffsetDateTime time = OffsetDateTime.now(); - UserDo user = UserDo.builder() + UserEntity user = UserEntity.builder() .setUsername(username) .setPassword(passwordEncoder.encode(password)) .setRole(role) @@ -95,7 +95,7 @@ public AttributedUser createUser(String username, String password, .setUpdateTime(time) .setEmail(email) .build(); - UserDo inserted = userRepository.save(user); + UserEntity inserted = userRepository.save(user); NewUserCreatedEvent newUserCreatedEvent = new NewUserCreatedEvent(inserted); eventPublisher.publishEvent(newUserCreatedEvent); return inserted; @@ -119,7 +119,7 @@ private void validate(String username, @Override public AttributedUserDetails getUser(long userId) throws UserViewException { - UserDo user = userRepository.findById(userId).orElse(null); + UserEntity user = userRepository.findById(userId).orElse(null); if (user == null) { throw new UserViewException(UserErrorCode.ERROR_USER_NOT_EXIST); } @@ -128,7 +128,7 @@ public AttributedUserDetails getUser(long userId) throws UserViewException { @Override public AttributedUserDetails getUser(String username) throws UserViewException { - UserDo user = userRepository.findByUsername(username).orElse(null); + UserEntity user = userRepository.findByUsername(username).orElse(null); if (user == null) { throw new UserViewException(UserErrorCode.ERROR_USER_NOT_EXIST); } @@ -137,7 +137,7 @@ public AttributedUserDetails getUser(String username) throws UserViewException { @Override public AttributedUserDetails getUserByEmail(String email) throws UserViewException { - UserDo user = userRepository.findByEmail(email).orElse(null); + UserEntity user = userRepository.findByEmail(email).orElse(null); if (user == null) { throw new UserViewException(UserErrorCode.ERROR_USER_NOT_EXIST); } From 011be986a42eaae0fa8b0781a9e016d09322606e Mon Sep 17 00:00:00 2001 From: rollw <73376341+roll-w@users.noreply.github.com> Date: Sat, 4 Apr 2026 22:40:41 +0800 Subject: [PATCH 4/6] refactor(entity): rename user ID column for consistency --- .../persistence/UserPersonalDataEntity.kt | 2 +- .../db/baseline/baseline-0.1.0-alpha1.yaml | 2 +- ...0.1.0-alpha1-02-resource-id-migration.yaml | 46 ------------------- 3 files changed, 2 insertions(+), 48 deletions(-) diff --git a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataEntity.kt b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataEntity.kt index 7c4e5e07..d3b89d89 100644 --- a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataEntity.kt +++ b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataEntity.kt @@ -44,7 +44,7 @@ import java.time.OffsetDateTime @Table(name = "user_personal_data") class UserPersonalDataEntity( @Id - @Column(name = "user_id", nullable = false) + @Column(name = "id", nullable = false) private var userId: Long = 0, @Column(name = "nickname", nullable = false, length = 120) diff --git a/lampray-web/src/main/resources/db/baseline/baseline-0.1.0-alpha1.yaml b/lampray-web/src/main/resources/db/baseline/baseline-0.1.0-alpha1.yaml index fbf27e06..c29251d5 100644 --- a/lampray-web/src/main/resources/db/baseline/baseline-0.1.0-alpha1.yaml +++ b/lampray-web/src/main/resources/db/baseline/baseline-0.1.0-alpha1.yaml @@ -582,7 +582,7 @@ databaseChangeLog: nullable: false primaryKey: true primaryKeyName: pk_user_personal_data - name: user_id + name: id type: BIGINT - column: constraints: diff --git a/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml b/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml index 30009124..19c6531a 100644 --- a/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml +++ b/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml @@ -211,49 +211,3 @@ databaseChangeLog: tableName: system_setting columnNames: resource_id constraintName: uc_system_setting_resource_id - - - changeSet: - id: 0.1.0-alpha1-16-user-personal-data-user-id - author: RollW - objectQuotingStrategy: QUOTE_ONLY_RESERVED_WORDS - dbms: mysql,mariadb,postgresql,h2,oracle,sqlserver - preConditions: - - onFail: MARK_RAN - - columnExists: - tableName: user_personal_data - columnName: id - - not: - columnExists: - tableName: user_personal_data - columnName: user_id - changes: - - dropPrimaryKey: - tableName: user_personal_data - - renameColumn: - tableName: user_personal_data - oldColumnName: id - newColumnName: user_id - columnDataType: BIGINT - - addPrimaryKey: - tableName: user_personal_data - columnNames: user_id - constraintName: pk_user_personal_data - - - changeSet: - id: 0.1.0-alpha1-18-user-personal-data-primary-key-recovery - author: RollW - objectQuotingStrategy: QUOTE_ONLY_RESERVED_WORDS - dbms: mysql,mariadb,postgresql,h2,oracle,sqlserver - preConditions: - - onFail: MARK_RAN - - columnExists: - tableName: user_personal_data - columnName: user_id - - not: - - primaryKeyExists: - tableName: user_personal_data - changes: - - addPrimaryKey: - tableName: user_personal_data - columnNames: user_id - constraintName: pk_user_personal_data From 3960debae872b2358ce0a24dc3b49a96eadf217f Mon Sep 17 00:00:00 2001 From: rollw <73376341+roll-w@users.noreply.github.com> Date: Sun, 5 Apr 2026 17:28:24 +0800 Subject: [PATCH 5/6] refactor: simplify repository interfaces and remove unused methods --- .../content/article/persistence/ArticleDao.kt | 7 +- .../article/persistence/ArticleRepository.kt | 12 ++- .../content/comment/persistence/CommentDao.kt | 4 +- .../comment/persistence/CommentRepository.kt | 10 +- .../lampray/content/ContentMetadata.java | 28 ++---- .../persistence/FavoriteGroupRepository.kt | 12 +-- .../persistence/FavoriteItemRepository.kt | 6 +- .../content/persistence/ContentMetadataDao.kt | 2 +- .../persistence/ContentMetadataEntity.kt | 25 ++--- .../persistence/ContentMetadataRepository.kt | 20 ++-- .../content/service/ContentService.java | 8 +- .../review/persistence/ReviewJobRepository.kt | 6 +- .../persistence/ReviewTaskRepository.kt | 8 +- .../RegisterVerificationToken.java | 22 ++--- .../repository/RegisterTokenDao.kt | 2 +- .../repository/RegisterTokenEntity.kt | 37 ++------ .../repository/RegisterTokenRepository.kt | 10 +- .../service/LoginRegisterService.java | 8 +- .../system/resource/LocalizedMessage.kt | 27 ++---- .../resource/data/LocalizedMessageDao.kt | 3 +- .../resource/data/LocalizedMessageEntity.kt | 32 ++----- .../data/LocalizedMessageRepository.kt | 19 ++-- .../LocalizedMessageResourceService.kt | 4 +- .../lampray/setting/SystemSetting.java | 16 +--- .../lampray/setting/data/SystemSettingDao.kt | 3 +- .../setting/data/SystemSettingEntity.kt | 20 +--- .../setting/data/SystemSettingRepository.kt | 20 +--- .../service/SystemSettingConfigProvider.kt | 19 ++-- .../staff/persistence/StaffRepository.kt | 6 +- .../persistence/UserPersonalDataDao.kt | 6 +- .../persistence/UserPersonalDataRepository.kt | 13 +-- .../service/UserPersonalDataServiceImpl.java | 9 +- .../lampray/user/repository/UserRepository.kt | 12 +-- .../db/baseline/baseline-0.1.0-alpha1.yaml | 22 ----- ...0.1.0-alpha1-02-resource-id-migration.yaml | 92 ------------------- 35 files changed, 135 insertions(+), 415 deletions(-) diff --git a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleDao.kt b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleDao.kt index c9228779..2e124bef 100644 --- a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleDao.kt +++ b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleDao.kt @@ -16,8 +16,6 @@ package tech.lamprism.lampray.content.article.persistence -import org.springframework.data.jpa.repository.Query -import org.springframework.data.repository.query.Param import tech.lamprism.lampray.common.data.CommonDao import tech.lamprism.lampray.common.data.Dao @@ -25,7 +23,4 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface ArticleDao : CommonDao { - @Query("FROM ArticleEntity a WHERE a.userId = :userId") - fun findAllByUserId(@Param("userId") userId: Long): List -} +interface ArticleDao : CommonDao diff --git a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleRepository.kt b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleRepository.kt index 577580e5..b865d2e7 100644 --- a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleRepository.kt +++ b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/persistence/ArticleRepository.kt @@ -26,22 +26,24 @@ import java.util.Optional */ @Repository class ArticleRepository( - private val articleDao: ArticleDao + articleDao: ArticleDao ) : CommonRepository(articleDao) { override fun save(entity: S): S { - return articleDao.saveAndFlush(entity) + return saveAndFlush(entity) } override fun saveAll(entities: Iterable): List { - return articleDao.saveAllAndFlush(entities) + return saveAllAndFlush(entities) } fun findAllByUserId(userId: Long): List { - return articleDao.findAllByUserId(userId) + return findAll { root, _, builder -> + builder.equal(root.get(ArticleEntity_.userId), userId) + } } fun findByTitle(title: String, userId: Long): Optional { - return articleDao.findOne(createTitleSpecification(title, userId)) + return findOne(createTitleSpecification(title, userId)) } private fun createTitleSpecification( diff --git a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentDao.kt b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentDao.kt index 78b2ad1a..48b1cb5c 100644 --- a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentDao.kt +++ b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentDao.kt @@ -23,6 +23,4 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface CommentDao : CommonDao { - fun findAllByUserId(userId: Long): List -} +interface CommentDao : CommonDao diff --git a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentRepository.kt b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentRepository.kt index 45e5486a..fb60c57e 100644 --- a/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentRepository.kt +++ b/lampray-content/comment-service/src/main/java/tech/lamprism/lampray/content/comment/persistence/CommentRepository.kt @@ -26,18 +26,20 @@ import tech.lamprism.lampray.content.ContentType */ @Repository class CommentRepository( - private val commentDao: CommentDao + commentDao: CommentDao ) : CommonRepository(commentDao) { override fun save(entity: S): S { - return commentDao.saveAndFlush(entity) + return saveAndFlush(entity) } override fun saveAll(entities: Iterable): List { - return commentDao.saveAllAndFlush(entities) + return saveAllAndFlush(entities) } fun findAllByUserId(userId: Long): List { - return commentDao.findAllByUserId(userId) + return findAll { root, _, builder -> + builder.equal(root.get(CommentEntity_.userId), userId) + } } fun findByContent( diff --git a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentMetadata.java b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentMetadata.java index 3c99250b..01f63034 100644 --- a/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentMetadata.java +++ b/lampray-content/content-api/src/main/java/tech/lamprism/lampray/content/ContentMetadata.java @@ -27,9 +27,8 @@ * @author RollW */ @SuppressWarnings({"ClassCanBeRecord"}) -public class ContentMetadata implements DataEntity, ContentTrait { +public class ContentMetadata implements DataEntity, ContentTrait { private final Long id; - private final String resourceId; private final long userId; private final String contentId; @@ -42,13 +41,12 @@ public class ContentMetadata implements DataEntity, ContentTrait { @NonNull private final ContentAccessAuthType contentAccessAuthType; - public ContentMetadata(Long id, String resourceId, long userId, + public ContentMetadata(Long id, long userId, @NonNull String contentId, @NonNull ContentType contentType, @NonNull ContentStatus contentStatus, @NonNull ContentAccessAuthType contentAccessAuthType) { this.id = id; - this.resourceId = resourceId; this.userId = userId; this.contentId = contentId; this.contentType = contentType; @@ -59,9 +57,6 @@ public ContentMetadata(Long id, String resourceId, long userId, @SuppressWarnings("all") private void checkForNull() { - if (resourceId == null) { - throw new NullPointerException("resourceId is null"); - } if (contentId == null) { throw new NullPointerException("contentId is null"); } @@ -81,8 +76,8 @@ public Long getId() { } @Override - public String getEntityId() { - return resourceId; + public Long getEntityId() { + return id; } @NonNull @@ -136,9 +131,8 @@ public SystemResourceKind getSystemResourceKind() { return ContentMetadataResourceKind.INSTANCE; } - public static class Builder implements EntityBuilder { + public static class Builder implements EntityBuilder { private Long id; - private String resourceId; private long userId; private String contentId; private ContentType contentType; @@ -150,7 +144,6 @@ public Builder() { public Builder(ContentMetadata contentMetadata) { this.id = contentMetadata.id; - this.resourceId = contentMetadata.resourceId; this.userId = contentMetadata.userId; this.contentId = contentMetadata.contentId; this.contentType = contentMetadata.contentType; @@ -159,8 +152,8 @@ public Builder(ContentMetadata contentMetadata) { } @Override - public Builder setEntityId(String id) { - return setResourceId(id); + public Builder setEntityId(Long id) { + return setId(id); } public Builder setId(Long id) { @@ -168,11 +161,6 @@ public Builder setId(Long id) { return this; } - public Builder setResourceId(String resourceId) { - this.resourceId = resourceId; - return this; - } - public Builder setUserId(long userId) { this.userId = userId; return this; @@ -200,7 +188,7 @@ public Builder setContentAccessAuthType(@NonNull ContentAccessAuthType contentAc @Override public ContentMetadata build() { - return new ContentMetadata(id, resourceId, userId, contentId, + return new ContentMetadata(id, userId, contentId, contentType, contentStatus, contentAccessAuthType); } } diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupRepository.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupRepository.kt index 254075e7..4f228b92 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupRepository.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteGroupRepository.kt @@ -26,28 +26,28 @@ import java.util.Optional */ @Repository class FavoriteGroupRepository( - private val favoriteGroupDao: FavoriteGroupDao + favoriteGroupDao: FavoriteGroupDao ) : CommonRepository(favoriteGroupDao) { override fun save(entity: S): S { - return favoriteGroupDao.saveAndFlush(entity) + return saveAndFlush(entity) } override fun saveAll(entities: Iterable): List { - return favoriteGroupDao.saveAllAndFlush(entities) + return saveAllAndFlush(entities) } fun findByName(name: String, operator: Operator): Optional = findOne { root, _, builder -> builder.and( - builder.equal(root.get("name"), name), - builder.equal(root.get("userId"), operator.operatorId) + builder.equal(root.get(FavoriteGroupEntity_.name), name), + builder.equal(root.get(FavoriteGroupEntity_.userId), operator.operatorId) ) } fun findByUser(userId: Long): List = findAll { root, _, criteriaBuilder -> criteriaBuilder.and( - criteriaBuilder.equal(root.get("userId"), userId) + criteriaBuilder.equal(root.get(FavoriteGroupEntity_.userId), userId) ) } } diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemRepository.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemRepository.kt index 575d55ad..62101df4 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemRepository.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/favorite/persistence/FavoriteItemRepository.kt @@ -24,13 +24,13 @@ import tech.lamprism.lampray.common.data.CommonRepository */ @Repository class FavoriteItemRepository( - private val favoriteItemDao: FavoriteItemDao + favoriteItemDao: FavoriteItemDao ) : CommonRepository(favoriteItemDao) { override fun save(entity: S): S { - return favoriteItemDao.saveAndFlush(entity) + return saveAndFlush(entity) } override fun saveAll(entities: Iterable): List { - return favoriteItemDao.saveAllAndFlush(entities) + return saveAllAndFlush(entities) } } diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataDao.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataDao.kt index 8f5ba44b..5e3c2e01 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataDao.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataDao.kt @@ -23,4 +23,4 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface ContentMetadataDao : CommonDao +interface ContentMetadataDao : CommonDao diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataEntity.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataEntity.kt index bbfad17d..36fad8b0 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataEntity.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataEntity.kt @@ -20,12 +20,12 @@ import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Table import jakarta.persistence.UniqueConstraint -import org.hibernate.annotations.Generated import org.hibernate.annotations.JdbcTypeCode -import org.hibernate.generator.EventType import org.hibernate.type.SqlTypes import tech.lamprism.lampray.DataEntity import tech.lamprism.lampray.TimeAttributed @@ -48,14 +48,11 @@ import java.time.OffsetDateTime ] ) class ContentMetadataEntity( + @Id @Column(name = "id", nullable = false, insertable = false, updatable = false) - @Generated(event = [EventType.INSERT]) + @GeneratedValue(strategy = GenerationType.IDENTITY) private var id: Long? = null, - @Id - @Column(name = "resource_id", nullable = false, length = 64, unique = true) - private var resourceId: String, - @Column(name = "user_id", nullable = false) var userId: Long = 0, @@ -76,8 +73,8 @@ class ContentMetadataEntity( @Enumerated(EnumType.STRING) @JdbcTypeCode(SqlTypes.VARCHAR) var contentAccessAuthType: ContentAccessAuthType = ContentAccessAuthType.PUBLIC -) : DataEntity, ContentTrait { - override fun getEntityId(): String = resourceId +) : DataEntity, ContentTrait { + override fun getEntityId(): Long = id!! fun getId(): Long? = id @@ -101,7 +98,7 @@ class ContentMetadataEntity( } fun lock(): ContentMetadata = ContentMetadata( - id, resourceId, userId, contentId, contentType, contentStatus, contentAccessAuthType + id, userId, contentId, contentType, contentStatus, contentAccessAuthType ) fun toBuilder(): Builder { @@ -116,7 +113,6 @@ class ContentMetadataEntity( fun ContentMetadata.toEntity() = ContentMetadataEntity( id = id, - resourceId = entityId, userId = userId, contentId = contentId, contentType = contentType, @@ -127,7 +123,6 @@ class ContentMetadataEntity( class Builder { private var id: Long? = null - private var resourceId: String? = null private var userId: Long = 0 private var contentId: String = "" private var contentType: ContentType? = null @@ -138,7 +133,6 @@ class ContentMetadataEntity( constructor(other: ContentMetadataEntity) { this.id = other.id - this.resourceId = other.resourceId this.userId = other.userId this.contentId = other.contentId this.contentType = other.contentType @@ -150,10 +144,6 @@ class ContentMetadataEntity( this.id = id } - fun setResourceId(resourceId: String) = apply { - this.resourceId = resourceId - } - fun setUserId(userId: Long) = apply { this.userId = userId } @@ -177,7 +167,6 @@ class ContentMetadataEntity( fun build(): ContentMetadataEntity { return ContentMetadataEntity( id = id, - resourceId = resourceId!!, userId = userId, contentId = contentId, contentType = contentType!!, diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataRepository.kt b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataRepository.kt index c395f508..7da5dda7 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataRepository.kt +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/persistence/ContentMetadataRepository.kt @@ -19,7 +19,6 @@ package tech.lamprism.lampray.content.persistence import org.springframework.stereotype.Repository import tech.lamprism.lampray.common.data.CommonRepository import tech.lamprism.lampray.content.ContentTrait -import tech.lamprism.lampray.content.ContentType import java.util.Optional /** @@ -27,21 +26,14 @@ import java.util.Optional */ @Repository class ContentMetadataRepository( - private val contentMetadataDao: ContentMetadataDao -) : CommonRepository(contentMetadataDao) { - override fun save(entity: S): S { - return contentMetadataDao.saveAndFlush(entity) - } - - override fun saveAll(entities: Iterable): List { - return contentMetadataDao.saveAllAndFlush(entities) - } + contentMetadataDao: ContentMetadataDao +) : CommonRepository(contentMetadataDao) { fun findByContent(content: ContentTrait): Optional { return findOne { root, _, criteriaBuilder -> criteriaBuilder.and( - criteriaBuilder.equal(root.get("contentId"), content.contentId), - criteriaBuilder.equal(root.get("contentType"), content.contentType) + criteriaBuilder.equal(root.get(ContentMetadataEntity_.contentId), content.contentId), + criteriaBuilder.equal(root.get(ContentMetadataEntity_.contentType), content.contentType) ) } } @@ -53,8 +45,8 @@ class ContentMetadataRepository( return findAll { root, _, builder -> val predicates = contents.map { builder.and( - builder.equal(root.get("contentId"), it.contentId), - builder.equal(root.get("contentType"), it.contentType) + builder.equal(root.get(ContentMetadataEntity_.contentId), it.contentId), + builder.equal(root.get(ContentMetadataEntity_.contentType), it.contentType) ) } builder.or(*predicates.toTypedArray()) diff --git a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/service/ContentService.java b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/service/ContentService.java index 016e97b9..611ac39b 100644 --- a/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/service/ContentService.java +++ b/lampray-content/content-service/src/main/java/tech/lamprism/lampray/content/service/ContentService.java @@ -20,7 +20,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; -import tech.lamprism.lampray.common.data.ResourceIdGenerator; import tech.lamprism.lampray.content.Content; import tech.lamprism.lampray.content.ContentAccessAuthType; import tech.lamprism.lampray.content.ContentAccessCredentials; @@ -28,7 +27,6 @@ import tech.lamprism.lampray.content.ContentDetails; import tech.lamprism.lampray.content.ContentMetadata; import tech.lamprism.lampray.content.ContentMetadataDetails; -import tech.lamprism.lampray.content.ContentMetadataResourceKind; import tech.lamprism.lampray.content.ContentProvider; import tech.lamprism.lampray.content.ContentProviderFactory; import tech.lamprism.lampray.content.ContentPublishProvider; @@ -74,7 +72,6 @@ public class ContentService implements ContentAccessService, private final ContentProviderFactory contentProviderFactory; private final ContentPermitChecker contentPermitChecker; private final ContentMetadataRepository contentMetadataRepository; - private final ResourceIdGenerator resourceIdGenerator; public ContentService(List contentPublishers, List uncreatedContentPreCheckers, @@ -82,8 +79,7 @@ public ContentService(List contentPublishers, List contentPublishListeners, ContentProviderFactory contentProviderFactory, ContentPermitChecker contentPermitChecker, - ContentMetadataRepository contentMetadataRepository, - ResourceIdGenerator resourceIdGenerator) { + ContentMetadataRepository contentMetadataRepository) { this.contentPublishers = contentPublishers; this.uncreatedContentPreCheckers = uncreatedContentPreCheckers; this.contentCollectionProviders = contentCollectionProviders; @@ -91,7 +87,6 @@ public ContentService(List contentPublishers, this.contentProviderFactory = contentProviderFactory; this.contentPermitChecker = contentPermitChecker; this.contentMetadataRepository = contentMetadataRepository; - this.resourceIdGenerator = resourceIdGenerator; } /** @@ -194,7 +189,6 @@ public ContentDetails publishContent(UncreatedContent uncreatedContent) throws C ); ContentMetadataEntity.Builder contentMetadataBuilder = ContentMetadataEntity .builder() - .setResourceId(resourceIdGenerator.nextId(ContentMetadataResourceKind.INSTANCE)) .setContentId(contentDetails.getContentId()) .setContentType(contentDetails.getContentType()) .setUserId(contentDetails.getUserId()) diff --git a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewJobRepository.kt b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewJobRepository.kt index f62c1f72..dbf5d51b 100644 --- a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewJobRepository.kt +++ b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewJobRepository.kt @@ -26,14 +26,14 @@ import tech.lamprism.lampray.content.review.ReviewStatus */ @Repository class ReviewJobRepository( - private val reviewJobDao: ReviewJobDao + reviewJobDao: ReviewJobDao ) : CommonRepository(reviewJobDao) { override fun save(entity: S): S { - return reviewJobDao.saveAndFlush(entity) + return saveAndFlush(entity) } override fun saveAll(entities: Iterable): List { - return reviewJobDao.saveAllAndFlush(entities) + return saveAllAndFlush(entities) } fun findByContent( diff --git a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewTaskRepository.kt b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewTaskRepository.kt index ea147225..59b9b12d 100644 --- a/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewTaskRepository.kt +++ b/lampray-content/review-service/src/main/java/tech/lamprism/lampray/content/review/persistence/ReviewTaskRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023-2025 RollW + * Copyright (C) 2023-2026 RollW * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,14 +26,14 @@ import tech.lamprism.lampray.content.review.ReviewTaskStatus */ @Repository class ReviewTaskRepository( - private val reviewTaskDao: ReviewTaskDao + reviewTaskDao: ReviewTaskDao ) : CommonRepository(reviewTaskDao) { override fun save(entity: S): S { - return reviewTaskDao.saveAndFlush(entity) + return saveAndFlush(entity) } override fun saveAll(entities: Iterable): List { - return reviewTaskDao.saveAllAndFlush(entities) + return saveAllAndFlush(entities) } fun findByJobId(jobId: String): List { diff --git a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/RegisterVerificationToken.java b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/RegisterVerificationToken.java index 215f7e7e..30f6d1e5 100644 --- a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/RegisterVerificationToken.java +++ b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/RegisterVerificationToken.java @@ -32,12 +32,11 @@ */ public record RegisterVerificationToken( Long id, - String resourceId, String token, long userId, long expiryTime, boolean used -) implements VerifiableToken, DataEntity { +) implements VerifiableToken, DataEntity { public boolean isExpired() { return System.currentTimeMillis() > expiryTime; @@ -60,8 +59,8 @@ public Date toDate() { } @Override - public String getEntityId() { - return resourceId; + public Long getEntityId() { + return id; } @NonNull @@ -104,9 +103,8 @@ public boolean isUsable() { return !used && !isExpired(); } - public static final class Builder implements EntityBuilder { + public static final class Builder implements EntityBuilder { private Long id; - private String resourceId; private String token; private long userId; private long expiryTime; @@ -117,7 +115,6 @@ public Builder() { public Builder(RegisterVerificationToken source) { this.id = source.id; - this.resourceId = source.resourceId; this.token = source.token; this.userId = source.userId; this.expiryTime = source.expiryTime; @@ -125,8 +122,8 @@ public Builder(RegisterVerificationToken source) { } @Override - public Builder setEntityId(String id) { - return setResourceId(id); + public Builder setEntityId(Long id) { + return setId(id); } public Builder setId(Long id) { @@ -134,11 +131,6 @@ public Builder setId(Long id) { return this; } - public Builder setResourceId(String resourceId) { - this.resourceId = resourceId; - return this; - } - public Builder setToken(String token) { this.token = token; return this; @@ -161,7 +153,7 @@ public Builder setUsed(boolean used) { @Override public RegisterVerificationToken build() { - return new RegisterVerificationToken(id, resourceId, token, userId, expiryTime, used); + return new RegisterVerificationToken(id, token, userId, expiryTime, used); } } } diff --git a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenDao.kt b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenDao.kt index b25488b9..0959c040 100644 --- a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenDao.kt +++ b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenDao.kt @@ -23,5 +23,5 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface RegisterTokenDao : CommonDao { +interface RegisterTokenDao : CommonDao { } diff --git a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenEntity.kt b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenEntity.kt index d1b998de..2f7254b6 100644 --- a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenEntity.kt +++ b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenEntity.kt @@ -18,11 +18,11 @@ package tech.lamprism.lampray.security.authentication.registration.repository import jakarta.persistence.Column import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Table import jakarta.persistence.UniqueConstraint -import org.hibernate.annotations.Generated -import org.hibernate.generator.EventType import tech.lamprism.lampray.DataEntity import tech.lamprism.lampray.TimeAttributed import tech.lamprism.lampray.security.authentication.VerifiableToken @@ -42,14 +42,11 @@ import java.time.OffsetDateTime ] ) class RegisterTokenEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false, insertable = false, updatable = false) - @Generated(event = [EventType.INSERT]) private var id: Long? = null, - @Id - @Column(name = "resource_id", nullable = false, length = 64, unique = true) - private var resourceId: String, - @Column(name = "token") var token: String = "", @@ -61,7 +58,7 @@ class RegisterTokenEntity( @Column(name = "used") var used: Boolean = false -) : VerifiableToken, DataEntity { +) : VerifiableToken, DataEntity { override fun token(): String { return token } @@ -80,8 +77,8 @@ class RegisterTokenEntity( fun getId(): Long? = id - override fun getEntityId(): String { - return resourceId + override fun getEntityId(): Long { + return id!! } override fun getCreateTime(): OffsetDateTime = TimeAttributed.NONE_TIME @@ -92,7 +89,7 @@ class RegisterTokenEntity( RegisterTokenResourceKind fun lock(): RegisterVerificationToken = RegisterVerificationToken( - id, resourceId, token, userId, expiryTime, used + id, token, userId, expiryTime, used ) fun toBuilder(): Builder { @@ -108,16 +105,6 @@ class RegisterTokenEntity( } companion object { - @JvmStatic - fun RegisterVerificationToken.toEntity(): RegisterTokenEntity = RegisterTokenEntity( - id = id, - resourceId = entityId, - token = token, - userId = userId, - expiryTime = expiryTime, - used = used - ) - @JvmStatic fun builder(): Builder { return Builder() @@ -126,7 +113,6 @@ class RegisterTokenEntity( class Builder { private var id: Long? = null - private var resourceId: String? = null private var token: String = "" private var userId: Long = 0 private var expiryTime: Long = 0 @@ -136,7 +122,6 @@ class RegisterTokenEntity( internal constructor(registerTokenEntity: RegisterTokenEntity) { id = registerTokenEntity.id - resourceId = registerTokenEntity.resourceId token = registerTokenEntity.token userId = registerTokenEntity.userId expiryTime = registerTokenEntity.expiryTime @@ -148,11 +133,6 @@ class RegisterTokenEntity( return this } - fun setResourceId(resourceId: String): Builder { - this.resourceId = resourceId - return this - } - fun setToken(token: String): Builder { this.token = token return this @@ -176,7 +156,6 @@ class RegisterTokenEntity( fun build(): RegisterTokenEntity { return RegisterTokenEntity( id = id, - resourceId = resourceId!!, token = token, userId = userId, expiryTime = expiryTime, diff --git a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenRepository.kt b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenRepository.kt index 560498d8..eef4e636 100644 --- a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenRepository.kt +++ b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/repository/RegisterTokenRepository.kt @@ -25,15 +25,11 @@ import java.util.Optional */ @Repository class RegisterTokenRepository( - private val registerTokenDao: RegisterTokenDao -) : CommonRepository(registerTokenDao) { - override fun save(entity: S): S { - return registerTokenDao.saveAndFlush(entity) - } - + registerTokenDao: RegisterTokenDao +) : CommonRepository(registerTokenDao) { fun findByToken(token: String): Optional { return findOne { root, _, cb -> - cb.equal(root.get("token"), token) + cb.equal(root.get(RegisterTokenEntity_.token), token) } } } diff --git a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/service/LoginRegisterService.java b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/service/LoginRegisterService.java index 03abd67d..df3e9428 100644 --- a/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/service/LoginRegisterService.java +++ b/lampray-iam/authentication-service/src/main/java/tech/lamprism/lampray/security/authentication/registration/service/LoginRegisterService.java @@ -28,7 +28,6 @@ import space.lingu.NonNull; import tech.lamprism.lampray.LampException; import tech.lamprism.lampray.RequestMetadata; -import tech.lamprism.lampray.common.data.ResourceIdGenerator; import tech.lamprism.lampray.security.authentication.UserInfoSignature; import tech.lamprism.lampray.security.authentication.VerifiableToken; import tech.lamprism.lampray.security.authentication.adapter.PreUserAuthenticationToken; @@ -40,7 +39,6 @@ import tech.lamprism.lampray.security.authentication.registration.OnUserRegistrationEvent; import tech.lamprism.lampray.security.authentication.registration.RegisterProvider; import tech.lamprism.lampray.security.authentication.registration.RegisterTokenProvider; -import tech.lamprism.lampray.security.authentication.registration.RegisterTokenResourceKind; import tech.lamprism.lampray.security.authentication.registration.RegisterVerificationToken; import tech.lamprism.lampray.security.authentication.registration.Registration; import tech.lamprism.lampray.security.authentication.registration.RegistrationInterceptor; @@ -80,7 +78,6 @@ public class LoginRegisterService implements LoginProvider, RegisterTokenProvide private final RegisterTokenRepository registerTokenRepository; private final FirewallRegistry firewallRegistry; private final SystemResourceOperatorProvider systemResourceOperatorProvider; - private final ResourceIdGenerator resourceIdGenerator; private final UserProvider userProvider; private final UserManageService userManageService; private final ApplicationEventPublisher eventPublisher; @@ -94,7 +91,6 @@ public LoginRegisterService(List strategies, RegisterTokenRepository registerTokenRepository, FirewallRegistry firewallRegistry, SystemResourceOperatorProvider systemResourceOperatorProvider, - ResourceIdGenerator resourceIdGenerator, UserProvider userProvider, UserManageService userManageService, ApplicationEventPublisher eventPublisher, @@ -104,7 +100,6 @@ public LoginRegisterService(List strategies, this.registerTokenRepository = registerTokenRepository; this.firewallRegistry = firewallRegistry; this.systemResourceOperatorProvider = systemResourceOperatorProvider; - this.resourceIdGenerator = resourceIdGenerator; this.userProvider = userProvider; this.userManageService = userManageService; this.eventPublisher = eventPublisher; @@ -241,8 +236,7 @@ public VerifiableToken createRegisterToken(UserIdentity userIdentity) { UUID uuid = UUID.randomUUID(); String token = uuid.toString(); long expiryTime = RegisterVerificationToken.calculateExpiryDate(); - RegisterTokenEntity registerVerificationToken = RegisterTokenEntity.Companion.builder() - .setResourceId(resourceIdGenerator.nextId(RegisterTokenResourceKind.INSTANCE)) + RegisterTokenEntity registerVerificationToken = RegisterTokenEntity.builder() .setToken(token) .setUserId(userIdentity.getUserId()) .setExpiryTime(expiryTime) diff --git a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/LocalizedMessage.kt b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/LocalizedMessage.kt index 95c112cc..d3ba7f35 100644 --- a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/LocalizedMessage.kt +++ b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/LocalizedMessage.kt @@ -15,11 +15,9 @@ */ package tech.lamprism.lampray.system.resource -import space.lingu.NonNull import tech.lamprism.lampray.DataEntity import tech.lamprism.lampray.EntityBuilder import tech.lamprism.lampray.TimeAttributed -import tech.rollw.common.web.system.SystemResourceKind import java.time.OffsetDateTime import java.util.Locale @@ -33,13 +31,12 @@ import java.util.Locale */ data class LocalizedMessage( private val id: Long?, - private val resourceId: String, override val key: String, override val value: String, override val locale: Locale, private val updateTime: OffsetDateTime -) : LocalizedMessageResource, DataEntity { - override fun getEntityId(): String = resourceId +) : LocalizedMessageResource, DataEntity { + override fun getEntityId(): Long = id!! fun getId(): Long? = id @@ -47,16 +44,12 @@ data class LocalizedMessage( override fun getUpdateTime(): OffsetDateTime = updateTime - @NonNull - override fun getSystemResourceKind(): SystemResourceKind { - return LocalizedMessageResourceKind - } + override fun getSystemResourceKind() = LocalizedMessageResourceKind fun toBuilder() = Builder(this) - class Builder : EntityBuilder { + class Builder : EntityBuilder { private var id: Long? = null - private var resourceId: String? = null private var key: String? = null private var value: String? = null private var locale: Locale? = null @@ -66,7 +59,6 @@ data class LocalizedMessage( constructor(localizedMessage: LocalizedMessage) { this.id = localizedMessage.id - this.resourceId = localizedMessage.resourceId this.key = localizedMessage.key this.value = localizedMessage.value this.locale = localizedMessage.locale @@ -77,12 +69,8 @@ data class LocalizedMessage( this.id = id } - override fun setEntityId(id: String) = apply { - this.resourceId = id - } - - fun setResourceId(resourceId: String) = apply { - this.resourceId = resourceId + override fun setEntityId(id: Long) = apply { + this.id = id } fun setKey(key: String) = apply { @@ -102,7 +90,7 @@ data class LocalizedMessage( } override fun build(): LocalizedMessage = - LocalizedMessage(id, resourceId!!, key!!, value!!, locale!!, updateTime!!) + LocalizedMessage(id, key!!, value!!, locale!!, updateTime!!) } companion object { @@ -116,7 +104,6 @@ data class LocalizedMessage( ): LocalizedMessage { val now = OffsetDateTime.now() return builder() - .setResourceId("") .setKey(key) .setValue(value) .setLocale(locale) diff --git a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageDao.kt b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageDao.kt index 1c27617b..33569421 100644 --- a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageDao.kt +++ b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageDao.kt @@ -23,5 +23,4 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface LocalizedMessageDao : CommonDao { -} +interface LocalizedMessageDao : CommonDao diff --git a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageEntity.kt b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageEntity.kt index 8cdec0d3..872f15df 100644 --- a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageEntity.kt +++ b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageEntity.kt @@ -19,14 +19,14 @@ package tech.lamprism.lampray.system.resource.data import jakarta.persistence.Column import jakarta.persistence.Convert import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType import jakarta.persistence.Id import jakarta.persistence.Lob import jakarta.persistence.Table import jakarta.persistence.Temporal import jakarta.persistence.TemporalType import jakarta.persistence.UniqueConstraint -import org.hibernate.annotations.Generated -import org.hibernate.generator.EventType import tech.lamprism.lampray.DataEntity import tech.lamprism.lampray.TimeAttributed import tech.lamprism.lampray.common.data.LocaleAttributeConverter @@ -47,13 +47,10 @@ import java.util.Locale ] ) class LocalizedMessageEntity( - @Column(name = "id", nullable = false, insertable = false, updatable = false) - @Generated(event = [EventType.INSERT]) - private var id: Long? = null, - @Id - @Column(name = "resource_id", nullable = false, length = 64, unique = true) - private var resourceId: String = "", + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, @Column(name = "key", nullable = false, length = 255) override var key: String = "", @@ -69,20 +66,11 @@ class LocalizedMessageEntity( @Temporal(TemporalType.TIMESTAMP) @Column(name = "update_time", nullable = false) private var updateTime: OffsetDateTime = OffsetDateTime.now() -) : LocalizedMessageResource, DataEntity { - override fun getEntityId(): String { - return resourceId +) : LocalizedMessageResource, DataEntity { + override fun getEntityId(): Long? { + return id } - fun getId(): Long? = id - - fun setId(id: Long?) { - this.id = id - } - - fun setResourceId(resourceId: String) { - this.resourceId = resourceId - } override fun getCreateTime(): OffsetDateTime = TimeAttributed.NONE_TIME @@ -97,12 +85,12 @@ class LocalizedMessageEntity( } fun lock(): LocalizedMessage { - return LocalizedMessage(id, resourceId, key, value, locale, updateTime) + return LocalizedMessage(id, key, value, locale, updateTime) } companion object { fun LocalizedMessage.toEntity(): LocalizedMessageEntity { - return LocalizedMessageEntity(getId(), entityId, key, value, locale, updateTime) + return LocalizedMessageEntity(entityId, key, value, locale, updateTime) } } } diff --git a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageRepository.kt b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageRepository.kt index 4bbce313..b0450601 100644 --- a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageRepository.kt +++ b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageRepository.kt @@ -27,17 +27,14 @@ import java.util.Locale */ @Repository class LocalizedMessageRepository( - private val localizedMessageDao: LocalizedMessageDao -) : CommonRepository(localizedMessageDao) { - override fun save(entity: S): S { - return localizedMessageDao.saveAndFlush(entity) - } + localizedMessageDao: LocalizedMessageDao +) : CommonRepository(localizedMessageDao) { fun findByKey(key: String, locale: Locale) = findOne { root, _, criteriaBuilder -> criteriaBuilder.and( - criteriaBuilder.equal(root.get("key"), key), - criteriaBuilder.equal(root.get("locale"), locale) + criteriaBuilder.equal(root.get(LocalizedMessageEntity_.key), key), + criteriaBuilder.equal(root.get(LocalizedMessageEntity_.locale), locale) ) } @@ -46,13 +43,13 @@ class LocalizedMessageRepository( pageable: Pageable = Pageable.unpaged() ): Page = findAll(pageable) { root, _, criteriaBuilder -> - criteriaBuilder.equal(root.get("key"), key) + criteriaBuilder.equal(root.get(LocalizedMessageEntity_.key), key) } fun deleteByKey(key: String) { delete { root, _, criteriaBuilder -> criteriaBuilder.and( - criteriaBuilder.equal(root.get("key"), key) + criteriaBuilder.equal(root.get(LocalizedMessageEntity_.key), key) ) } } @@ -60,8 +57,8 @@ class LocalizedMessageRepository( fun deleteByKey(key: String, locale: Locale) { delete { root, _, criteriaBuilder -> criteriaBuilder.and( - criteriaBuilder.equal(root.get("key"), key), - criteriaBuilder.equal(root.get("locale"), locale) + criteriaBuilder.equal(root.get(LocalizedMessageEntity_.key), key), + criteriaBuilder.equal(root.get(LocalizedMessageEntity_.locale), locale) ) } } diff --git a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/service/LocalizedMessageResourceService.kt b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/service/LocalizedMessageResourceService.kt index 24b155ae..f4f54b5d 100644 --- a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/service/LocalizedMessageResourceService.kt +++ b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/service/LocalizedMessageResourceService.kt @@ -19,7 +19,6 @@ package tech.lamprism.lampray.system.resource.service import org.springframework.stereotype.Service import tech.lamprism.lampray.common.data.ResourceIdGenerator import tech.lamprism.lampray.system.resource.LocalizedMessageResource -import tech.lamprism.lampray.system.resource.LocalizedMessageResourceKind import tech.lamprism.lampray.system.resource.LocalizedMessageResourceProvider import tech.lamprism.lampray.system.resource.SimpleLocalizedMessageResource import tech.lamprism.lampray.system.resource.data.LocalizedMessageEntity @@ -51,12 +50,11 @@ class LocalizedMessageResourceService( ) { val localizedMessageEntity = localizedMessageRepository .findByKey(key, locale).orElse(null) ?: LocalizedMessageEntity().apply { - setResourceId(resourceIdGenerator.nextId(LocalizedMessageResourceKind)) this.key = key this.value = value this.locale = locale } - if (localizedMessageEntity.getId() != null && + if (localizedMessageEntity.id != null && localizedMessageEntity.value == value ) { return diff --git a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/SystemSetting.java b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/SystemSetting.java index 02e10522..5b6bf6e8 100644 --- a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/SystemSetting.java +++ b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/SystemSetting.java @@ -30,13 +30,11 @@ @SuppressWarnings("all") public class SystemSetting implements DataEntity { private final Long id; - private final String resourceId; private final String key; private final String value; - public SystemSetting(Long id, String resourceId, String key, String value) { + public SystemSetting(Long id, String key, String value) { this.id = id; - this.resourceId = resourceId; this.key = key; this.value = value; } @@ -55,7 +53,7 @@ public Long getId() { @Override public String getEntityId() { - return resourceId; + return key; } @NonNull @@ -103,7 +101,6 @@ public String toString() { public static final class Builder implements EntityBuilder { private Long id; - private String resourceId; private String key; private String value; @@ -112,15 +109,10 @@ public Builder() { public Builder(SystemSetting systemSetting) { this.id = systemSetting.id; - this.resourceId = systemSetting.resourceId; this.key = systemSetting.getKey(); this.value = systemSetting.getValue(); } - public Builder setResourceId(String resourceId) { - this.resourceId = resourceId; - return this; - } public Builder setKey(String key) { this.key = key; @@ -134,7 +126,7 @@ public Builder setValue(String value) { @Override public SystemSetting build() { - return new SystemSetting(id, resourceId, key, value); + return new SystemSetting(id, key, value); } public Builder setId(Long id) { @@ -144,7 +136,7 @@ public Builder setId(Long id) { @Override public Builder setEntityId(String id) { - this.resourceId = id; + this.key = id; return this; } } diff --git a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingDao.kt b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingDao.kt index 175f6006..daaec285 100644 --- a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingDao.kt +++ b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingDao.kt @@ -23,5 +23,4 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface SystemSettingDao : CommonDao { -} +interface SystemSettingDao : CommonDao diff --git a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingEntity.kt b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingEntity.kt index 9cec2a94..d4124a45 100644 --- a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingEntity.kt +++ b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingEntity.kt @@ -40,13 +40,10 @@ import java.time.OffsetDateTime class SystemSettingEntity( @Column(name = "id", nullable = false, insertable = false, updatable = false) @Generated(event = [EventType.INSERT]) - private var id: Long? = null, + var id: Long? = null, @Id - @Column(name = "resource_id", nullable = false, length = 64, unique = true) - private var resourceId: String = "", - - @Column(name = "key") + @Column(name = "key", nullable = false, unique = true, length = 255) var key: String = "", @Column(name = "value") @@ -55,13 +52,7 @@ class SystemSettingEntity( @Column(name = "update_time", nullable = false) private var updateTime: OffsetDateTime = OffsetDateTime.now() ) : DataEntity { - override fun getEntityId(): String = resourceId - - fun getId(): Long? = id - - fun setResourceId(resourceId: String) { - this.resourceId = resourceId - } + override fun getEntityId(): String = key fun setUpdateTime(updateTime: OffsetDateTime) { this.updateTime = updateTime @@ -74,13 +65,12 @@ class SystemSettingEntity( override fun getSystemResourceKind(): SystemResourceKind = SystemSettingResourceKind - fun lock(): SystemSetting = SystemSetting(id, resourceId, key, value) + fun lock(): SystemSetting = SystemSetting(id, key, value) companion object { @JvmStatic fun SystemSetting.toEntity() = SystemSettingEntity( - id = getId(), - resourceId = entityId, + id = id, key = key, value = value ) diff --git a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingRepository.kt b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingRepository.kt index 1468c54d..89e1a9b9 100644 --- a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingRepository.kt +++ b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/data/SystemSettingRepository.kt @@ -18,31 +18,15 @@ package tech.lamprism.lampray.setting.data import org.springframework.stereotype.Repository import tech.lamprism.lampray.common.data.CommonRepository -import java.util.Optional /** * @author RollW */ @Repository class SystemSettingRepository( - private val systemSettingDao: SystemSettingDao + systemSettingDao: SystemSettingDao ) : CommonRepository(systemSettingDao) { override fun save(entity: S): S { - return systemSettingDao.saveAndFlush(entity) - } - - fun findByKey(key: String): Optional { - return findOne { root, query, criteriaBuilder -> - criteriaBuilder.equal(root.get("key"), key) - } - } - - fun findByKeyIn(keys: Set): List { - if (keys.isEmpty()) { - return emptyList() - } - return findAll { root, query, criteriaBuilder -> - root.get("key").`in`(keys) - } + return saveAndFlush(entity) } } diff --git a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/service/SystemSettingConfigProvider.kt b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/service/SystemSettingConfigProvider.kt index 152d9747..fec040a2 100644 --- a/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/service/SystemSettingConfigProvider.kt +++ b/lampray-system/setting-service/src/main/java/tech/lamprism/lampray/setting/service/SystemSettingConfigProvider.kt @@ -17,7 +17,6 @@ package tech.lamprism.lampray.setting.service import org.springframework.stereotype.Service -import tech.lamprism.lampray.common.data.ResourceIdGenerator import tech.lamprism.lampray.setting.ConfigPath import tech.lamprism.lampray.setting.ConfigProvider import tech.lamprism.lampray.setting.ConfigReader @@ -29,7 +28,6 @@ import tech.lamprism.lampray.setting.SettingSpecification.Companion.keyName import tech.lamprism.lampray.setting.SettingSpecificationHelper import tech.lamprism.lampray.setting.SettingSpecificationProvider import tech.lamprism.lampray.setting.SnapshotConfigValue -import tech.lamprism.lampray.setting.SystemSettingResourceKind import tech.lamprism.lampray.setting.data.SystemSettingEntity import tech.lamprism.lampray.setting.data.SystemSettingRepository @@ -40,7 +38,6 @@ import tech.lamprism.lampray.setting.data.SystemSettingRepository class SystemSettingConfigProvider( private val systemSettingRepository: SystemSettingRepository, private val settingSpecificationProvider: SettingSpecificationProvider, - private val resourceIdGenerator: ResourceIdGenerator ) : ConfigProvider { override val metadata: ConfigReader.Metadata = @@ -50,7 +47,7 @@ class SystemSettingConfigProvider( ) override fun get(key: String): String? { - return systemSettingRepository.findByKey(key) + return systemSettingRepository.findById(key) .orElse(null)?.value } @@ -58,7 +55,7 @@ class SystemSettingConfigProvider( get(key) ?: defaultValue override fun get(specification: SettingSpecification): T? { - val setting = systemSettingRepository.findByKey(specification.keyName) + val setting = systemSettingRepository.findById(specification.keyName) .orElse(null) ?: return null return with(SettingSpecificationHelper) { setting.value.deserialize(specification) @@ -71,7 +68,7 @@ class SystemSettingConfigProvider( ): T = get(specification) ?: defaultValue override fun getValue(specification: SettingSpecification): ConfigValue { - val setting = systemSettingRepository.findByKey(specification.keyName) + val setting = systemSettingRepository.findById(specification.keyName) .orElse(null) ?: return SnapshotConfigValue(null, SettingSource.DATABASE, specification) return with(SettingSpecificationHelper) { SnapshotConfigValue( @@ -87,7 +84,7 @@ class SystemSettingConfigProvider( return emptyList() } val keys = specifications.map { it.keyName }.toSet() - val settings = systemSettingRepository.findByKeyIn(keys) + val settings = systemSettingRepository.findAllById(keys) .associateBy { it.key } @Suppress("UNCHECKED_CAST") return (specifications as List>).map { spec -> @@ -105,7 +102,7 @@ class SystemSettingConfigProvider( } override fun set(key: String, value: String?): SettingSource { - val setting = systemSettingRepository.findByKey(key) + val setting = systemSettingRepository.findById(key) .orElse(null) if (setting != null) { setting.value = value @@ -113,7 +110,6 @@ class SystemSettingConfigProvider( return SettingSource.DATABASE } val newSetting = SystemSettingEntity( - resourceId = resourceIdGenerator.nextId(SystemSettingResourceKind), key = key, value = value ) @@ -122,7 +118,7 @@ class SystemSettingConfigProvider( } override fun set(spec: SettingSpecification, value: T?): SettingSource { - val setting = systemSettingRepository.findByKey(spec.keyName) + val setting = systemSettingRepository.findById(spec.keyName) .orElse(null) val value = with(SettingSpecificationHelper) { value.serialize(spec) @@ -133,7 +129,6 @@ class SystemSettingConfigProvider( return SettingSource.DATABASE } val newSetting = SystemSettingEntity( - resourceId = resourceIdGenerator.nextId(SystemSettingResourceKind), key = spec.key.name, value = value ) @@ -142,7 +137,7 @@ class SystemSettingConfigProvider( } override fun reset(spec: SettingSpecification): SettingSource { - val setting = systemSettingRepository.findByKey(spec.keyName) + val setting = systemSettingRepository.findById(spec.keyName) .orElse(null) ?: return SettingSource.NONE systemSettingRepository.delete(setting) return SettingSource.DATABASE diff --git a/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffRepository.kt b/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffRepository.kt index 14298228..1366d06e 100644 --- a/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffRepository.kt +++ b/lampray-user/staff-service/src/main/java/tech/lamprism/lampray/staff/persistence/StaffRepository.kt @@ -26,11 +26,11 @@ import java.util.Optional */ @Repository class StaffRepository( - private val staffDao: StaffDao + staffDao: StaffDao ) : CommonRepository(staffDao) { fun findByUserId(userId: Long): Optional { return findOne { root, _, criteriaBuilder -> - criteriaBuilder.equal(root.get("userId"), userId) + criteriaBuilder.equal(root.get(StaffEntity_.userId), userId) } } @@ -38,7 +38,7 @@ class StaffRepository( return findAll { root, _, criteriaBuilder -> criteriaBuilder.and( *types.map { - criteriaBuilder.isMember(it, root.get>("types")) + criteriaBuilder.isMember(it, root.get(StaffEntity_.types)) }.toTypedArray() ) } diff --git a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataDao.kt b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataDao.kt index 985d7c17..1a1fb443 100644 --- a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataDao.kt +++ b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataDao.kt @@ -23,8 +23,4 @@ import tech.lamprism.lampray.common.data.Dao * @author RollW */ @Dao -interface UserPersonalDataDao : CommonDao { - fun findByUserId(userId: Long): UserPersonalDataEntity? - - fun findAllByUserIdIn(userIds: Iterable): List -} +interface UserPersonalDataDao : CommonDao diff --git a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataRepository.kt b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataRepository.kt index 1c63e940..fc7f7fbd 100644 --- a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataRepository.kt +++ b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/persistence/UserPersonalDataRepository.kt @@ -24,17 +24,6 @@ import tech.lamprism.lampray.common.data.CommonRepository */ @Repository class UserPersonalDataRepository( - private val userPersonalDataDao: UserPersonalDataDao + userPersonalDataDao: UserPersonalDataDao ) : CommonRepository(userPersonalDataDao) { - override fun save(entity: S): S { - return userPersonalDataDao.saveAndFlush(entity) - } - - fun findByUserId(userId: Long): UserPersonalDataEntity? { - return userPersonalDataDao.findByUserId(userId) - } - - fun findAllByUserIdIn(userIds: Iterable): List { - return userPersonalDataDao.findAllByUserIdIn(userIds) - } } diff --git a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/service/UserPersonalDataServiceImpl.java b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/service/UserPersonalDataServiceImpl.java index e38e80da..100de6ea 100644 --- a/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/service/UserPersonalDataServiceImpl.java +++ b/lampray-user/user-details-service/src/main/java/tech/lamprism/lampray/user/details/service/UserPersonalDataServiceImpl.java @@ -47,7 +47,7 @@ public UserPersonalDataServiceImpl(UserProvider userProvider, @Override public UserPersonalData getPersonalData(long userId) { - UserPersonalDataEntity data = userPersonalDataRepository.findByUserId(userId); + UserPersonalDataEntity data = userPersonalDataRepository.findById(userId).orElse(null); if (data == null) { AttributedUser user = userProvider.getUser(userId); return UserPersonalData.defaultOf(user); @@ -62,8 +62,7 @@ public UserPersonalData getPersonalData(long userId) { @Override public UserPersonalData getPersonalData(UserIdentity userIdentity) { - UserPersonalDataEntity data = userPersonalDataRepository.findByUserId( - userIdentity.getUserId()); + UserPersonalDataEntity data = userPersonalDataRepository.findById(userIdentity.getUserId()).orElse(null); if (data == null) { return UserPersonalData.defaultOf(userIdentity); } @@ -95,7 +94,7 @@ public List getPersonalData(List userI @Override public List getPersonalDataByIds(List ids) { - return userPersonalDataRepository.findAllByUserIdIn(ids) + return userPersonalDataRepository.findAllById(ids) .stream() .map(UserPersonalDataEntity::lock) .toList(); @@ -112,7 +111,7 @@ public void updatePersonalData(UserTrait user, UserDataField... fields) { if (fields.length == 0) { return; } - UserPersonalDataEntity exist = userPersonalDataRepository.findByUserId(user.getUserId()); + UserPersonalDataEntity exist = userPersonalDataRepository.findById(user.getUserId()).orElse(null); UserPersonalDataEntity.Builder builder = toBuilder(exist, user.getUserId()); for (UserDataField field : fields) { Utils.setBuilderValue(builder, field); diff --git a/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserRepository.kt b/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserRepository.kt index 67e5cc3b..a11eddf0 100644 --- a/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserRepository.kt +++ b/lampray-user/user-service/src/main/java/tech/lamprism/lampray/user/repository/UserRepository.kt @@ -26,22 +26,22 @@ import java.util.Optional */ @Repository class UserRepository( - private val userDao: UserDao + userDao: UserDao ) : CommonRepository(userDao) { fun searchBy(keyword: String): List { - return userDao.findAll(createSearchBySpec(keyword)) + return findAll(createSearchBySpec(keyword)) } fun findByUsername(username: String): Optional { return findOne { root, _, criteriaBuilder -> - criteriaBuilder.equal(root.get("username"), username) + criteriaBuilder.equal(root.get(UserEntity_.username), username) } } fun findByEmail(email: String): Optional { return findOne { root, _, criteriaBuilder -> - criteriaBuilder.equal(root.get("email"), email) + criteriaBuilder.equal(root.get(UserEntity_.email), email) } } @@ -54,11 +54,11 @@ class UserRepository( } fun hasUsers(): Boolean { - return userDao.count() > 0 + return count() > 0 } private fun createSearchBySpec(keyword: String): Specification = Specification { root, _, builder -> - builder.like(root.get("username"), keyword) + builder.like(root.get(UserEntity_.username), keyword) } } diff --git a/lampray-web/src/main/resources/db/baseline/baseline-0.1.0-alpha1.yaml b/lampray-web/src/main/resources/db/baseline/baseline-0.1.0-alpha1.yaml index c29251d5..be04f013 100644 --- a/lampray-web/src/main/resources/db/baseline/baseline-0.1.0-alpha1.yaml +++ b/lampray-web/src/main/resources/db/baseline/baseline-0.1.0-alpha1.yaml @@ -135,12 +135,6 @@ databaseChangeLog: primaryKeyName: pk_content_metadata name: id type: BIGINT - - column: - constraints: - nullable: false - unique: true - name: resource_id - type: VARCHAR(64) - column: constraints: nullable: false @@ -291,12 +285,6 @@ databaseChangeLog: primaryKeyName: pk_localized_message name: id type: BIGINT - - column: - constraints: - nullable: false - unique: true - name: resource_id - type: VARCHAR(64) - column: constraints: nullable: false @@ -328,12 +316,6 @@ databaseChangeLog: primaryKeyName: pk_register_verification_token name: id type: BIGINT - - column: - constraints: - nullable: false - unique: true - name: resource_id - type: VARCHAR(64) - column: name: token type: VARCHAR(255) @@ -499,10 +481,6 @@ databaseChangeLog: - column: constraints: nullable: false - unique: true - name: resource_id - type: VARCHAR(64) - - column: name: key type: VARCHAR(255) - column: diff --git a/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml b/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml index 19c6531a..72f89f2d 100644 --- a/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml +++ b/lampray-web/src/main/resources/db/changelog/0.1.0/0.1.0-alpha1-02-resource-id-migration.yaml @@ -63,32 +63,6 @@ databaseChangeLog: columnNames: resource_id constraintName: uc_comment_resource_id - - addColumn: - tableName: content_metadata - columns: - - column: - name: resource_id - type: VARCHAR(64) - - update: - tableName: content_metadata - columns: - - column: - name: resource_id - valueComputed: id - where: resource_id IS NULL - - modifyDataType: - tableName: content_metadata - columnName: content_id - newDataType: VARCHAR(64) - - addNotNullConstraint: - tableName: content_metadata - columnName: resource_id - columnDataType: VARCHAR(64) - - addUniqueConstraint: - tableName: content_metadata - columnNames: resource_id - constraintName: uc_content_metadata_resource_id - - addColumn: tableName: favorite_group columns: @@ -141,73 +115,7 @@ databaseChangeLog: columnNames: resource_id constraintName: uc_favorite_item_resource_id - - addColumn: - tableName: localized_message - columns: - - column: - name: resource_id - type: VARCHAR(64) - - update: - tableName: localized_message - columns: - - column: - name: resource_id - valueComputed: id - where: resource_id IS NULL - - addNotNullConstraint: - tableName: localized_message - columnName: resource_id - columnDataType: VARCHAR(64) - - addUniqueConstraint: - tableName: localized_message - columnNames: resource_id - constraintName: uc_localized_message_resource_id - - - addColumn: - tableName: register_verification_token - columns: - - column: - name: resource_id - type: VARCHAR(64) - - update: - tableName: register_verification_token - columns: - - column: - name: resource_id - valueComputed: id - where: resource_id IS NULL - - addNotNullConstraint: - tableName: register_verification_token - columnName: resource_id - columnDataType: VARCHAR(64) - - addUniqueConstraint: - tableName: register_verification_token - columnNames: resource_id - constraintName: uc_register_verification_token_resource_id - - modifyDataType: tableName: review_job columnName: content_id newDataType: VARCHAR(64) - - - addColumn: - tableName: system_setting - columns: - - column: - name: resource_id - type: VARCHAR(64) - - update: - tableName: system_setting - columns: - - column: - name: resource_id - valueComputed: id - where: resource_id IS NULL - - addNotNullConstraint: - tableName: system_setting - columnName: resource_id - columnDataType: VARCHAR(64) - - addUniqueConstraint: - tableName: system_setting - columnNames: resource_id - constraintName: uc_system_setting_resource_id From 4cc7d9cda9bcf72db06b139f2181809c50411182 Mon Sep 17 00:00:00 2001 From: rollw <73376341+roll-w@users.noreply.github.com> Date: Sun, 5 Apr 2026 22:46:04 +0800 Subject: [PATCH 6/6] chore: polish code --- .../lampray/content/article/service/ArticleService.java | 2 +- .../lampray/system/resource/data/LocalizedMessageEntity.kt | 1 - .../resource/service/LocalizedMessageResourceService.kt | 4 +--- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/service/ArticleService.java b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/service/ArticleService.java index 5c9ac66d..cdaf053c 100644 --- a/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/service/ArticleService.java +++ b/lampray-content/article-service/src/main/java/tech/lamprism/lampray/content/article/service/ArticleService.java @@ -64,7 +64,7 @@ public ContentDetails publish(@NonNull UncreatedContent uncreatedContent, throw new IllegalArgumentException("Content type not supported: " + uncreatedContent.getContentType()); } - String title = Validate.notEmpty(Objects.requireNonNull(uncreatedContent.getTitle())); + String title = Validate.notEmpty(uncreatedContent.getTitle()); StructuralText content = Objects.requireNonNull(uncreatedContent.getContent()); long userId = uncreatedContent.getOperator().getUserId(); diff --git a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageEntity.kt b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageEntity.kt index 872f15df..a9697f72 100644 --- a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageEntity.kt +++ b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/data/LocalizedMessageEntity.kt @@ -71,7 +71,6 @@ class LocalizedMessageEntity( return id } - override fun getCreateTime(): OffsetDateTime = TimeAttributed.NONE_TIME override fun getUpdateTime(): OffsetDateTime = updateTime diff --git a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/service/LocalizedMessageResourceService.kt b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/service/LocalizedMessageResourceService.kt index f4f54b5d..8c3069e7 100644 --- a/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/service/LocalizedMessageResourceService.kt +++ b/lampray-system/message-resource-service/src/main/java/tech/lamprism/lampray/system/resource/service/LocalizedMessageResourceService.kt @@ -17,7 +17,6 @@ package tech.lamprism.lampray.system.resource.service import org.springframework.stereotype.Service -import tech.lamprism.lampray.common.data.ResourceIdGenerator import tech.lamprism.lampray.system.resource.LocalizedMessageResource import tech.lamprism.lampray.system.resource.LocalizedMessageResourceProvider import tech.lamprism.lampray.system.resource.SimpleLocalizedMessageResource @@ -32,8 +31,7 @@ import java.util.function.Supplier */ @Service class LocalizedMessageResourceService( - private val localizedMessageRepository: LocalizedMessageRepository, - private val resourceIdGenerator: ResourceIdGenerator + private val localizedMessageRepository: LocalizedMessageRepository ) : LocalizedMessageResourceProvider { private var _fallbackLocale: Locale = Locale.ROOT