diff --git a/.gitignore b/.gitignore index 6a502e5..d209ac3 100644 --- a/.gitignore +++ b/.gitignore @@ -234,8 +234,8 @@ $RECYCLE.BIN/ *.lnk # application config -src/main/resources/application.properties -src/main/resources/application.yml +*.properties +*.yml # End of https://www.toptal.com/developers/gitignore/api/java,intellij,macos,windows diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 729cea0..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,19 +0,0 @@ -version: '3' -services: - mysql: - image: mysql - environment: - MYSQL_ROOT_PASSWORD: 1111 - MYSQL_DATABASE: tripami - - your-application: - image: kangjaekwon/tripami_be - depends_on: - - mysql - environment: - SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/tripami - SPRING_DATASOURCE_USERNAME: root - SPRING_DATASOURCE_PASSWORD: 1111 - - ports: - - "8080:8080" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 3fa8f86..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/src/main/java/TripAmi/backend/BackendApplication.java b/src/main/java/TripAmi/backend/BackendApplication.java index e757938..b1522d1 100644 --- a/src/main/java/TripAmi/backend/BackendApplication.java +++ b/src/main/java/TripAmi/backend/BackendApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication +@EnableJpaAuditing public class BackendApplication { public static void main(String[] args) { diff --git a/src/main/java/TripAmi/backend/app/banner/domain/Banner.java b/src/main/java/TripAmi/backend/app/banner/domain/Banner.java index d28fce5..0fd454f 100644 --- a/src/main/java/TripAmi/backend/app/banner/domain/Banner.java +++ b/src/main/java/TripAmi/backend/app/banner/domain/Banner.java @@ -1,13 +1,12 @@ package TripAmi.backend.app.banner.domain; -import javax.persistence.*; +import TripAmi.backend.app.util.BaseEntity; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.data.annotation.CreatedDate; -import java.time.LocalDateTime; +import javax.persistence.*; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -24,14 +23,21 @@ public class Banner { private String title; - @CreatedDate - @Column(nullable = false, name = "create_at") - private LocalDateTime createdAt; + @Embedded + private BaseEntity baseEntity = new BaseEntity(); @Builder - public Banner(String imgUrl, String title, LocalDateTime createdAt) { + public Banner(String imgUrl, String title) { this.imgUrl = imgUrl; this.title = title; - this.createdAt = createdAt; + } + + public void update(String title, String imgUrl) { + this.title = title; + this.imgUrl = imgUrl; + } + + public void delete() { + this.baseEntity.delete(); } } diff --git a/src/main/java/TripAmi/backend/app/banner/domain/BannerRepository.java b/src/main/java/TripAmi/backend/app/banner/domain/BannerRepository.java index 85c9909..55eaf47 100644 --- a/src/main/java/TripAmi/backend/app/banner/domain/BannerRepository.java +++ b/src/main/java/TripAmi/backend/app/banner/domain/BannerRepository.java @@ -3,4 +3,5 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface BannerRepository extends JpaRepository { + } diff --git a/src/main/java/TripAmi/backend/app/banner/exception/BannerNotFound.java b/src/main/java/TripAmi/backend/app/banner/exception/BannerNotFound.java new file mode 100644 index 0000000..1e4058b --- /dev/null +++ b/src/main/java/TripAmi/backend/app/banner/exception/BannerNotFound.java @@ -0,0 +1,4 @@ +package TripAmi.backend.app.banner.exception; + +public class BannerNotFound extends RuntimeException{ +} diff --git a/src/main/java/TripAmi/backend/app/banner/infra/BannerServiceImpl.java b/src/main/java/TripAmi/backend/app/banner/infra/BannerServiceImpl.java new file mode 100644 index 0000000..c7efbbf --- /dev/null +++ b/src/main/java/TripAmi/backend/app/banner/infra/BannerServiceImpl.java @@ -0,0 +1,64 @@ +package TripAmi.backend.app.banner.infra; + +import TripAmi.backend.app.banner.domain.Banner; +import TripAmi.backend.app.banner.domain.BannerRepository; +import TripAmi.backend.app.banner.exception.BannerNotFound; +import TripAmi.backend.app.banner.service.BannerService; +import TripAmi.backend.web.api.banner.response.BannerDto; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class BannerServiceImpl implements BannerService { + private final BannerRepository bannerRepository; + + @Override + @Transactional + public void join(String title, String imgUrl) { + Banner banner = Banner.builder() + .title(title) + .imgUrl(imgUrl) + .build(); + bannerRepository.save(banner); + } + + @Override + public BannerDto findById(Long id) { + Banner findOne = bannerRepository.findById(id).orElseThrow(BannerNotFound::new); + return new BannerDto(id, findOne.getTitle(), findOne.getImgUrl()); + } + + @Override + public List findBanners() { + List items = bannerRepository.findAll(); + return items.stream() + .map(item -> new BannerDto( + item.getId(), + item.getTitle(), + item.getImgUrl() + )).collect(Collectors.toList()); + } + + @Override + public BannerDto update(Long id, String title, String imgUrl) { + Banner banner = bannerRepository.findById(id).orElseThrow(BannerNotFound::new); + banner.update(title, imgUrl); + return BannerDto.builder() + .title(title) + .imgUrl(imgUrl) + .build(); + } + + @Override + public void delete(Long id) { + Banner banner = bannerRepository.findById(id).orElseThrow(BannerNotFound::new); + banner.delete(); + } + +} diff --git a/src/main/java/TripAmi/backend/app/banner/service/BannerService.java b/src/main/java/TripAmi/backend/app/banner/service/BannerService.java new file mode 100644 index 0000000..18dee96 --- /dev/null +++ b/src/main/java/TripAmi/backend/app/banner/service/BannerService.java @@ -0,0 +1,17 @@ +package TripAmi.backend.app.banner.service; + +import TripAmi.backend.web.api.banner.response.BannerDto; + +import java.util.List; + +public interface BannerService { + void join(String title, String imgUrl); + + BannerDto findById(Long id); + + List findBanners(); + + BannerDto update(Long id, String title, String imgUrl); + + void delete(Long id); +} diff --git a/src/main/java/TripAmi/backend/app/faq/domain/Faq.java b/src/main/java/TripAmi/backend/app/faq/domain/Faq.java new file mode 100644 index 0000000..303a6ce --- /dev/null +++ b/src/main/java/TripAmi/backend/app/faq/domain/Faq.java @@ -0,0 +1,41 @@ +package TripAmi.backend.app.faq.domain; + +import TripAmi.backend.app.util.BaseEntity; +import javax.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Table(name = "faq") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Faq { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "faq_id") + private Long id; + + private String question; + + private String answer; + + @Embedded + private BaseEntity baseEntity = new BaseEntity(); + + @Builder + public Faq(String question, String answer) { + this.question = question; + this.answer = answer; + } + + public void update(String question, String answer) { + this.question = question; + this.answer = answer; + } + + public void delete() { + this.baseEntity.delete(); + } +} diff --git a/src/main/java/TripAmi/backend/app/faq/domain/FaqRepository.java b/src/main/java/TripAmi/backend/app/faq/domain/FaqRepository.java new file mode 100644 index 0000000..9d310cc --- /dev/null +++ b/src/main/java/TripAmi/backend/app/faq/domain/FaqRepository.java @@ -0,0 +1,6 @@ +package TripAmi.backend.app.faq.domain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface FaqRepository extends JpaRepository { +} diff --git a/src/main/java/TripAmi/backend/app/faq/exception/FaqNotFound.java b/src/main/java/TripAmi/backend/app/faq/exception/FaqNotFound.java new file mode 100644 index 0000000..a8c9b0d --- /dev/null +++ b/src/main/java/TripAmi/backend/app/faq/exception/FaqNotFound.java @@ -0,0 +1,4 @@ +package TripAmi.backend.app.faq.exception; + +public class FaqNotFound extends RuntimeException{ +} diff --git a/src/main/java/TripAmi/backend/app/faq/infra/FaqServiceImpl.java b/src/main/java/TripAmi/backend/app/faq/infra/FaqServiceImpl.java new file mode 100644 index 0000000..325d6a2 --- /dev/null +++ b/src/main/java/TripAmi/backend/app/faq/infra/FaqServiceImpl.java @@ -0,0 +1,67 @@ +package TripAmi.backend.app.faq.infra; + +import TripAmi.backend.app.faq.domain.Faq; +import TripAmi.backend.app.faq.domain.FaqRepository; +import TripAmi.backend.app.faq.exception.FaqNotFound; +import TripAmi.backend.app.faq.service.FaqService; +import TripAmi.backend.web.api.faq.response.FaqDto; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class FaqServiceImpl implements FaqService { + private final FaqRepository faqRepository; + + @Override + @Transactional + public void save(String question, String answer) { + Faq faq = Faq.builder() + .question(question) + .answer(answer) + .build(); + faqRepository.save(faq); + } + + @Override + public FaqDto findById(Long id) { + Faq item = faqRepository.findById(id).orElseThrow(FaqNotFound::new); + return FaqDto.builder() + .question(item.getQuestion()) + .answer(item.getAnswer()) + .build(); + } + + @Override + public List findFaqs() { + List allFaq = faqRepository.findAll(); + return allFaq.stream() + .map(item -> new FaqDto( + item.getQuestion(), + item.getAnswer() + )) + .collect(Collectors.toList()); + } + + @Override + @Transactional + public FaqDto update(Long faqId, String question, String answer) { + Faq faq = faqRepository.findById(faqId).orElseThrow(FaqNotFound::new); + faq.update(question, answer); + return FaqDto.builder() + .question(question) + .answer(answer) + .build(); + } + + @Override + public void delete(Long id) { + Faq faq = faqRepository.findById(id).orElseThrow(FaqNotFound::new); + faq.delete(); + } +} diff --git a/src/main/java/TripAmi/backend/app/faq/service/FaqService.java b/src/main/java/TripAmi/backend/app/faq/service/FaqService.java new file mode 100644 index 0000000..c8cbf7d --- /dev/null +++ b/src/main/java/TripAmi/backend/app/faq/service/FaqService.java @@ -0,0 +1,19 @@ +package TripAmi.backend.app.faq.service; + +import TripAmi.backend.web.api.faq.request.FaqCreateRequest; +import TripAmi.backend.web.api.faq.request.FaqUpdateRequest; +import TripAmi.backend.web.api.faq.response.FaqDto; + +import java.util.List; + +public interface FaqService { + void save(String question, String answer); + + FaqDto findById(Long id); + + List findFaqs(); + + FaqDto update(Long faqId, String question, String answer); + + void delete(Long id); +} diff --git a/src/main/java/TripAmi/backend/app/notification/domain/EmitterRepository.java b/src/main/java/TripAmi/backend/app/notification/domain/EmitterRepository.java new file mode 100644 index 0000000..59e6959 --- /dev/null +++ b/src/main/java/TripAmi/backend/app/notification/domain/EmitterRepository.java @@ -0,0 +1,21 @@ +package TripAmi.backend.app.notification.domain; + +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.Map; + +public interface EmitterRepository { + SseEmitter save(String emitterId, SseEmitter sseEmitter); + + void saveEventCache(String emitterId, Object event); + + Map findAllEmitterStartWithMemberId(String memberId); + + Map findAllEventCacheStartWithByMemberId(String memberId); + + void deleteById(String id); + + void deleteAllEmitterStartWithId(String memberId); + + void deleteAllEventCacheStartWithId(String memberId); +} diff --git a/src/main/java/TripAmi/backend/app/notification/domain/Notification.java b/src/main/java/TripAmi/backend/app/notification/domain/Notification.java new file mode 100644 index 0000000..10c480c --- /dev/null +++ b/src/main/java/TripAmi/backend/app/notification/domain/Notification.java @@ -0,0 +1,38 @@ +package TripAmi.backend.app.notification.domain; + +import TripAmi.backend.app.util.BaseEntity; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Getter +@Table(name = "notification") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Notification { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(nullable = false, name = "noti_id") + private Long id; + + private String title; + + private String content; + + @Column(name = "is_read") + private Boolean isRead; + + @Embedded + private BaseEntity baseEntity = new BaseEntity(); + + @Builder + public Notification(String title, String content) { + this.title = title; + this.content = content; + this.isRead = false; + } +} diff --git a/src/main/java/TripAmi/backend/app/notification/domain/NotificationRepository.java b/src/main/java/TripAmi/backend/app/notification/domain/NotificationRepository.java new file mode 100644 index 0000000..91f632c --- /dev/null +++ b/src/main/java/TripAmi/backend/app/notification/domain/NotificationRepository.java @@ -0,0 +1,6 @@ +package TripAmi.backend.app.notification.domain; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NotificationRepository extends JpaRepository { +} diff --git a/src/main/java/TripAmi/backend/app/notification/infra/EmitterRepositoryImpl.java b/src/main/java/TripAmi/backend/app/notification/infra/EmitterRepositoryImpl.java new file mode 100644 index 0000000..15b990b --- /dev/null +++ b/src/main/java/TripAmi/backend/app/notification/infra/EmitterRepositoryImpl.java @@ -0,0 +1,71 @@ +package TripAmi.backend.app.notification.infra; + +import TripAmi.backend.app.notification.domain.EmitterRepository; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.springframework.stereotype.Repository; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +@Repository +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class EmitterRepositoryImpl implements EmitterRepository { + private final Map emitters = new ConcurrentHashMap<>(); + private final Map eventCache = new ConcurrentHashMap<>(); + + @Override + public SseEmitter save(String emitterId, SseEmitter sseEmitter) { + emitters.put(emitterId, sseEmitter); + return sseEmitter; + } + + @Override + public void saveEventCache(String emitterId, Object event) { + eventCache.put(emitterId, event); + } + + + @Override + public Map findAllEmitterStartWithMemberId(String memberId) { + return emitters.entrySet().stream() + .filter(entry -> entry.getKey().startsWith(memberId)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + @Override + public Map findAllEventCacheStartWithByMemberId(String memberId) { + return eventCache.entrySet().stream() + .filter(entry -> entry.getKey().startsWith(memberId)) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + @Override + public void deleteById(String id) { + emitters.remove(id); + } + + @Override + public void deleteAllEmitterStartWithId(String memberId) { + emitters.forEach( + (key, emitter) -> { + if (key.startsWith(memberId)) { + emitters.remove(key); + } + } + ); + } + + @Override + public void deleteAllEventCacheStartWithId(String memberId) { + eventCache.forEach( + (key, emitter) -> { + if (key.startsWith(memberId)) { + eventCache.remove(key); + } + } + ); + } +} diff --git a/src/main/java/TripAmi/backend/app/notification/infra/NotificationServiceImpl.java b/src/main/java/TripAmi/backend/app/notification/infra/NotificationServiceImpl.java new file mode 100644 index 0000000..bdd8c4d --- /dev/null +++ b/src/main/java/TripAmi/backend/app/notification/infra/NotificationServiceImpl.java @@ -0,0 +1,23 @@ +package TripAmi.backend.app.notification.infra; + +import TripAmi.backend.app.notification.service.NotificationService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class NotificationServiceImpl implements NotificationService { + + @Override + public SseEmitter subscribe(Long memberId, String lastEventId) { + return null; + } + + @Override + public String makeTimeIncludeId(Long memberId) { + return memberId + "_" + System.currentTimeMillis(); + } +} diff --git a/src/main/java/TripAmi/backend/app/notification/service/NotificationService.java b/src/main/java/TripAmi/backend/app/notification/service/NotificationService.java new file mode 100644 index 0000000..b27f479 --- /dev/null +++ b/src/main/java/TripAmi/backend/app/notification/service/NotificationService.java @@ -0,0 +1,9 @@ +package TripAmi.backend.app.notification.service; + +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +public interface NotificationService { + SseEmitter subscribe(Long memberId, String lastEventId); + + String makeTimeIncludeId(Long memberId); +} diff --git a/src/main/java/TripAmi/backend/app/product/domain/Product.java b/src/main/java/TripAmi/backend/app/product/domain/Product.java index a9a8580..0d2f106 100644 --- a/src/main/java/TripAmi/backend/app/product/domain/Product.java +++ b/src/main/java/TripAmi/backend/app/product/domain/Product.java @@ -35,8 +35,4 @@ public Product(String title, List images, String content, Integer price) this.content = content; this.price = price; } - -// public void delete() { -// baseEntity.delete(); -// } } diff --git a/src/main/java/TripAmi/backend/app/product/domain/Program.java b/src/main/java/TripAmi/backend/app/product/domain/Program.java index c7b79e7..3a6f448 100644 --- a/src/main/java/TripAmi/backend/app/product/domain/Program.java +++ b/src/main/java/TripAmi/backend/app/product/domain/Program.java @@ -1,6 +1,7 @@ package TripAmi.backend.app.product.domain; import TripAmi.backend.app.product.ProgramTheme; +import TripAmi.backend.app.util.BaseEntity; import TripAmi.backend.app.util.infra.StringListConverter; import javax.persistence.*; import lombok.AccessLevel; @@ -15,32 +16,39 @@ @DiscriminatorValue("program") @PrimaryKeyJoinColumn(name = "program_id") @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Table(name = "program") @Getter public class Program extends Product { + @Column(name = "ami_id") Long amiId; - @OneToMany(mappedBy = "program") + @OneToMany(mappedBy = "program", cascade = CascadeType.ALL) List spots = new ArrayList<>(); - Integer totalPersonnel; + @Column(name = "total_people") + Integer totalPeople; @Enumerated(value = EnumType.STRING) ProgramTheme theme; @Convert(converter = StringListConverter.class) - List keyWords; + List keywords; //todo address class 작성 String location; + @Embedded + private BaseEntity baseEntity; + @Builder - public Program(String title, List images, String content, Integer price, List spots, Long amiId, Integer totalPersonnel, ProgramTheme theme, List keyWords, String location) { + public Program(String title, List images, String content, Integer price, List spots, Long amiId, Integer totalPeople, ProgramTheme theme, List keywords, String location) { super(title, images, content, price); this.amiId = amiId; this.spots.addAll(spots); - this.totalPersonnel = totalPersonnel; + this.totalPeople = totalPeople; this.theme = theme; - this.keyWords = keyWords; + this.keywords = keywords; this.location = location; + this.baseEntity = new BaseEntity(); } } diff --git a/src/main/java/TripAmi/backend/app/product/domain/ProgramRepository.java b/src/main/java/TripAmi/backend/app/product/domain/ProgramRepository.java new file mode 100644 index 0000000..d99c910 --- /dev/null +++ b/src/main/java/TripAmi/backend/app/product/domain/ProgramRepository.java @@ -0,0 +1,17 @@ +package TripAmi.backend.app.product.domain; + +import TripAmi.backend.app.product.ProgramTheme; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface ProgramRepository extends JpaRepository { + + @Query("SELECT p FROM Program p WHERE p.title = :title") + List findByTitle(String title); + + @Query("SELECT p FROM Program p WHERE p.theme = :theme") + List findByTheme(@Param("theme") ProgramTheme theme); +} diff --git a/src/main/java/TripAmi/backend/app/product/infra/ProgramServiceImpl.java b/src/main/java/TripAmi/backend/app/product/infra/ProgramServiceImpl.java new file mode 100644 index 0000000..2bdbc48 --- /dev/null +++ b/src/main/java/TripAmi/backend/app/product/infra/ProgramServiceImpl.java @@ -0,0 +1,59 @@ +package TripAmi.backend.app.product.infra; + +import TripAmi.backend.app.product.ProgramTheme; +import TripAmi.backend.app.product.domain.Program; +import TripAmi.backend.app.product.domain.ProgramRepository; +import TripAmi.backend.app.product.service.ProgramService; +import TripAmi.backend.web.api.program.request.CreateProgramRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ProgramServiceImpl implements ProgramService { + + private final ProgramRepository programRepository; + + @Override + @Transactional + public void save(CreateProgramRequest request) { + Program program = Program.builder() + .amiId(request.amiId()) + .title(request.title()) + .images(request.images()) + .content(request.content()) + .price(request.price()) + .totalPeople(request.totalPeople()) + .theme(request.theme()) + .keywords(request.keyWords()) + .spots(request.spots()) + .location(request.location()) + .build(); + programRepository.save(program); + } + + @Override + public Program findById(Long id) { + return programRepository.findById(id).orElseThrow(); + } + + @Override + public List findByTitle(String title) { + return programRepository.findByTitle(title); + } + + @Override + public List findByTheme(ProgramTheme theme) { + return programRepository.findByTheme(theme); + } + + @Override + public Program findDetailById(Long id) { + return programRepository.findById(id).orElseThrow(); + } + +} diff --git a/src/main/java/TripAmi/backend/app/product/service/ProgramService.java b/src/main/java/TripAmi/backend/app/product/service/ProgramService.java new file mode 100644 index 0000000..4775ff4 --- /dev/null +++ b/src/main/java/TripAmi/backend/app/product/service/ProgramService.java @@ -0,0 +1,19 @@ +package TripAmi.backend.app.product.service; + +import TripAmi.backend.app.product.ProgramTheme; +import TripAmi.backend.app.product.domain.Program; +import TripAmi.backend.web.api.program.request.CreateProgramRequest; + +import java.util.List; + +public interface ProgramService { + void save(CreateProgramRequest request); + + Program findById(Long id); + + List findByTitle(String title); + + List findByTheme(ProgramTheme theme); + + Program findDetailById(Long id); +} diff --git a/src/main/java/TripAmi/backend/app/reservation/domain/Reservation.java b/src/main/java/TripAmi/backend/app/reservation/domain/Reservation.java index bf42d7c..889f8ac 100644 --- a/src/main/java/TripAmi/backend/app/reservation/domain/Reservation.java +++ b/src/main/java/TripAmi/backend/app/reservation/domain/Reservation.java @@ -1,11 +1,15 @@ package TripAmi.backend.app.reservation.domain; -import TripAmi.backend.app.member.domain.Traveler; -import javax.persistence.*; +import TripAmi.backend.app.product.domain.Program; import lombok.AccessLevel; -import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.SneakyThrows; + +import javax.persistence.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -13,18 +17,38 @@ @Getter public class Reservation { @Id - @Column(name = "reservation_id") @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @Column(name = "reservation_id") + Long id; -// private Program program + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "program_id") + Program program; + + @Column(nullable = false, name = "fixed_date") + LocalDateTime fixedDate; + + @Column(nullable = false, name = "fixed_people") + Integer fixedPeople; + + @ElementCollection + @CollectionTable(name = "reservation_people", joinColumns = @JoinColumn(name = "reservation_id")) + @Column(name = "authmember_id") + List joinedPersonIds = new ArrayList<>(); + + public Reservation(Program program, LocalDateTime fixedDate) { + this.program = program; + this.fixedDate = fixedDate; + this.fixedPeople = 0; + } - @ManyToOne - @JoinColumn(name = "member_id") - private Traveler traveler; + @SneakyThrows + public void joinPerson(Long personId) { + if (this.fixedPeople < program.getTotalPeople()) + fixedPeople++; + else + throw new RuntimeException("정원 초과되었습니다."); - @Builder - public Reservation(Traveler traveler) { - this.traveler = traveler; + joinedPersonIds.add(personId); } } diff --git a/src/main/java/TripAmi/backend/app/reservation/domain/ReservationRepository.java b/src/main/java/TripAmi/backend/app/reservation/domain/ReservationRepository.java index 70b9b11..d8c4474 100644 --- a/src/main/java/TripAmi/backend/app/reservation/domain/ReservationRepository.java +++ b/src/main/java/TripAmi/backend/app/reservation/domain/ReservationRepository.java @@ -1,6 +1,11 @@ package TripAmi.backend.app.reservation.domain; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; public interface ReservationRepository extends JpaRepository { + @Query("SELECT r FROM Reservation r WHERE r.program.id = :programId") + List findByProgramId(Long programId); } diff --git a/src/main/java/TripAmi/backend/app/reservation/infra/ReservationServiceImpl.java b/src/main/java/TripAmi/backend/app/reservation/infra/ReservationServiceImpl.java new file mode 100644 index 0000000..c682e4d --- /dev/null +++ b/src/main/java/TripAmi/backend/app/reservation/infra/ReservationServiceImpl.java @@ -0,0 +1,22 @@ +package TripAmi.backend.app.reservation.infra; + +import TripAmi.backend.app.reservation.domain.Reservation; +import TripAmi.backend.app.reservation.domain.ReservationRepository; +import TripAmi.backend.app.reservation.service.ReservationService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ReservationServiceImpl implements ReservationService { + + private final ReservationRepository reservationRepository; + @Override + public List findFixedDays(Long programId) { + return reservationRepository.findByProgramId(programId); + } +} diff --git a/src/main/java/TripAmi/backend/app/reservation/service/ReservationService.java b/src/main/java/TripAmi/backend/app/reservation/service/ReservationService.java new file mode 100644 index 0000000..95c12c3 --- /dev/null +++ b/src/main/java/TripAmi/backend/app/reservation/service/ReservationService.java @@ -0,0 +1,9 @@ +package TripAmi.backend.app.reservation.service; + +import TripAmi.backend.app.reservation.domain.Reservation; + +import java.util.List; + +public interface ReservationService { + List findFixedDays(Long programId); +} diff --git a/src/main/java/TripAmi/backend/app/util/BaseEntity.java b/src/main/java/TripAmi/backend/app/util/BaseEntity.java index aff9cbf..350052e 100644 --- a/src/main/java/TripAmi/backend/app/util/BaseEntity.java +++ b/src/main/java/TripAmi/backend/app/util/BaseEntity.java @@ -1,14 +1,11 @@ package TripAmi.backend.app.util; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.MappedSuperclass; -import lombok.AccessLevel; import lombok.Getter; -import lombok.NoArgsConstructor; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; +import javax.persistence.Column; +import javax.persistence.Embeddable; import java.time.LocalDateTime; @Embeddable @@ -17,10 +14,12 @@ public class BaseEntity { @CreatedDate @Column(name = "created_at") LocalDateTime createdAt; + @LastModifiedDate @Column(name = "updated_at") LocalDateTime updatedAt; Boolean deleted; + public void delete() { this.deleted = true; } diff --git a/src/main/java/TripAmi/backend/app/util/ProgramTheme.java b/src/main/java/TripAmi/backend/app/util/ProgramTheme.java deleted file mode 100644 index 1728f2e..0000000 --- a/src/main/java/TripAmi/backend/app/util/ProgramTheme.java +++ /dev/null @@ -1,10 +0,0 @@ -package TripAmi.backend.app.util; - -public enum ProgramTheme { - ACTIVITY, - RESTAURANT, - TOURISM, - PHOTO, - K_POP, - SHOPPING, -} diff --git a/src/main/java/TripAmi/backend/app/util/service/exception/SendMailException.java b/src/main/java/TripAmi/backend/app/util/service/exception/SendMailException.java index acd6e6c..0bfa974 100644 --- a/src/main/java/TripAmi/backend/app/util/service/exception/SendMailException.java +++ b/src/main/java/TripAmi/backend/app/util/service/exception/SendMailException.java @@ -1,4 +1,4 @@ package TripAmi.backend.app.util.service.exception; -public class SendMailException extends RuntimeException{ +public class SendMailException extends RuntimeException { } diff --git a/src/main/java/TripAmi/backend/auth/security/JwtAuthenticationFilter.java b/src/main/java/TripAmi/backend/auth/security/JwtAuthenticationFilter.java index fe1576a..57e2573 100644 --- a/src/main/java/TripAmi/backend/auth/security/JwtAuthenticationFilter.java +++ b/src/main/java/TripAmi/backend/auth/security/JwtAuthenticationFilter.java @@ -74,7 +74,7 @@ protected void successfulAuthentication(HttpServletRequest request, Authentication authResult) throws IOException, ServletException { log.info("JwtAuthenticationFilter.successfulAuthentication"); Set roles = new HashSet<>(); - roles.add(Role.ROLE_ADMIN); + roles.add(Role.ROLE_MEMBER); CustomUserDetails customUserDetails = CustomUserDetails.of((String)authResult.getPrincipal(), "", roles); TokenDto tokenDto = jwtProvider.generateTokenDto(customUserDetails); String accessToken = tokenDto.accessToken(); diff --git a/src/main/java/TripAmi/backend/config/SecurityConfig.java b/src/main/java/TripAmi/backend/config/SecurityConfig.java index 899223a..b71692a 100644 --- a/src/main/java/TripAmi/backend/config/SecurityConfig.java +++ b/src/main/java/TripAmi/backend/config/SecurityConfig.java @@ -71,12 +71,6 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .and() .apply(new CustomFilterConfigurer()) .and() -// // 조건별로 요청 허용/제한 설정 -// .authorizeRequests() -// .antMatchers("/api/v1/join/**", "/api/v1/login", "/api/v1/refresh").permitAll() -// .antMatchers("/api/v1/admin/**").hasRole("ADMIN") -// .antMatchers("/api/v1/member/**", "/api/v1/program/**").hasAnyRole("MEMBER", "ADMIN") -// .anyRequest().denyAll() // //에러 핸들링 .exceptionHandling() .accessDeniedHandler(new AccessDeniedHandler() { @@ -100,7 +94,11 @@ public void commence(HttpServletRequest request, HttpServletResponse response, A } }) .and() - .authorizeHttpRequests(authorize -> authorize.anyRequest().permitAll()); + .authorizeRequests() + .antMatchers("/api/v1/join/**", "/api/v1/login", "/api/v1/refresh").permitAll() + .antMatchers("/api/v1/admin/**").hasRole("ADMIN") + .antMatchers("/api/v1/member/**", "/api/v1/program/**").hasAnyRole("MEMBER", "ADMIN") + .anyRequest().authenticated(); return http.build(); } @Bean diff --git a/src/main/java/TripAmi/backend/web/api/banner/BannerController.java b/src/main/java/TripAmi/backend/web/api/banner/BannerController.java new file mode 100644 index 0000000..4e4862d --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/banner/BannerController.java @@ -0,0 +1,54 @@ +package TripAmi.backend.web.api.banner; + +import TripAmi.backend.app.banner.service.BannerService; +import TripAmi.backend.web.api.banner.request.BannerCreateRequest; +import TripAmi.backend.web.api.banner.request.BannerUpdateRequest; +import TripAmi.backend.web.api.banner.response.BannerDto; +import TripAmi.backend.web.api.banner.response.BannerListResponse; +import TripAmi.backend.web.api.common.GenericResponse; +import TripAmi.backend.web.api.response.EmptyResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/v1/banners") +@RequiredArgsConstructor +public class BannerController { + private final BannerService bannerService; + + @PostMapping + public GenericResponse save(@RequestBody BannerCreateRequest request) { + bannerService.join(request.title(), request.imgUrl()); + return GenericResponse.ok(BannerDto.builder() + .title(request.title()) + .imgUrl(request.imgUrl()) + .build()); + } + + @GetMapping("/{id}") + public GenericResponse findById(@PathVariable Long id) { + return GenericResponse.ok(bannerService.findById(id)); + } + + @GetMapping + public GenericResponse findAll() { + List banners = bannerService.findBanners(); + BannerListResponse response = BannerListResponse.builder() + .banners(banners) + .build(); + return GenericResponse.ok(response); + } + + @PutMapping("/{id}") + public GenericResponse update(@PathVariable Long id, @RequestBody BannerUpdateRequest request) { + return GenericResponse.ok(bannerService.update(id, request.title(), request.imgUrl())); + } + + @DeleteMapping("/{id}") + public GenericResponse delete(@PathVariable Long id) { + bannerService.findById(id); + return GenericResponse.ok(); + } +} diff --git a/src/main/java/TripAmi/backend/web/api/banner/request/BannerCreateRequest.java b/src/main/java/TripAmi/backend/web/api/banner/request/BannerCreateRequest.java new file mode 100644 index 0000000..a5c891f --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/banner/request/BannerCreateRequest.java @@ -0,0 +1,9 @@ +package TripAmi.backend.web.api.banner.request; + +import lombok.Builder; + +public record BannerCreateRequest(String title, String imgUrl) { + @Builder + public BannerCreateRequest { + } +} diff --git a/src/main/java/TripAmi/backend/web/api/banner/request/BannerUpdateRequest.java b/src/main/java/TripAmi/backend/web/api/banner/request/BannerUpdateRequest.java new file mode 100644 index 0000000..bb9e134 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/banner/request/BannerUpdateRequest.java @@ -0,0 +1,9 @@ +package TripAmi.backend.web.api.banner.request; + +import lombok.Builder; + +public record BannerUpdateRequest(String title, String imgUrl) { + @Builder + public BannerUpdateRequest { + } +} diff --git a/src/main/java/TripAmi/backend/web/api/banner/response/BannerDto.java b/src/main/java/TripAmi/backend/web/api/banner/response/BannerDto.java new file mode 100644 index 0000000..bad2a9d --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/banner/response/BannerDto.java @@ -0,0 +1,11 @@ +package TripAmi.backend.web.api.banner.response; + + +import lombok.Builder; + +public record BannerDto(Long id, String title, String imgUrl) { + + @Builder + public BannerDto { + } +} diff --git a/src/main/java/TripAmi/backend/web/api/banner/response/BannerListResponse.java b/src/main/java/TripAmi/backend/web/api/banner/response/BannerListResponse.java new file mode 100644 index 0000000..2840f23 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/banner/response/BannerListResponse.java @@ -0,0 +1,12 @@ +package TripAmi.backend.web.api.banner.response; + +import lombok.Builder; + +import java.util.List; + +public record BannerListResponse(List banners) { + + @Builder + public BannerListResponse { + } +} diff --git a/src/main/java/TripAmi/backend/web/api/common/GenericResponse.java b/src/main/java/TripAmi/backend/web/api/common/GenericResponse.java index 0bdf8e0..7ffcd56 100644 --- a/src/main/java/TripAmi/backend/web/api/common/GenericResponse.java +++ b/src/main/java/TripAmi/backend/web/api/common/GenericResponse.java @@ -1,5 +1,6 @@ package TripAmi.backend.web.api.common; +import TripAmi.backend.web.api.program.response.ThemeListResponse; import lombok.Builder; import lombok.Getter; import org.springframework.http.HttpStatus; @@ -23,5 +24,4 @@ public static GenericResponse ok(T data) { public static GenericResponse ok() { return new GenericResponse<>(HttpStatus.OK.value(), null, HttpStatus.OK.getReasonPhrase()); } - } diff --git a/src/main/java/TripAmi/backend/web/api/faq/FaqController.java b/src/main/java/TripAmi/backend/web/api/faq/FaqController.java new file mode 100644 index 0000000..5a5bc71 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/faq/FaqController.java @@ -0,0 +1,50 @@ +package TripAmi.backend.web.api.faq; + +import TripAmi.backend.app.faq.service.FaqService; +import TripAmi.backend.web.api.common.GenericResponse; +import TripAmi.backend.web.api.faq.request.FaqCreateRequest; +import TripAmi.backend.web.api.faq.request.FaqUpdateRequest; +import TripAmi.backend.web.api.faq.response.FaqDto; +import TripAmi.backend.web.api.faq.response.FaqListResponse; +import TripAmi.backend.web.api.response.EmptyResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("api/v1/faqs") +@RequiredArgsConstructor +public class FaqController { + private final FaqService faqService; + + @PostMapping + public void save(@RequestBody FaqCreateRequest request) { + faqService.save(request.question(), request.answer()); + } + + @GetMapping + public GenericResponse findAll() { + List result = faqService.findFaqs(); + FaqListResponse response = FaqListResponse.builder() + .faqs(result) + .build(); + return GenericResponse.ok(response); + } + + @GetMapping("/{id}") + public GenericResponse findById(@PathVariable Long id) { + return GenericResponse.ok(faqService.findById(id)); + } + + @PostMapping("/{id}") + public GenericResponse update(@PathVariable Long id, @RequestBody FaqUpdateRequest request) { + return GenericResponse.ok(faqService.update(id, request.question(), request.answer())); + } + + @DeleteMapping("/{id}") + public GenericResponse delete(@PathVariable Long id) { + faqService.delete(id); + return GenericResponse.ok(); + } +} diff --git a/src/main/java/TripAmi/backend/web/api/faq/request/FaqCreateRequest.java b/src/main/java/TripAmi/backend/web/api/faq/request/FaqCreateRequest.java new file mode 100644 index 0000000..559ac24 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/faq/request/FaqCreateRequest.java @@ -0,0 +1,7 @@ +package TripAmi.backend.web.api.faq.request; + +public record FaqCreateRequest( + String question, + String answer +) { +} diff --git a/src/main/java/TripAmi/backend/web/api/faq/request/FaqUpdateRequest.java b/src/main/java/TripAmi/backend/web/api/faq/request/FaqUpdateRequest.java new file mode 100644 index 0000000..89fe0f8 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/faq/request/FaqUpdateRequest.java @@ -0,0 +1,9 @@ +package TripAmi.backend.web.api.faq.request; + +import lombok.Builder; + +public record FaqUpdateRequest(String question, String answer) { + @Builder + public FaqUpdateRequest { + } +} diff --git a/src/main/java/TripAmi/backend/web/api/faq/response/FaqDto.java b/src/main/java/TripAmi/backend/web/api/faq/response/FaqDto.java new file mode 100644 index 0000000..2aef2ed --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/faq/response/FaqDto.java @@ -0,0 +1,12 @@ +package TripAmi.backend.web.api.faq.response; + +import lombok.Builder; + +public record FaqDto( + String question, + String answer +) { + @Builder + public FaqDto { + } +} diff --git a/src/main/java/TripAmi/backend/web/api/faq/response/FaqListResponse.java b/src/main/java/TripAmi/backend/web/api/faq/response/FaqListResponse.java new file mode 100644 index 0000000..d665ede --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/faq/response/FaqListResponse.java @@ -0,0 +1,14 @@ +package TripAmi.backend.web.api.faq.response; + +import lombok.Builder; + +import java.util.List; + +public record FaqListResponse( + List faqs + +) { + @Builder + public FaqListResponse { + } +} diff --git a/src/main/java/TripAmi/backend/web/api/notification/NotificationController.java b/src/main/java/TripAmi/backend/web/api/notification/NotificationController.java new file mode 100644 index 0000000..8613a44 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/notification/NotificationController.java @@ -0,0 +1,19 @@ +package TripAmi.backend.web.api.notification; + +import TripAmi.backend.app.notification.service.NotificationService; +import TripAmi.backend.auth.authmember.domain.AuthMember; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +@RestController +@RequiredArgsConstructor +public class NotificationController { + private final NotificationService notificationService; + + public SseEmitter subscribe(@AuthenticationPrincipal AuthMember member, @RequestHeader(value = "Last-Event-ID", required = false, defaultValue = "") String lastEventId) { + return notificationService.subscribe(member.getId(), lastEventId); + } +} diff --git a/src/main/java/TripAmi/backend/web/api/program/ProgramController.java b/src/main/java/TripAmi/backend/web/api/program/ProgramController.java new file mode 100644 index 0000000..5a9c622 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/program/ProgramController.java @@ -0,0 +1,125 @@ +package TripAmi.backend.web.api.program; + + +import TripAmi.backend.app.product.ProgramTheme; +import TripAmi.backend.app.product.domain.Program; +import TripAmi.backend.app.product.service.ProgramService; +import TripAmi.backend.web.api.common.GenericResponse; +import TripAmi.backend.web.api.program.request.CreateProgramRequest; +import TripAmi.backend.web.api.program.response.*; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("api/v1/programs") +@RequiredArgsConstructor +public class ProgramController { + private final ProgramService programService; + + /** + * 프로그램 생성 url + * 필요 데이터를 가지고 메소드 호출시 생성 (return은 없습니다.) + * + * @param request 생성 DTO (CreateProgramRequest) + */ + @PostMapping + public void save(@RequestBody CreateProgramRequest request) { + programService.save(request); + } + + + + @GetMapping("/{id}") + public GenericResponse findById(@PathVariable Long id) { + Program program = programService.findById(id); + ProgramDto response = ProgramDto.builder() + .title(program.getTitle()) + .amiId(program.getAmiId()) + .content(program.getContent()) + .images(program.getImages()) + .price(program.getPrice()) + .theme(program.getTheme()) + .build(); + return GenericResponse.ok(response); + } + + /** + * ToDo 현재 일반 프로그램 조회와 디테일 조회의 차이가 없음 + * + * @param id program id + * @return 해당하는 id의 프로그램 정보 + */ + @GetMapping("/{id}/detail") + public GenericResponse findProgramDetail(@PathVariable Long id) { + Program program = programService.findDetailById(id); + ProgramDetailDto response = ProgramDetailDto.builder() + .title(program.getTitle()) + .amiId(program.getAmiId()) + .content(program.getContent()) + .images(program.getImages()) + .price(program.getPrice()) + .theme(program.getTheme()) + .build(); + + return GenericResponse.ok(response); + } + + /** + * 현재 Program들이 가지고 있는 Theme 전체 검색 + * 데이터가 있는 Theme만 불러와짐 + * + * @return themes + */ + + @GetMapping("/themes") + public GenericResponse findThemes() { + List result = Arrays.stream(ProgramTheme.values()) + .map(theme -> new ThemeDto(theme.toString())) + .collect(Collectors.toList()); + + ThemeListResponse response = new ThemeListResponse(result); + return GenericResponse.ok(response); + } + + /** + * Parameter로 받은 Theme에 해당하는 프로그램들을 조회 + * 없는 Theme를 받으면 Exception + * + * @param theme 테마이름 (String) + * @return 해당하는 Theme에 포함된 Program(s) + */ + @GetMapping("/{theme}") + public GenericResponse findProgramByTheme(@PathVariable String theme) { + ProgramTheme programTheme = ProgramTheme.valueOf(theme.toUpperCase()); + List result = programService.findByTheme(programTheme).stream() + .map(item -> ProgramDto.builder() + .title(item.getTitle()) + .theme(item.getTheme()) + .price(item.getPrice()) + .amiId(item.getAmiId()) + .content(item.getContent()) + .images(item.getImages()) + .build() + ).collect(Collectors.toList()); + + ProgramListResponse response = new ProgramListResponse(result); + return GenericResponse.ok(response); + } + + @GetMapping("/{id}/remain") + public GenericResponse getRemainingCount(@PathVariable Long id) { + Program program = programService.findById(id); + + // ToDo 전체 인원에서 조인한 인원 빼줘야됨 + return GenericResponse.ok(program.getTotalPeople()); + } + + /** + * ToDo + * 기간 별 조회 + */ +} diff --git a/src/main/java/TripAmi/backend/web/api/program/request/CreateProgramRequest.java b/src/main/java/TripAmi/backend/web/api/program/request/CreateProgramRequest.java new file mode 100644 index 0000000..8e232c1 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/program/request/CreateProgramRequest.java @@ -0,0 +1,21 @@ +package TripAmi.backend.web.api.program.request; + +import TripAmi.backend.app.product.ProgramTheme; +import TripAmi.backend.app.product.domain.Spot; + +import java.util.List; + +public record CreateProgramRequest( + Long amiId, + String title, + String content, + String profileImgUrl, + List images, + Integer price, + Integer totalPeople, + ProgramTheme theme, + List keyWords, + List spots, + String location +) { +} diff --git a/src/main/java/TripAmi/backend/web/api/program/request/CreateSpotRequest.java b/src/main/java/TripAmi/backend/web/api/program/request/CreateSpotRequest.java new file mode 100644 index 0000000..fd854bb --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/program/request/CreateSpotRequest.java @@ -0,0 +1,11 @@ +package TripAmi.backend.web.api.program.request; + +import java.time.LocalTime; + +public record CreateSpotRequest( + String title, + String imgUrl, + String content, + LocalTime requiredTime +) { +} diff --git a/src/main/java/TripAmi/backend/web/api/program/response/ProgramDetailDto.java b/src/main/java/TripAmi/backend/web/api/program/response/ProgramDetailDto.java new file mode 100644 index 0000000..7e3164b --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/program/response/ProgramDetailDto.java @@ -0,0 +1,18 @@ +package TripAmi.backend.web.api.program.response; + +import TripAmi.backend.app.product.ProgramTheme; +import lombok.Builder; + +import java.util.List; + +@Builder +public record ProgramDetailDto( + String title, + List images, + String content, + Integer price, + Long amiId, + ProgramTheme theme + +) { +} diff --git a/src/main/java/TripAmi/backend/web/api/program/response/ProgramDto.java b/src/main/java/TripAmi/backend/web/api/program/response/ProgramDto.java new file mode 100644 index 0000000..83f2e71 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/program/response/ProgramDto.java @@ -0,0 +1,17 @@ +package TripAmi.backend.web.api.program.response; + +import TripAmi.backend.app.product.ProgramTheme; +import lombok.Builder; + +import java.util.List; + +@Builder +public record ProgramDto( + String title, + List images, + String content, + Integer price, + Long amiId, + ProgramTheme theme +) { +} diff --git a/src/main/java/TripAmi/backend/web/api/program/response/ProgramListResponse.java b/src/main/java/TripAmi/backend/web/api/program/response/ProgramListResponse.java new file mode 100644 index 0000000..48dab46 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/program/response/ProgramListResponse.java @@ -0,0 +1,12 @@ +package TripAmi.backend.web.api.program.response; + +import TripAmi.backend.app.product.domain.Program; +import lombok.Builder; + +import java.util.List; + +public record ProgramListResponse(List programs) { + @Builder + public ProgramListResponse { + } +} diff --git a/src/main/java/TripAmi/backend/web/api/program/response/ThemeDto.java b/src/main/java/TripAmi/backend/web/api/program/response/ThemeDto.java new file mode 100644 index 0000000..d192437 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/program/response/ThemeDto.java @@ -0,0 +1,5 @@ +package TripAmi.backend.web.api.program.response; + +public record ThemeDto(String theme) { + +} diff --git a/src/main/java/TripAmi/backend/web/api/program/response/ThemeListResponse.java b/src/main/java/TripAmi/backend/web/api/program/response/ThemeListResponse.java new file mode 100644 index 0000000..de337f6 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/program/response/ThemeListResponse.java @@ -0,0 +1,11 @@ +package TripAmi.backend.web.api.program.response; + +import lombok.Builder; + +import java.util.List; + +public record ThemeListResponse(List themes) { + @Builder + public ThemeListResponse { + } +} diff --git a/src/main/java/TripAmi/backend/web/api/response/EmptyResponse.java b/src/main/java/TripAmi/backend/web/api/response/EmptyResponse.java new file mode 100644 index 0000000..3204fa9 --- /dev/null +++ b/src/main/java/TripAmi/backend/web/api/response/EmptyResponse.java @@ -0,0 +1,6 @@ +package TripAmi.backend.web.api.response; + +import lombok.Builder; + +public record EmptyResponse() { +} diff --git a/src/test/java/TripAmi/backend/app/product/service/ProgramServiceTest.java b/src/test/java/TripAmi/backend/app/product/service/ProgramServiceTest.java new file mode 100644 index 0000000..cc77291 --- /dev/null +++ b/src/test/java/TripAmi/backend/app/product/service/ProgramServiceTest.java @@ -0,0 +1,53 @@ +package TripAmi.backend.app.product.service; + +import TripAmi.backend.app.product.ProgramTheme; +import TripAmi.backend.app.product.domain.ProgramRepository; +import TripAmi.backend.web.api.program.request.CreateProgramRequest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.Collections; + +@SpringBootTest +public class ProgramServiceTest { + + @Mock + private ProgramRepository programRepository; + + @InjectMocks + private ProgramService programService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void create_program() { + // Arrange + CreateProgramRequest request = new CreateProgramRequest( + 1L, + "Program Title", + "Program Content", + "img_url", + Collections.singletonList("image1.jpg"), + 100, + 10, + ProgramTheme.ACTIVITY, + Collections.singletonList("keyword"), + Collections.emptyList(), // Assuming no spots for simplicity + "Location" + ); + +// // Act + programService.save(request); +// +// // Assert + Assertions.assertNotNull(programService.findByTitle("Program Title")); // Correct title + } +} \ No newline at end of file diff --git a/src/test/java/TripAmi/backend/web/api/member/MemberControllerTest.java b/src/test/java/TripAmi/backend/web/api/member/MemberControllerTest.java deleted file mode 100644 index 2fc7bdf..0000000 --- a/src/test/java/TripAmi/backend/web/api/member/MemberControllerTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package TripAmi.backend.web.api.member; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@SpringBootTest -@AutoConfigureMockMvc -class MemberControllerTest { - - @Autowired - private MockMvc mockMvc; - - @InjectMocks - private MemberController memberController; - - @BeforeEach - public void setup() { - mockMvc = MockMvcBuilders.standaloneSetup(memberController).build(); - } - - @Test - public void testGetMapping() throws Exception { - String testName = "TestUser"; - - mockMvc.perform(get("/api/v1/members") - .param("name", testName)) - .andExpect(status().isOk()); - - } -}