From aedd7d96db0827bbad3e24bc3e8887e22648dd5f Mon Sep 17 00:00:00 2001 From: sangrae Date: Thu, 29 Oct 2026 17:15:39 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=ED=96=89=EC=82=AC=20=EB=8B=A8?= =?UTF-8?q?=EA=B1=B4=20=EC=A1=B0=ED=9A=8C=EC=97=90=20=EC=97=86=EC=9C=BC?= =?UTF-8?q?=EB=A9=B4=20=EB=B9=84=EB=8A=94=20=EA=B2=BD=EB=A1=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/domain/event/service/EventService.java | 10 ++++++++++ .../domain/event/service/impl/EventServiceImpl.java | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/event/service/EventService.java b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/event/service/EventService.java index 416a387eb..92e6f3c2d 100644 --- a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/event/service/EventService.java +++ b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/event/service/EventService.java @@ -1,6 +1,7 @@ package kr.ac.kookmin.stream.event.domain.event.service; import kr.ac.kookmin.stream.common.CursorSliceResult; +import kr.ac.kookmin.stream.event.domain.event.domain.Event; import kr.ac.kookmin.stream.event.domain.event.domain.EventCursor; import kr.ac.kookmin.stream.event.domain.event.domain.EventDetail; import kr.ac.kookmin.stream.event.domain.event.domain.EventSummary; @@ -15,4 +16,13 @@ public interface EventService { CursorSliceResult getPublishedEvents(RecruitStatus recruitStatus, EventCursor cursor, int size); EventDetail getPublishedEvent(Long eventId); + + /** + * 행사 한 건. 없거나 삭제됐으면 {@code null}이다. 아직 게시하지 않은 행사도 돌려준다 — 운영진 화면은 + * 미게시 행사도 보여야 하고, 학생에게 감추는 일은 부르는 쪽이 {@link Event#isPublished()}로 한다. + *

+ * 다른 화면이 행사를 곁들여 보여줄 때(아카이빙의 연결 행사) 쓴다. 행사가 사라졌다고 그 화면까지 404가 되면 + * 안 되므로 {@link #getPublishedEvent(Long)}와 달리 던지지 않는다. + */ + Event getByIdOrNull(Long eventId); } diff --git a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/event/service/impl/EventServiceImpl.java b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/event/service/impl/EventServiceImpl.java index e93260d25..e2169b87b 100644 --- a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/event/service/impl/EventServiceImpl.java +++ b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/event/service/impl/EventServiceImpl.java @@ -58,4 +58,9 @@ public EventDetail getPublishedEvent(Long eventId) { LocalDateTime now = LocalDateTime.now(); return EventDetail.of(event, eventApplicationRepository.countAppliedByEventId(eventId), now); } + + @Override + public Event getByIdOrNull(Long eventId) { + return eventRepository.findById(eventId).orElse(null); + } } From 76bab4da27be61f9646b6e76f05ae16ea70d8724 Mon Sep 17 00:00:00 2001 From: sangrae Date: Thu, 29 Oct 2026 17:16:04 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=EC=95=84=EC=B9=B4=EC=9D=B4?= =?UTF-8?q?=EB=B9=99=20=EA=B4=80=EB=A0=A8=20=EB=A7=81=ED=81=AC=EB=A5=BC=20?= =?UTF-8?q?=ED=96=89=EC=82=AC=20=EC=97=B0=EA=B2=B0=EB=A1=9C=20=EA=B5=90?= =?UTF-8?q?=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/ArchiveDetailResponse.java | 14 +---- .../event/domain/archive/domain/Archive.java | 7 ++- .../domain/archive/domain/ArchiveDetail.java | 8 ++- .../archive/domain/ArchiveRelatedLink.java | 28 --------- .../archive/repository/ArchiveRepository.java | 3 - .../service/impl/ArchiveServiceImpl.java | 2 +- .../stream/db/event/ArchiveJpaEntity.java | 7 ++- .../db/event/ArchiveRelatedLinkJpaEntity.java | 57 ------------------- .../ArchiveRelatedLinkJpaRepository.java | 13 ----- .../db/event/ArchiveRepositoryImpl.java | 9 --- ...chive_related_links_with_related_event.sql | 16 ++++++ 11 files changed, 35 insertions(+), 129 deletions(-) delete mode 100644 core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/domain/ArchiveRelatedLink.java delete mode 100644 infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveRelatedLinkJpaEntity.java delete mode 100644 infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveRelatedLinkJpaRepository.java create mode 100644 infrastructure/db/src/main/resources/db/migration/V28__replace_archive_related_links_with_related_event.sql diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/response/ArchiveDetailResponse.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/response/ArchiveDetailResponse.java index 1631c1203..7393414db 100644 --- a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/response/ArchiveDetailResponse.java +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/response/ArchiveDetailResponse.java @@ -5,7 +5,6 @@ import java.util.Map; import kr.ac.kookmin.stream.api.common.StorageUrlBuilder; import kr.ac.kookmin.stream.event.domain.archive.domain.ArchiveDetail; -import kr.ac.kookmin.stream.event.domain.archive.domain.ArchiveRelatedLink; import kr.ac.kookmin.stream.file.domain.File; public record ArchiveDetailResponse( @@ -16,8 +15,7 @@ public record ArchiveDetailResponse( String location, String departmentName, String content, - List images, - List relatedLinks + List images ) { /** @@ -35,8 +33,7 @@ public static ArchiveDetailResponse from(ArchiveDetail detail, Map i detail.imageIds().stream() .filter(imagesById::containsKey) .map(fileId -> Image.of(fileId, imagesById.get(fileId))) - .toList(), - detail.relatedLinks().stream().map(RelatedLink::from).toList() + .toList() ); } @@ -46,11 +43,4 @@ public static Image of(Long fileId, File file) { return new Image(fileId, StorageUrlBuilder.build(file.getFileKey())); } } - - public record RelatedLink(String title, String url) { - - public static RelatedLink from(ArchiveRelatedLink relatedLink) { - return new RelatedLink(relatedLink.getTitle(), relatedLink.getUrl()); - } - } } diff --git a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/domain/Archive.java b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/domain/Archive.java index 1a31352a3..8c718288a 100644 --- a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/domain/Archive.java +++ b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/domain/Archive.java @@ -23,6 +23,8 @@ public class Archive { private List imageIds; /** 슬랑제(연말 행사) 전용 페이지에 띄울지. 삭제 여부와 달리 운영진이 목록에서 보고 켜고 끄는 값이라 도메인이 들고 있다. */ private boolean exposed; + /** 연결한 행사. 연결하지 않았으면 {@code null}이다. 제목은 담지 않는다 — 조회 시점에 행사에서 가져온다. */ + private Long relatedEventId; /** 등록 일시. 운영진 목록이 이 순서로 정렬하고 화면에도 보여준다. */ private LocalDateTime createdAt; @@ -36,10 +38,11 @@ public static Archive of( String departmentName, List imageIds, boolean exposed, + Long relatedEventId, LocalDateTime createdAt ) { - return new Archive( - id, title, content, startDate, endDate, location, departmentName, imageIds, exposed, createdAt); + return new Archive(id, title, content, startDate, endDate, location, departmentName, imageIds, + exposed, relatedEventId, createdAt); } /** diff --git a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/domain/ArchiveDetail.java b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/domain/ArchiveDetail.java index 91df91264..da5149eb3 100644 --- a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/domain/ArchiveDetail.java +++ b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/domain/ArchiveDetail.java @@ -9,6 +9,8 @@ *

* 학생 앱과 운영진이 같이 쓴다. 담는 값이 거의 같고 운영진만 보는 것은 두 개(슬랑제 노출 여부·등록 일시)뿐이라 * 하나로 두고, 학생 앱 응답이 그 둘을 담지 않는다. + * + * @param relatedEventId 연결한 행사. 연결하지 않았으면 {@code null}이고, 제목은 담지 않는다 */ public record ArchiveDetail( Long archiveId, @@ -19,12 +21,12 @@ public record ArchiveDetail( String departmentName, String content, List imageIds, - List relatedLinks, + Long relatedEventId, boolean exposed, LocalDateTime createdAt ) { - public static ArchiveDetail of(Archive archive, List relatedLinks) { + public static ArchiveDetail from(Archive archive) { return new ArchiveDetail( archive.getId(), archive.getTitle(), @@ -34,7 +36,7 @@ public static ArchiveDetail of(Archive archive, List related archive.getDepartmentName(), archive.getContent(), archive.getImageIds() == null ? List.of() : archive.getImageIds(), - relatedLinks, + archive.getRelatedEventId(), archive.isExposed(), archive.getCreatedAt() ); diff --git a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/domain/ArchiveRelatedLink.java b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/domain/ArchiveRelatedLink.java deleted file mode 100644 index 43231cc94..000000000 --- a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/domain/ArchiveRelatedLink.java +++ /dev/null @@ -1,28 +0,0 @@ -package kr.ac.kookmin.stream.event.domain.archive.domain; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; - -@Getter -@EqualsAndHashCode -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class ArchiveRelatedLink { - - private Long id; - private Long archiveId; - private String title; - private String url; - private int displayOrder; - - public static ArchiveRelatedLink of( - Long id, - Long archiveId, - String title, - String url, - int displayOrder - ) { - return new ArchiveRelatedLink(id, archiveId, title, url, displayOrder); - } -} diff --git a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/repository/ArchiveRepository.java b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/repository/ArchiveRepository.java index 66f7e5c91..638c21354 100644 --- a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/repository/ArchiveRepository.java +++ b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/repository/ArchiveRepository.java @@ -5,7 +5,6 @@ import kr.ac.kookmin.stream.common.PageOffset; import kr.ac.kookmin.stream.common.PageResult; import kr.ac.kookmin.stream.event.domain.archive.domain.Archive; -import kr.ac.kookmin.stream.event.domain.archive.domain.ArchiveRelatedLink; public interface ArchiveRepository { @@ -30,6 +29,4 @@ public interface ArchiveRepository { PageResult search(String keyword, Boolean exposed, PageOffset pageOffset); Optional findById(Long id); - - List findRelatedLinksByArchiveId(Long archiveId); } diff --git a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/service/impl/ArchiveServiceImpl.java b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/service/impl/ArchiveServiceImpl.java index e07ec61bc..11f1c859d 100644 --- a/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/service/impl/ArchiveServiceImpl.java +++ b/core/domain/event/src/main/java/kr/ac/kookmin/stream/event/domain/archive/service/impl/ArchiveServiceImpl.java @@ -38,6 +38,6 @@ public ArchiveDetail getArchive(Long archiveId) { Archive archive = archiveRepository.findById(archiveId) .orElseThrow(() -> new BusinessException(ArchiveErrorCode.ARCHIVE_NOT_FOUND)); - return ArchiveDetail.of(archive, archiveRepository.findRelatedLinksByArchiveId(archiveId)); + return ArchiveDetail.from(archive); } } diff --git a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveJpaEntity.java b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveJpaEntity.java index abc421cc1..3b62884b8 100644 --- a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveJpaEntity.java +++ b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveJpaEntity.java @@ -49,6 +49,10 @@ public class ArchiveJpaEntity extends BaseSoftDeleteEntity { @Column(name = "is_exposed", nullable = false) private boolean exposed; + /** 연결한 행사. 연결하지 않았으면 NULL이다. */ + @Column(name = "related_event_id") + private Long relatedEventId; + private ArchiveJpaEntity(Archive archive) { this.id = archive.getId(); this.title = archive.getTitle(); @@ -59,6 +63,7 @@ private ArchiveJpaEntity(Archive archive) { this.departmentName = archive.getDepartmentName(); this.imageIds = archive.getImageIds(); this.exposed = archive.isExposed(); + this.relatedEventId = archive.getRelatedEventId(); } public static ArchiveJpaEntity from(Archive archive) { @@ -68,6 +73,6 @@ public static ArchiveJpaEntity from(Archive archive) { public Archive toDomain() { return Archive.of( id, title, content, startDate, endDate, location, departmentName, imageIds, exposed, - getCreatedAt()); + relatedEventId, getCreatedAt()); } } diff --git a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveRelatedLinkJpaEntity.java b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveRelatedLinkJpaEntity.java deleted file mode 100644 index cd18c7cb5..000000000 --- a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveRelatedLinkJpaEntity.java +++ /dev/null @@ -1,57 +0,0 @@ -package kr.ac.kookmin.stream.db.event; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Index; -import jakarta.persistence.Table; -import kr.ac.kookmin.stream.db.common.BaseTimeEntity; -import kr.ac.kookmin.stream.event.domain.archive.domain.ArchiveRelatedLink; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; - -@Entity -@Table( - name = "archive_related_links", - indexes = { - @Index(name = "idx_archive_related_links_archive_id", columnList = "archive_id") - } -) -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ArchiveRelatedLinkJpaEntity extends BaseTimeEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "archive_related_link_id") - private Long id; - - @Column(name = "archive_id", nullable = false) - private Long archiveId; - - @Column(nullable = false, length = 200) - private String title; - - @Column(nullable = false, length = 2048) - private String url; - - @Column(name = "display_order", nullable = false) - private int displayOrder; - - private ArchiveRelatedLinkJpaEntity(ArchiveRelatedLink link) { - this.id = link.getId(); - this.archiveId = link.getArchiveId(); - this.title = link.getTitle(); - this.url = link.getUrl(); - this.displayOrder = link.getDisplayOrder(); - } - - public static ArchiveRelatedLinkJpaEntity from(ArchiveRelatedLink link) { - return new ArchiveRelatedLinkJpaEntity(link); - } - - public ArchiveRelatedLink toDomain() { - return ArchiveRelatedLink.of(id, archiveId, title, url, displayOrder); - } -} diff --git a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveRelatedLinkJpaRepository.java b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveRelatedLinkJpaRepository.java deleted file mode 100644 index a186b43f3..000000000 --- a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveRelatedLinkJpaRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package kr.ac.kookmin.stream.db.event; - -import java.util.List; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface ArchiveRelatedLinkJpaRepository extends JpaRepository { - - /** - * display_order에 유니크 제약이 없어 같은 값이 둘 이상일 수 있다. 식별자를 동점 기준으로 더해 - * 같은 요청이 항상 같은 순서를 돌려주게 한다. - */ - List findAllByArchiveIdOrderByDisplayOrderAscIdAsc(Long archiveId); -} diff --git a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveRepositoryImpl.java b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveRepositoryImpl.java index 06f9bec27..0b463851e 100644 --- a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveRepositoryImpl.java +++ b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveRepositoryImpl.java @@ -7,7 +7,6 @@ import kr.ac.kookmin.stream.common.PageResult; import kr.ac.kookmin.stream.db.common.LikePatterns; import kr.ac.kookmin.stream.event.domain.archive.domain.Archive; -import kr.ac.kookmin.stream.event.domain.archive.domain.ArchiveRelatedLink; import kr.ac.kookmin.stream.event.domain.archive.repository.ArchiveRepository; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -19,7 +18,6 @@ public class ArchiveRepositoryImpl implements ArchiveRepository { private final ArchiveJpaRepository archiveJpaRepository; - private final ArchiveRelatedLinkJpaRepository archiveRelatedLinkJpaRepository; /** * 연도를 반개구간으로 바꿔 조회한다. {@code YEAR(start_date) = ?}처럼 컬럼에 함수를 씌우면 인덱스를 @@ -58,11 +56,4 @@ public PageResult search(String keyword, Boolean exposed, PageOffset pa public Optional findById(Long id) { return archiveJpaRepository.findByIdAndIsDeletedFalse(id).map(ArchiveJpaEntity::toDomain); } - - @Override - public List findRelatedLinksByArchiveId(Long archiveId) { - return archiveRelatedLinkJpaRepository.findAllByArchiveIdOrderByDisplayOrderAscIdAsc(archiveId).stream() - .map(ArchiveRelatedLinkJpaEntity::toDomain) - .toList(); - } } diff --git a/infrastructure/db/src/main/resources/db/migration/V28__replace_archive_related_links_with_related_event.sql b/infrastructure/db/src/main/resources/db/migration/V28__replace_archive_related_links_with_related_event.sql new file mode 100644 index 000000000..897a542ff --- /dev/null +++ b/infrastructure/db/src/main/resources/db/migration/V28__replace_archive_related_links_with_related_event.sql @@ -0,0 +1,16 @@ +-- 아카이빙의 관련 링크를 행사 참조로 바꾼다. +-- +-- 관리자 화면이 드롭다운에서 행사를 고르고, 학생 앱은 눌러서 그 행사로 이동한다. 제목+URL로는 식별자를 복원할 수 +-- 없어 앱이 어디로 가야 할지 알 수 없고, 저장소에 행사 URL을 만드는 코드도 없다. +-- 제목을 복사해 두면 원본이 수정됐을 때 낡으므로 식별자만 저장하고 제목은 조회 시점에 가져온다. +-- +-- 역방향 조회("이 행사를 건 아카이빙 찾기")가 없어 인덱스는 두지 않는다. +ALTER TABLE archives + ADD COLUMN related_event_id BIGINT NULL; + +-- ⚠️ 되돌릴 수 없다. 지운 뒤에는 이전 이미지로 롤백할 수 없다 — ddl-auto: validate가 없는 테이블을 찾아 기동을 막는다. +-- 적용 전 SELECT COUNT(*) FROM archive_related_links 로 행 수를 확인한다. +-- 연결은 행사 한 건이면 충분하다고 정해서 옮기는 백필을 두지 않았다. 행이 있으면 적용 전에 손으로 옮긴다. +-- 그리고 한 파일에 ADD COLUMN과 DROP TABLE이 함께 있는데 MySQL은 DDL을 트랜잭션으로 묶지 않는다. +-- 중간에 실패하면 앞 문장이 이미 적용된 채로 남아 손으로 되돌려야 한다 (V21과 같은 상황이다). +DROP TABLE archive_related_links; From d336200634b2af9ab379a89955b5e005012d3ed3 Mon Sep 17 00:00:00 2001 From: sangrae Date: Thu, 29 Oct 2026 17:16:21 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=EC=95=84=EC=B9=B4=EC=9D=B4?= =?UTF-8?q?=EB=B9=99=20=EC=83=81=EC=84=B8=EC=97=90=20=EC=97=B0=EA=B2=B0?= =?UTF-8?q?=ED=95=9C=20=ED=96=89=EC=82=AC=20=EB=8B=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/event/archive/AdminArchiveApi.java | 4 +- .../event/archive/AdminArchiveController.java | 13 +++--- .../response/AdminArchiveDetailResponse.java | 20 ++++++++- .../usecase/AdminArchiveDetailUseCase.java | 41 +++++++++++++++++++ .../usecase/ArchiveWithRelatedEvent.java | 15 +++++++ .../api/app/event/archive/AppArchiveApi.java | 7 +++- .../event/archive/AppArchiveController.java | 11 +++-- .../response/ArchiveDetailResponse.java | 21 ++++++++-- .../archive/usecase/ArchiveDetailUseCase.java | 40 ++++++++++++++++++ .../usecase/ArchiveWithRelatedEvent.java | 15 +++++++ 10 files changed, 169 insertions(+), 18 deletions(-) create mode 100644 api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/usecase/AdminArchiveDetailUseCase.java create mode 100644 api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/usecase/ArchiveWithRelatedEvent.java create mode 100644 api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/usecase/ArchiveDetailUseCase.java create mode 100644 api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/usecase/ArchiveWithRelatedEvent.java diff --git a/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/AdminArchiveApi.java b/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/AdminArchiveApi.java index fa1d00928..6718116bb 100644 --- a/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/AdminArchiveApi.java +++ b/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/AdminArchiveApi.java @@ -39,7 +39,9 @@ ApiResponse> getArchives( @Operation(summary = "아카이빙 상세 조회", description = "아카이빙 한 건을 조회한다. 당일 활동은 endDate가 null이다. " + "활동 사진은 노출 순서대로 담고 첫 장이 대표 이미지다 — 대표를 따로 내려주지 않는다. " - + "삭제된 파일은 사진 목록에서 조용히 빠진다.") + + "삭제된 파일은 사진 목록에서 조용히 빠진다. " + + "relatedEvent는 연결한 행사이고, 제목은 저장하지 않고 조회할 때마다 원본에서 가져온다. " + + "연결하지 않았거나 행사가 삭제됐으면 null이다 — 운영진 화면이므로 아직 게시하지 않은 행사도 보여준다.") @ApiErrorCode(type = ArchiveErrorCode.class, codes = {"ARCHIVE_NOT_FOUND"}) ApiResponse getArchive(AdminApiUser apiUser, Long archiveId); } diff --git a/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/AdminArchiveController.java b/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/AdminArchiveController.java index cdef0275a..c14438290 100644 --- a/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/AdminArchiveController.java +++ b/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/AdminArchiveController.java @@ -6,14 +6,14 @@ import kr.ac.kookmin.stream.api.admin.event.archive.request.ArchiveSearchParams; import kr.ac.kookmin.stream.api.admin.event.archive.response.AdminArchiveDetailResponse; import kr.ac.kookmin.stream.api.admin.event.archive.response.AdminArchiveListItemResponse; +import kr.ac.kookmin.stream.api.admin.event.archive.usecase.AdminArchiveDetailUseCase; +import kr.ac.kookmin.stream.api.admin.event.archive.usecase.ArchiveWithRelatedEvent; import kr.ac.kookmin.stream.api.common.dto.ApiResponse; import kr.ac.kookmin.stream.api.common.dto.PageParams; import kr.ac.kookmin.stream.api.common.dto.PageResponse; import kr.ac.kookmin.stream.common.PageResult; import kr.ac.kookmin.stream.event.domain.archive.domain.Archive; -import kr.ac.kookmin.stream.event.domain.archive.domain.ArchiveDetail; import kr.ac.kookmin.stream.event.domain.archive.service.AdminArchiveService; -import kr.ac.kookmin.stream.event.domain.archive.service.ArchiveService; import kr.ac.kookmin.stream.file.domain.File; import kr.ac.kookmin.stream.file.service.FileService; import lombok.RequiredArgsConstructor; @@ -33,7 +33,7 @@ public class AdminArchiveController implements AdminArchiveApi { private final AdminArchiveService adminArchiveService; - private final ArchiveService archiveService; + private final AdminArchiveDetailUseCase adminArchiveDetailUseCase; private final FileService fileService; @Override @@ -55,9 +55,10 @@ public ApiResponse getArchive( AdminApiUser apiUser, @PathVariable Long archiveId ) { - ArchiveDetail detail = archiveService.getArchive(archiveId); - Map imagesById = fileService.findAllByIdIn(detail.imageIds()); + ArchiveWithRelatedEvent archive = adminArchiveDetailUseCase.getArchive(archiveId); + Map imagesById = fileService.findAllByIdIn(archive.detail().imageIds()); - return ApiResponse.success(AdminArchiveDetailResponse.of(detail, imagesById)); + return ApiResponse.success( + AdminArchiveDetailResponse.of(archive.detail(), archive.relatedEventTitle(), imagesById)); } } diff --git a/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/response/AdminArchiveDetailResponse.java b/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/response/AdminArchiveDetailResponse.java index 9b705f6b8..ead85342f 100644 --- a/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/response/AdminArchiveDetailResponse.java +++ b/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/response/AdminArchiveDetailResponse.java @@ -25,11 +25,17 @@ public record AdminArchiveDetailResponse( String content, boolean isExposed, List images, + RelatedEvent relatedEvent, LocalDateTime createdAt ) { - /** @param imagesById 활동 사진 파일. 삭제된 파일은 키가 없어 목록에서 조용히 빠진다 */ - public static AdminArchiveDetailResponse of(ArchiveDetail detail, Map imagesById) { + /** + * @param relatedEventTitle 연결한 행사의 제목. 연결이 없거나 행사가 사라졌으면 {@code null} + * @param imagesById 활동 사진 파일. 삭제된 파일은 키가 없어 목록에서 조용히 빠진다 + */ + public static AdminArchiveDetailResponse of( + ArchiveDetail detail, String relatedEventTitle, Map imagesById + ) { return new AdminArchiveDetailResponse( detail.archiveId(), detail.title(), @@ -43,10 +49,20 @@ public static AdminArchiveDetailResponse of(ArchiveDetail detail, Map Image.of(fileId, imagesById.get(fileId))) .toList(), + RelatedEvent.of(detail.relatedEventId(), relatedEventTitle), detail.createdAt() ); } + /** 연결한 행사. 수정 화면이 드롭다운의 기존 값을 채울 때도 쓴다. */ + public record RelatedEvent(Long eventId, String title) { + + /** 연결이 없거나 행사가 사라져 제목을 못 가져왔으면 {@code null}이다. */ + public static RelatedEvent of(Long eventId, String title) { + return eventId == null || title == null ? null : new RelatedEvent(eventId, title); + } + } + public record Image(Long fileId, String fileUrl) { public static Image of(Long fileId, File file) { diff --git a/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/usecase/AdminArchiveDetailUseCase.java b/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/usecase/AdminArchiveDetailUseCase.java new file mode 100644 index 000000000..d809e0328 --- /dev/null +++ b/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/usecase/AdminArchiveDetailUseCase.java @@ -0,0 +1,41 @@ +package kr.ac.kookmin.stream.api.admin.event.archive.usecase; + +import kr.ac.kookmin.stream.event.domain.archive.domain.ArchiveDetail; +import kr.ac.kookmin.stream.event.domain.archive.service.ArchiveService; +import kr.ac.kookmin.stream.event.domain.event.domain.Event; +import kr.ac.kookmin.stream.event.domain.event.service.EventService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * 아카이빙 상세에 연결한 행사의 제목을 붙인다. 아카이빙과 행사는 같은 모듈의 서로 다른 도메인이라 서비스끼리 + * 부르지 않고 이 UseCase가 조합한다({@code architecture.md} 6-1절, 레이어 규칙 4). + *

+ * 상세 조회 자체는 학생 앱과 내용이 같아 {@link ArchiveService}를 그대로 쓴다({@code coding-style.md} 2-7절). + * 학생 앱 쪽({@code ArchiveDetailUseCase})과 달리 미게시 행사도 보여준다 — 운영진은 아직 공개하지 않은 + * 행사도 연결 대상으로 확인할 수 있어야 한다. + */ +@Component +@RequiredArgsConstructor +public class AdminArchiveDetailUseCase { + + private final ArchiveService archiveService; + private final EventService eventService; + + public ArchiveWithRelatedEvent getArchive(Long archiveId) { + ArchiveDetail detail = archiveService.getArchive(archiveId); + return new ArchiveWithRelatedEvent(detail, resolveTitle(detail.relatedEventId())); + } + + /** + * 연결한 행사가 지워졌으면 {@code null}을 돌려준다. 아카이빙까지 404로 만들지 않고 연결만 빼서 보여준다. + */ + private String resolveTitle(Long relatedEventId) { + if (relatedEventId == null) { + return null; + } + + Event event = eventService.getByIdOrNull(relatedEventId); + return event == null ? null : event.getTitle(); + } +} diff --git a/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/usecase/ArchiveWithRelatedEvent.java b/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/usecase/ArchiveWithRelatedEvent.java new file mode 100644 index 000000000..86ee9ccc4 --- /dev/null +++ b/api/admin-api/src/main/java/kr/ac/kookmin/stream/api/admin/event/archive/usecase/ArchiveWithRelatedEvent.java @@ -0,0 +1,15 @@ +package kr.ac.kookmin.stream.api.admin.event.archive.usecase; + +import kr.ac.kookmin.stream.event.domain.archive.domain.ArchiveDetail; + +/** + * {@link AdminArchiveDetailUseCase}가 조합한 결과. DTO 변환은 Controller가 한다. + *

+ * {@code api:app-api}에도 같은 이름의 record가 있다. 두 모듈은 서로를 모르고, 몇 줄을 공유하자고 + * {@code api:common-api}가 event 도메인에 의존하게 만들 이유가 없어 복제해 둔다 — 운영진과 학생 앱의 + * 연결 행사 판정이 이미 갈려 있어(미게시 행사) 앞으로 더 달라질 자리이기도 하다. + * + * @param relatedEventTitle 연결한 행사의 제목. 연결이 없거나 행사가 사라졌으면 {@code null} + */ +public record ArchiveWithRelatedEvent(ArchiveDetail detail, String relatedEventTitle) { +} diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/AppArchiveApi.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/AppArchiveApi.java index e52e225c6..c5a748e00 100644 --- a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/AppArchiveApi.java +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/AppArchiveApi.java @@ -26,8 +26,11 @@ public interface AppArchiveApi { @ApiErrorCode(type = CommonErrorCode.class, codes = {"INVALID_INPUT"}) ApiResponse getArchives(@ParameterObject ArchiveListParams params); - /** 아카이브 상세. */ - @Operation(summary = "행사 아카이브 상세 조회") + /** 아카이브 상세. 연결된 행사가 있으면 눌러서 이동할 수 있게 함께 내려준다. */ + @Operation(summary = "행사 아카이브 상세 조회", + description = "아카이브 한 건을 조회한다. 현장 사진은 저장된 순서가 곧 표시 순서이고, 삭제된 파일은 " + + "조용히 빠진다. relatedEvent는 연결한 행사이고 제목은 조회할 때마다 원본에서 가져와 항상 최신이다. " + + "연결하지 않았거나 행사가 삭제·미게시면 null이다.") @ApiErrorCode(type = ArchiveErrorCode.class, codes = {"ARCHIVE_NOT_FOUND"}) ApiResponse getArchive(Long archiveId); } diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/AppArchiveController.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/AppArchiveController.java index 88cffaf98..eab223f44 100644 --- a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/AppArchiveController.java +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/AppArchiveController.java @@ -7,7 +7,8 @@ import kr.ac.kookmin.stream.api.app.event.archive.request.ArchiveListParams; import kr.ac.kookmin.stream.api.app.event.archive.response.ArchiveDetailResponse; import kr.ac.kookmin.stream.api.app.event.archive.response.ArchiveListResponse; -import kr.ac.kookmin.stream.event.domain.archive.domain.ArchiveDetail; +import kr.ac.kookmin.stream.api.app.event.archive.usecase.ArchiveDetailUseCase; +import kr.ac.kookmin.stream.api.app.event.archive.usecase.ArchiveWithRelatedEvent; import kr.ac.kookmin.stream.event.domain.archive.domain.ArchiveSummary; import kr.ac.kookmin.stream.event.domain.archive.service.ArchiveService; import kr.ac.kookmin.stream.file.domain.File; @@ -25,6 +26,7 @@ public class AppArchiveController implements AppArchiveApi { private final ArchiveService archiveService; + private final ArchiveDetailUseCase archiveDetailUseCase; private final FileService fileService; /** @@ -46,9 +48,10 @@ public ApiResponse getArchives(@Valid @ModelAttribute Archi @Override @GetMapping("/{archiveId}") public ApiResponse getArchive(@PathVariable Long archiveId) { - ArchiveDetail detail = archiveService.getArchive(archiveId); - Map imagesById = fileService.findAllByIdIn(detail.imageIds()); + ArchiveWithRelatedEvent archive = archiveDetailUseCase.getArchive(archiveId); + Map imagesById = fileService.findAllByIdIn(archive.detail().imageIds()); - return ApiResponse.success(ArchiveDetailResponse.from(detail, imagesById)); + return ApiResponse.success( + ArchiveDetailResponse.of(archive.detail(), archive.relatedEventTitle(), imagesById)); } } diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/response/ArchiveDetailResponse.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/response/ArchiveDetailResponse.java index 7393414db..7aa3d64d5 100644 --- a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/response/ArchiveDetailResponse.java +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/response/ArchiveDetailResponse.java @@ -15,13 +15,18 @@ public record ArchiveDetailResponse( String location, String departmentName, String content, - List images + List images, + RelatedEvent relatedEvent ) { /** * 존재하지 않는(삭제된) fileId는 imagesById에 키가 없다 — 그런 항목은 목록에서 조용히 제외한다. + * + * @param relatedEventTitle 연결한 행사의 제목. 연결이 없거나 행사가 사라졌으면 {@code null} */ - public static ArchiveDetailResponse from(ArchiveDetail detail, Map imagesById) { + public static ArchiveDetailResponse of( + ArchiveDetail detail, String relatedEventTitle, Map imagesById + ) { return new ArchiveDetailResponse( detail.archiveId(), detail.title(), @@ -33,10 +38,20 @@ public static ArchiveDetailResponse from(ArchiveDetail detail, Map i detail.imageIds().stream() .filter(imagesById::containsKey) .map(fileId -> Image.of(fileId, imagesById.get(fileId))) - .toList() + .toList(), + RelatedEvent.of(detail.relatedEventId(), relatedEventTitle) ); } + /** 눌러서 이동할 행사. */ + public record RelatedEvent(Long eventId, String title) { + + /** 연결이 없거나 행사가 사라져 제목을 못 가져왔으면 {@code null}이다. */ + public static RelatedEvent of(Long eventId, String title) { + return eventId == null || title == null ? null : new RelatedEvent(eventId, title); + } + } + public record Image(Long fileId, String fileUrl) { public static Image of(Long fileId, File file) { diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/usecase/ArchiveDetailUseCase.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/usecase/ArchiveDetailUseCase.java new file mode 100644 index 000000000..0df302c1f --- /dev/null +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/usecase/ArchiveDetailUseCase.java @@ -0,0 +1,40 @@ +package kr.ac.kookmin.stream.api.app.event.archive.usecase; + +import kr.ac.kookmin.stream.event.domain.archive.domain.ArchiveDetail; +import kr.ac.kookmin.stream.event.domain.archive.service.ArchiveService; +import kr.ac.kookmin.stream.event.domain.event.domain.Event; +import kr.ac.kookmin.stream.event.domain.event.service.EventService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * 아카이빙 상세에 연결한 행사의 제목을 붙인다. 아카이빙과 행사는 같은 모듈의 서로 다른 도메인이라 서비스끼리 + * 부르지 않고 이 UseCase가 조합한다({@code architecture.md} 6-1절, 레이어 규칙 4). + *

+ * 제목을 저장하지 않고 조회할 때마다 가져오므로 행사명을 고쳐도 아카이빙 화면에 최신 제목이 뜬다. + */ +@Component +@RequiredArgsConstructor +public class ArchiveDetailUseCase { + + private final ArchiveService archiveService; + private final EventService eventService; + + public ArchiveWithRelatedEvent getArchive(Long archiveId) { + ArchiveDetail detail = archiveService.getArchive(archiveId); + return new ArchiveWithRelatedEvent(detail, resolveTitle(detail.relatedEventId())); + } + + /** + * 연결한 행사가 지워졌으면 {@code null}을 돌려준다. 아카이빙까지 404로 만들지 않고 연결만 빼서 보여준다. + * 아직 게시하지 않은 행사도 학생에게는 없는 것으로 본다. + */ + private String resolveTitle(Long relatedEventId) { + if (relatedEventId == null) { + return null; + } + + Event event = eventService.getByIdOrNull(relatedEventId); + return event == null || !event.isPublished() ? null : event.getTitle(); + } +} diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/usecase/ArchiveWithRelatedEvent.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/usecase/ArchiveWithRelatedEvent.java new file mode 100644 index 000000000..c517d2a1c --- /dev/null +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/usecase/ArchiveWithRelatedEvent.java @@ -0,0 +1,15 @@ +package kr.ac.kookmin.stream.api.app.event.archive.usecase; + +import kr.ac.kookmin.stream.event.domain.archive.domain.ArchiveDetail; + +/** + * {@link ArchiveDetailUseCase}가 조합한 결과. DTO 변환은 Controller가 한다. + *

+ * {@code api:admin-api}에도 같은 이름의 record가 있다. 두 모듈은 서로를 모르고, 몇 줄을 공유하자고 + * {@code api:common-api}가 event 도메인에 의존하게 만들 이유가 없어 복제해 둔다 — 운영진과 학생 앱의 + * 연결 행사 판정이 이미 갈려 있어(미게시 행사) 앞으로 더 달라질 자리이기도 하다. + * + * @param relatedEventTitle 연결한 행사의 제목. 연결이 없거나 행사가 사라졌으면 {@code null} + */ +public record ArchiveWithRelatedEvent(ArchiveDetail detail, String relatedEventTitle) { +} From 80c2eec6bc334cd4cca87c719389f23cf336afad Mon Sep 17 00:00:00 2001 From: sangrae Date: Thu, 29 Oct 2026 17:44:26 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20=EC=95=84=EC=B9=B4=EC=9D=B4?= =?UTF-8?q?=EB=B9=99=20=EC=97=B0=EA=B2=B0=20=ED=96=89=EC=82=AC=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=A3=BC=EC=84=9D=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/event/archive/usecase/ArchiveWithRelatedEvent.java | 4 +--- .../java/kr/ac/kookmin/stream/db/event/ArchiveJpaEntity.java | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/usecase/ArchiveWithRelatedEvent.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/usecase/ArchiveWithRelatedEvent.java index c517d2a1c..93b6d2143 100644 --- a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/usecase/ArchiveWithRelatedEvent.java +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/event/archive/usecase/ArchiveWithRelatedEvent.java @@ -5,9 +5,7 @@ /** * {@link ArchiveDetailUseCase}가 조합한 결과. DTO 변환은 Controller가 한다. *

- * {@code api:admin-api}에도 같은 이름의 record가 있다. 두 모듈은 서로를 모르고, 몇 줄을 공유하자고 - * {@code api:common-api}가 event 도메인에 의존하게 만들 이유가 없어 복제해 둔다 — 운영진과 학생 앱의 - * 연결 행사 판정이 이미 갈려 있어(미게시 행사) 앞으로 더 달라질 자리이기도 하다. + * {@code api:admin-api}에도 같은 record가 있다. 복제해 둔 이유는 그쪽 javadoc에 적었다. * * @param relatedEventTitle 연결한 행사의 제목. 연결이 없거나 행사가 사라졌으면 {@code null} */ diff --git a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveJpaEntity.java b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveJpaEntity.java index 3b62884b8..0d19036f4 100644 --- a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveJpaEntity.java +++ b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/event/ArchiveJpaEntity.java @@ -49,7 +49,6 @@ public class ArchiveJpaEntity extends BaseSoftDeleteEntity { @Column(name = "is_exposed", nullable = false) private boolean exposed; - /** 연결한 행사. 연결하지 않았으면 NULL이다. */ @Column(name = "related_event_id") private Long relatedEventId; From 7a87f6becf9c86b19875c42fa0a159cc9fe6b60b Mon Sep 17 00:00:00 2001 From: sangrae Date: Mon, 2 Nov 2026 17:48:42 +0900 Subject: [PATCH 5/5] =?UTF-8?q?chore:=20Flyway=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98=20=EB=B2=84=EC=A0=84=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...> V31__replace_archive_related_links_with_related_event.sql} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename infrastructure/db/src/main/resources/db/migration/{V28__replace_archive_related_links_with_related_event.sql => V31__replace_archive_related_links_with_related_event.sql} (95%) diff --git a/infrastructure/db/src/main/resources/db/migration/V28__replace_archive_related_links_with_related_event.sql b/infrastructure/db/src/main/resources/db/migration/V31__replace_archive_related_links_with_related_event.sql similarity index 95% rename from infrastructure/db/src/main/resources/db/migration/V28__replace_archive_related_links_with_related_event.sql rename to infrastructure/db/src/main/resources/db/migration/V31__replace_archive_related_links_with_related_event.sql index 897a542ff..6ee587b24 100644 --- a/infrastructure/db/src/main/resources/db/migration/V28__replace_archive_related_links_with_related_event.sql +++ b/infrastructure/db/src/main/resources/db/migration/V31__replace_archive_related_links_with_related_event.sql @@ -12,5 +12,5 @@ ALTER TABLE archives -- 적용 전 SELECT COUNT(*) FROM archive_related_links 로 행 수를 확인한다. -- 연결은 행사 한 건이면 충분하다고 정해서 옮기는 백필을 두지 않았다. 행이 있으면 적용 전에 손으로 옮긴다. -- 그리고 한 파일에 ADD COLUMN과 DROP TABLE이 함께 있는데 MySQL은 DDL을 트랜잭션으로 묶지 않는다. --- 중간에 실패하면 앞 문장이 이미 적용된 채로 남아 손으로 되돌려야 한다 (V21과 같은 상황이다). +-- 중간에 실패하면 앞 문장이 이미 적용된 채로 남아 손으로 되돌려야 한다 (V28과 같은 상황이다). DROP TABLE archive_related_links;