-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: 허브 태그 자동 생성 로직을 구현한다.
- Loading branch information
Showing
23 changed files
with
504 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.seong.shoutlink.domain.tag; | ||
|
||
import com.seong.shoutlink.domain.hub.Hub; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
public class HubTag { | ||
|
||
private final Hub hub; | ||
private final Tag tag; | ||
|
||
public HubTag(Hub hub, Tag tag) { | ||
this.hub = hub; | ||
this.tag = tag; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.seong.shoutlink.domain.tag; | ||
|
||
import lombok.Getter; | ||
|
||
@Getter | ||
public class Tag { | ||
|
||
private Long tagId; | ||
private String name; | ||
|
||
public Tag(String name) { | ||
this(null, name); | ||
} | ||
|
||
public Tag(Long tagId, String name) { | ||
this.tagId = tagId; | ||
this.name = name; | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
src/main/java/com/seong/shoutlink/domain/tag/repository/HubTagEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.seong.shoutlink.domain.tag.repository; | ||
|
||
import jakarta.persistence.DiscriminatorValue; | ||
import jakarta.persistence.Entity; | ||
import lombok.AccessLevel; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@Entity | ||
@DiscriminatorValue("hub") | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class HubTagEntity extends TagEntity { | ||
|
||
private Long hubId; | ||
|
||
public HubTagEntity(String name, Long hubId) { | ||
super(name); | ||
this.hubId = hubId; | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
src/main/java/com/seong/shoutlink/domain/tag/repository/TagEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package com.seong.shoutlink.domain.tag.repository; | ||
|
||
import com.seong.shoutlink.domain.common.BaseEntity; | ||
import com.seong.shoutlink.domain.hub.Hub; | ||
import com.seong.shoutlink.domain.tag.HubTag; | ||
import com.seong.shoutlink.domain.tag.Tag; | ||
import jakarta.persistence.DiscriminatorColumn; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.Inheritance; | ||
import jakarta.persistence.InheritanceType; | ||
import jakarta.persistence.Table; | ||
import lombok.AccessLevel; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@Entity | ||
@Table(name = "tag") | ||
@DiscriminatorColumn | ||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public abstract class TagEntity extends BaseEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long tagId; | ||
|
||
private String name; | ||
|
||
protected TagEntity(String name) { | ||
this.name = name; | ||
} | ||
|
||
public static TagEntity from(HubTag hubTag) { | ||
Hub hub = hubTag.getHub(); | ||
Tag tag = hubTag.getTag(); | ||
return new HubTagEntity(tag.getName(), hub.getHubId()); | ||
} | ||
|
||
public Tag toDomain() { | ||
return new Tag(tagId, name); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
src/main/java/com/seong/shoutlink/domain/tag/repository/TagJpaRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.seong.shoutlink.domain.tag.repository; | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.data.jpa.repository.Query; | ||
import org.springframework.data.repository.query.Param; | ||
|
||
public interface TagJpaRepository extends JpaRepository<TagEntity, Long> { | ||
|
||
@Query("delete from HubTagEntity t where t.hubId=:hubId") | ||
long deleteByHubId(@Param("hubId") Long hubId); | ||
} |
31 changes: 31 additions & 0 deletions
31
src/main/java/com/seong/shoutlink/domain/tag/repository/TagRepositoryImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.seong.shoutlink.domain.tag.repository; | ||
|
||
import com.seong.shoutlink.domain.hub.Hub; | ||
import com.seong.shoutlink.domain.tag.HubTag; | ||
import com.seong.shoutlink.domain.tag.Tag; | ||
import com.seong.shoutlink.domain.tag.service.TagRepository; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
@RequiredArgsConstructor | ||
public class TagRepositoryImpl implements TagRepository { | ||
|
||
private final TagJpaRepository tagJpaRepository; | ||
|
||
@Override | ||
public List<Tag> saveAll(List<HubTag> tags) { | ||
List<TagEntity> tagEntities = tags.stream() | ||
.map(TagEntity::from) | ||
.toList(); | ||
return tagJpaRepository.saveAll(tagEntities).stream() | ||
.map(TagEntity::toDomain) | ||
.toList(); | ||
} | ||
|
||
@Override | ||
public long deleteHubTags(Hub hub) { | ||
return tagJpaRepository.deleteByHubId(hub.getHubId()); | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/java/com/seong/shoutlink/domain/tag/service/TagRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.seong.shoutlink.domain.tag.service; | ||
|
||
import com.seong.shoutlink.domain.hub.Hub; | ||
import com.seong.shoutlink.domain.tag.HubTag; | ||
import com.seong.shoutlink.domain.tag.Tag; | ||
import java.util.List; | ||
|
||
public interface TagRepository { | ||
|
||
List<Tag> saveAll(List<HubTag> tags); | ||
|
||
long deleteHubTags(Hub hub); | ||
} |
82 changes: 82 additions & 0 deletions
82
src/main/java/com/seong/shoutlink/domain/tag/service/TagService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package com.seong.shoutlink.domain.tag.service; | ||
|
||
import static java.util.stream.Collectors.*; | ||
|
||
import com.seong.shoutlink.domain.exception.ErrorCode; | ||
import com.seong.shoutlink.domain.exception.ShoutLinkException; | ||
import com.seong.shoutlink.domain.hub.Hub; | ||
import com.seong.shoutlink.domain.hub.service.HubRepository; | ||
import com.seong.shoutlink.domain.link.LinkWithLinkBundle; | ||
import com.seong.shoutlink.domain.link.service.LinkRepository; | ||
import com.seong.shoutlink.domain.linkbundle.LinkBundle; | ||
import com.seong.shoutlink.domain.linkbundle.service.LinkBundleRepository; | ||
import com.seong.shoutlink.domain.link.LinkBundleAndLinks; | ||
import com.seong.shoutlink.domain.tag.HubTag; | ||
import com.seong.shoutlink.domain.tag.Tag; | ||
import com.seong.shoutlink.domain.tag.service.ai.GenerateAutoTagCommand; | ||
import com.seong.shoutlink.domain.tag.service.request.AutoCreateTagCommand; | ||
import com.seong.shoutlink.domain.tag.service.response.CreateTagResponse; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class TagService { | ||
|
||
private static final int MINIMUM_TAG_CONDITION = 5; | ||
private static final int MAXIMUM_TAG_COUNT = 5; | ||
|
||
private final TagRepository tagRepository; | ||
private final HubRepository hubRepository; | ||
private final LinkBundleRepository linkBundleRepository; | ||
private final LinkRepository linkRepository; | ||
private final AutoGenerativeClient autoGenerativeClient; | ||
|
||
@Transactional | ||
public CreateTagResponse autoCreateHubTags(AutoCreateTagCommand command) { | ||
Hub hub = getHub(command.hubId()); | ||
List<LinkBundle> hubLinkBundles = linkBundleRepository.findHubLinkBundles(hub); | ||
List<LinkWithLinkBundle> links = linkRepository.findAllByLinkBundlesIn( | ||
hubLinkBundles); | ||
|
||
List<LinkBundleAndLinks> linkBundlesAndLinks = groupingLinks(links); | ||
int generateTagCount = calculateNumberOfTag(links); | ||
GenerateAutoTagCommand generateAutoTagCommand = GenerateAutoTagCommand | ||
.create(linkBundlesAndLinks, generateTagCount); | ||
|
||
List<HubTag> hubTags = autoGenerativeClient.generateTags(generateAutoTagCommand) | ||
.stream() | ||
.map(generatedTag -> new Tag(generatedTag.name())) | ||
.map(tag -> new HubTag(hub, tag)) | ||
.toList(); | ||
if(!hubTags.isEmpty()) { | ||
tagRepository.deleteHubTags(hub); | ||
} | ||
return CreateTagResponse.from(tagRepository.saveAll(hubTags)); | ||
} | ||
|
||
private List<LinkBundleAndLinks> groupingLinks(List<LinkWithLinkBundle> links) { | ||
return links.stream() | ||
.collect(groupingBy( | ||
LinkWithLinkBundle::getLinkBundle, | ||
mapping(LinkWithLinkBundle::getLink, toList()))) | ||
.entrySet().stream() | ||
.map(entry -> new LinkBundleAndLinks(entry.getKey(), entry.getValue())) | ||
.toList(); | ||
} | ||
|
||
private int calculateNumberOfTag(List<LinkWithLinkBundle> links) { | ||
int totalLinkCount = links.size(); | ||
if (totalLinkCount < MINIMUM_TAG_CONDITION) { | ||
throw new ShoutLinkException("태그 생성 조건을 충족하지 못했습니다.", ErrorCode.NOT_MET_CONDITION); | ||
} | ||
return Math.min(MAXIMUM_TAG_COUNT, totalLinkCount / MINIMUM_TAG_CONDITION); | ||
} | ||
|
||
private Hub getHub(Long hubId) { | ||
return hubRepository.findById(hubId) | ||
.orElseThrow(() -> new ShoutLinkException("존재하지 않는 허브입니다.", ErrorCode.NOT_FOUND)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
src/main/java/com/seong/shoutlink/domain/tag/service/request/AutoCreateTagCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.seong.shoutlink.domain.tag.service.request; | ||
|
||
public record AutoCreateTagCommand(Long hubId) { | ||
|
||
} |
14 changes: 14 additions & 0 deletions
14
src/main/java/com/seong/shoutlink/domain/tag/service/response/CreateTagResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.seong.shoutlink.domain.tag.service.response; | ||
|
||
import com.seong.shoutlink.domain.tag.Tag; | ||
import java.util.List; | ||
|
||
public record CreateTagResponse(List<Long> tagIds) { | ||
|
||
public static CreateTagResponse from(List<Tag> tags) { | ||
List<Long> tagIds = tags.stream() | ||
.map(Tag::getTagId) | ||
.toList(); | ||
return new CreateTagResponse(tagIds); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.