Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.sofa.linkiving.domain.link.controller;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.validation.annotation.Validated;

import com.sofa.linkiving.domain.link.dto.request.LinkCreateReq;
import com.sofa.linkiving.domain.link.dto.request.LinkMemoUpdateReq;
import com.sofa.linkiving.domain.link.dto.request.LinkTitleUpdateReq;
import com.sofa.linkiving.domain.link.dto.request.LinkUpdateReq;
import com.sofa.linkiving.domain.link.dto.request.MetaScrapeReq;
import com.sofa.linkiving.domain.link.dto.response.LinkCardsRes;
import com.sofa.linkiving.domain.link.dto.response.LinkDetailRes;
import com.sofa.linkiving.domain.link.dto.response.LinkDuplicateCheckRes;
import com.sofa.linkiving.domain.link.dto.response.LinkRes;
import com.sofa.linkiving.domain.link.dto.response.MetaScrapeRes;
Expand All @@ -17,10 +18,14 @@
import com.sofa.linkiving.global.common.BaseResponse;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;

@Validated
@Tag(name = "Link", description = "링크 관리 API")
public interface LinkApi {

Expand Down Expand Up @@ -55,16 +60,21 @@ BaseResponse<Void> deleteLink(
Member member
);

@Operation(summary = "링크 조회", description = "링크 상세 정보를 조회합니다")
BaseResponse<LinkRes> getLink(
@Operation(summary = "링크 상세 조회", description = "링크 상세 정보를 조회합니다")
BaseResponse<LinkDetailRes> getLink(
Long id,
Member member
);

@Operation(summary = "링크 목록 조회", description = "저장된 링크 목록을 페이징하여 조회합니다")
BaseResponse<Page<LinkRes>> getLinkList(
Pageable pageable,
Member member
@Operation(summary = "링크 카드 목록 조회", description = "저장된 링크 목록을 무한 스크롤 방식으로 조회합니다")
BaseResponse<LinkCardsRes> getLinkList(
Member member,
@Parameter(description = "페이징을 위한 마지막 메시지 ID, 첫 조회 시 null") Long lastId,

@Parameter(description = "한번에 조회할 데이터 갯수")
@Min(value = 1, message = "최소 1개 이상 조회해야 합니다.")
@Max(value = 50, message = "한 번에 최대 50개까지만 조회할 수 있습니다.")
int size
);

@Operation(summary = "링크 제목 수정", description = "링크 제목만 수정합니다")
Expand All @@ -84,7 +94,8 @@ BaseResponse<LinkRes> updateMemo(
@Operation(summary = "요약 재생성", description = "요약을 재생성 하고 신규 요약 기존 요약, 기존 및 신규 요약 비교 정보을 제공합니다.")
BaseResponse<RecreateSummaryResponse> recreateSummary(
Long id,
@Valid @Schema(description = "요청 형식(CONCISE: 간결하게, DETAILED:자세하게)") Format format,
@Schema(description = "요청 형식(CONCISE: 간결하게, DETAILED:자세하게)")
@Valid Format format,
Member member
);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.sofa.linkiving.domain.link.controller;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
Expand All @@ -19,6 +17,8 @@
import com.sofa.linkiving.domain.link.dto.request.LinkTitleUpdateReq;
import com.sofa.linkiving.domain.link.dto.request.LinkUpdateReq;
import com.sofa.linkiving.domain.link.dto.request.MetaScrapeReq;
import com.sofa.linkiving.domain.link.dto.response.LinkCardsRes;
import com.sofa.linkiving.domain.link.dto.response.LinkDetailRes;
import com.sofa.linkiving.domain.link.dto.response.LinkDuplicateCheckRes;
import com.sofa.linkiving.domain.link.dto.response.LinkRes;
import com.sofa.linkiving.domain.link.dto.response.MetaScrapeRes;
Expand Down Expand Up @@ -102,21 +102,22 @@ public BaseResponse<Void> deleteLink(

@Override
@GetMapping("/{id}")
public BaseResponse<LinkRes> getLink(
public BaseResponse<LinkDetailRes> getLink(
@PathVariable Long id,
@AuthMember Member member
) {
LinkRes response = linkFacade.getLink(id, member);
LinkDetailRes response = linkFacade.getLinkDetail(id, member);
return BaseResponse.success(response, "링크 조회 완료");
}

@Override
@GetMapping
public BaseResponse<Page<LinkRes>> getLinkList(
@PageableDefault(size = 20) Pageable pageable,
@AuthMember Member member
public BaseResponse<LinkCardsRes> getLinkList(
@AuthMember Member member,
@RequestParam(required = false) Long lastId,
@RequestParam(defaultValue = "20") int size
) {
Page<LinkRes> response = linkFacade.getLinkList(member, pageable);
LinkCardsRes response = linkFacade.getLinkCards(member, lastId, size);
return BaseResponse.success(response, "링크 목록 조회 완료");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sofa.linkiving.domain.link.dto.internal;

import com.sofa.linkiving.domain.link.entity.Link;
import com.sofa.linkiving.domain.link.entity.Summary;

public record LinkDto(
Link link,
Summary summary
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sofa.linkiving.domain.link.dto.internal;

import java.util.List;

public record LinksDto(
List<LinkDto> linkDtos,
boolean hasNext
) {
}

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.sofa.linkiving.domain.link.dto;
package com.sofa.linkiving.domain.link.dto.internal;

import lombok.Builder;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.sofa.linkiving.domain.link.dto.response;

import java.util.List;

import com.sofa.linkiving.domain.link.dto.internal.LinkDto;
import com.sofa.linkiving.domain.link.dto.internal.LinksDto;
import com.sofa.linkiving.domain.link.entity.Link;
import com.sofa.linkiving.domain.link.entity.Summary;

import io.swagger.v3.oas.annotations.media.Schema;

public record LinkCardsRes(
@Schema(description = "링크 목록")
List<LinkCardRes> links,
@Schema(description = "다음 페이지 존재 여부")
boolean hasNext,
@Schema(description = "마지막 메시지 ID (다음 요청 커서용)")
Long lastId
) {
public static LinkCardsRes of(LinksDto linksDto) {
List<LinkCardRes> links = linksDto.linkDtos().stream().map(LinkCardRes::from).toList();
Long lastId = links.isEmpty() ? null : links.get(links.size() - 1).id();

return new LinkCardsRes(links, linksDto.hasNext(), lastId);
}

public record LinkCardRes(
@Schema(description = "링크 ID")
Long id,

@Schema(description = "링크 URL", example = "https://example.com")
String url,

@Schema(description = "링크 제목", example = "유용한 개발 자료")
String title,

@Schema(description = "이미지 URL", example = "https://example.com/image.jpg")
String imageUrl,

@Schema(description = "요약 정보")
String summary
) {
public static LinkCardRes from(LinkDto dto) {
return of(dto.link(), dto.summary());
}

public static LinkCardRes of(Link link, Summary summary) {
return new LinkCardRes(
link.getId(),
link.getUrl(),
link.getTitle(),
link.getImageUrl(),
summary == null ? null : summary.getContent()
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.sofa.linkiving.domain.link.dto.response;

import com.sofa.linkiving.domain.link.dto.internal.LinkDto;
import com.sofa.linkiving.domain.link.entity.Link;
import com.sofa.linkiving.domain.link.entity.Summary;

import io.swagger.v3.oas.annotations.media.Schema;

public record LinkDetailRes(
@Schema(description = "링크 ID")
Long id,

@Schema(description = "링크 URL", example = "https://example.com")
String url,

@Schema(description = "링크 제목", example = "유용한 개발 자료")
String title,

@Schema(description = "메모", example = "나중에 읽어볼 것")
String memo,

@Schema(description = "이미지 URL", example = "https://example.com/image.jpg")
String imageUrl,

@Schema(description = "요약 정보")
SummaryRes summary
) {
public static LinkDetailRes from(LinkDto dto) {
return of(dto.link(), dto.summary());
}

public static LinkDetailRes of(Link link, Summary summary) {
return new LinkDetailRes(
link.getId(),
link.getUrl(),
link.getTitle(),
link.getMemo(),
link.getImageUrl(),
SummaryRes.from(summary)
);
}

public record SummaryRes(
@Schema(description = "요약 ID")
Long id,
@Schema(description = "요약 내용", example = "이 링크는 예시 링크입니다.")
String content
) {
public static SummaryRes from(Summary summary) {
if (summary == null) {
return null;
}
return new SummaryRes(summary.getId(), summary.getContent());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.sofa.linkiving.domain.link.dto.response;

import com.sofa.linkiving.domain.link.dto.OgTagDto;
import com.sofa.linkiving.domain.link.dto.internal.OgTagDto;

import io.swagger.v3.oas.annotations.media.Schema;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ public class Summary extends BaseEntity {
@Column(columnDefinition = "TEXT", nullable = false)
private String content;

@Column(name = "selected", columnDefinition = "TEXT")
private String selected;
@Column(name = "selected")
private boolean selected;

@Builder
public Summary(Link link, Format format, String content, String select) {
public Summary(Link link, Format format, String content, boolean select) {
this.link = link;
this.format = format;
this.content = content;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.sofa.linkiving.domain.link.facade;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.sofa.linkiving.domain.link.dto.OgTagDto;
import com.sofa.linkiving.domain.link.dto.internal.LinkDto;
import com.sofa.linkiving.domain.link.dto.internal.LinksDto;
import com.sofa.linkiving.domain.link.dto.internal.OgTagDto;
import com.sofa.linkiving.domain.link.dto.response.LinkCardsRes;
import com.sofa.linkiving.domain.link.dto.response.LinkDetailRes;
import com.sofa.linkiving.domain.link.dto.response.LinkDuplicateCheckRes;
import com.sofa.linkiving.domain.link.dto.response.LinkRes;
import com.sofa.linkiving.domain.link.dto.response.MetaScrapeRes;
Expand Down Expand Up @@ -53,15 +55,15 @@ public void deleteLink(Long linkId, Member member) {
}

@Transactional(readOnly = true)
public LinkRes getLink(Long linkId, Member member) {
Link link = linkService.getLink(linkId, member);
return LinkRes.from(link);
public LinkDetailRes getLinkDetail(Long linkId, Member member) {
LinkDto linkDto = linkService.getLinkWithSummary(linkId, member);
return LinkDetailRes.from(linkDto);
}

@Transactional(readOnly = true)
public Page<LinkRes> getLinkList(Member member, Pageable pageable) {
Page<Link> links = linkService.getLinkList(member, pageable);
return links.map(LinkRes::from);
public LinkCardsRes getLinkCards(Member member, Long lastId, int size) {
LinksDto linkDtos = linkService.getLinksWithSummary(member, lastId, size);
return LinkCardsRes.of(linkDtos);
}

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,55 @@
package com.sofa.linkiving.domain.link.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.sofa.linkiving.domain.link.dto.internal.LinkDto;
import com.sofa.linkiving.domain.link.entity.Link;
import com.sofa.linkiving.domain.member.entity.Member;

public interface LinkRepository extends JpaRepository<Link, Long> {

Optional<Link> findByIdAndMember(Long id, Member member);

Page<Link> findByMemberAndIsDeleteFalse(Member member, Pageable pageable);

boolean existsByMemberAndUrlAndIsDeleteFalse(Member member, String url);

@Query("SELECT l.id FROM Link l WHERE l.member = :member AND l.url = :url AND l.isDelete = false")
@Query("""
SELECT l.id
FROM Link l
WHERE l.member = :member AND l.url = :url AND l.isDelete = false
""")
Optional<Long> findIdByMemberAndUrlAndIsDeleteFalse(@Param("member") Member member, @Param("url") String url);

@Query("""
SELECT new com.sofa.linkiving.domain.link.dto.internal.LinkDto(l, s)
FROM Link l
LEFT JOIN Summary s ON s.link = l AND s.selected = true
WHERE l.id = :id
AND l.member = :member
AND l.isDelete = false
""")
Optional<LinkDto> findByIdAndMemberWithSummaryAndIsDeleteFalse(
@Param("id") Long id,
@Param("member") Member member
);

@Query("""
SELECT new com.sofa.linkiving.domain.link.dto.internal.LinkDto(l, s)
FROM Link l
LEFT JOIN Summary s ON s.link = l AND s.selected = true
WHERE l.member = :member
AND l.isDelete = false
AND (:lastId IS NULL OR l.id < :lastId)
ORDER BY l.id DESC
""")
List<LinkDto> findAllByMemberWithSummaryAndCursorAndIsDeleteFalse(
@Param("member") Member member,
@Param("lastId") Long lastId,
Pageable pageable
);
}
Loading