diff --git a/src/main/java/ceos/backend/domain/startup/StartupController.java b/src/main/java/ceos/backend/domain/startup/StartupController.java new file mode 100644 index 0000000..126bbd0 --- /dev/null +++ b/src/main/java/ceos/backend/domain/startup/StartupController.java @@ -0,0 +1,68 @@ +package ceos.backend.domain.startup; + +import ceos.backend.domain.startup.dto.request.StartupRequest; +import ceos.backend.domain.startup.dto.response.StartupResponse; +import ceos.backend.domain.startup.dto.response.StartupsResponse; +import ceos.backend.domain.startup.service.StartupService; +import ceos.backend.global.common.dto.AwsS3Url; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/start-ups") +@Tag(name = "Start-Up") +public class StartupController { + + private final StartupService startupService; + + @Operation(summary = "창업 리스트 추가") + @PostMapping + public void createStartup(@RequestBody @Valid StartupRequest startupRequest) { + log.info("창업 리스트 추가"); + startupService.createStartup(startupRequest); + } + + @Operation(summary = "창업 리스트 보기") + @GetMapping + public StartupsResponse getStartups( + @RequestParam("pageNum") Integer pageNum, @RequestParam("limit") Integer limit) { + log.info("창업 리스트 보기"); + return startupService.getStartups(pageNum, limit); + } + + @Operation(summary = "창업 리스트 상세 보기") + @GetMapping(value = "/{startupId}") + public StartupResponse getStartup(@PathVariable("startupId") Long startupId) { + log.info("창업 리스트 상세 보기"); + return startupService.getStartup(startupId); + } + + @Operation(summary = "창업 리스트 수정") + @PatchMapping(value = "/{startupId}") + public StartupResponse updateStartup(@PathVariable("startupId") Long startupId, + @RequestBody @Valid StartupRequest startupRequest) { + log.info("창업 리스트 수정"); + return startupService.updateStartup(startupId, startupRequest); + } + + @Operation(summary = "창업 리스트 삭제") + @DeleteMapping("/{startupId}") + public void deleteManagement(@PathVariable(name = "startupId") Long startupId) { + log.info("창업 리스트 삭제"); + startupService.deleteStartup(startupId); + } + + @Operation(summary = "서비스 이미지 url 생성하기") + @GetMapping("/image") + public AwsS3Url getImageUrl() { + log.info("서비스 이미지 url 생성하기"); + return startupService.getImageUrl(); + } + +} diff --git a/src/main/java/ceos/backend/domain/startup/domain/Startup.java b/src/main/java/ceos/backend/domain/startup/domain/Startup.java new file mode 100644 index 0000000..73e16c1 --- /dev/null +++ b/src/main/java/ceos/backend/domain/startup/domain/Startup.java @@ -0,0 +1,58 @@ +package ceos.backend.domain.startup.domain; + +import ceos.backend.domain.startup.dto.request.StartupRequest; +import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +public class Startup { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "startup_id") + private Long id; + + @NotBlank + private String serviceName; + + private String companyName; + + @NotBlank + private String imageUrl; + + @NotBlank + private String serviceUrl; + + @NotNull + private Integer generation; + + @NotBlank + private String founder; + + @Builder + public Startup(String serviceName, String companyName, String imageUrl, String serviceUrl, Integer generation, String founder) { + this.serviceName = serviceName; + this.companyName = companyName; + this.imageUrl = imageUrl; + this.serviceUrl = serviceUrl; + this.generation = generation; + this.founder = founder; + } + + public void update(StartupRequest startupRequest) { + this.serviceName = startupRequest.getServiceName(); + this.companyName = startupRequest.getCompanyName(); + this.imageUrl = startupRequest.getImageUrl(); + this.serviceUrl = startupRequest.getServiceUrl(); + this.generation = startupRequest.getGeneration(); + this.founder = startupRequest.getFounder(); + } + +} diff --git a/src/main/java/ceos/backend/domain/startup/dto/request/StartupRequest.java b/src/main/java/ceos/backend/domain/startup/dto/request/StartupRequest.java new file mode 100644 index 0000000..54779c2 --- /dev/null +++ b/src/main/java/ceos/backend/domain/startup/dto/request/StartupRequest.java @@ -0,0 +1,46 @@ +package ceos.backend.domain.startup.dto.request; + +import ceos.backend.domain.startup.domain.Startup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; + +@Getter +public class StartupRequest { + + @NotBlank + @Schema(defaultValue = "Repick",description = "서비스명") + private String serviceName; + + @Schema(defaultValue = "(주)Repick",description = "회사명(생략가능)") + private String companyName; + + @NotBlank + @Schema(description = "서비스 이미지 url") + private String imageUrl; + + @NotBlank + @Schema(description = "서비스 url") + private String serviceUrl; + + @NotNull + @Schema(defaultValue = "17", description = "창업자 활동 기수") + private Integer generation; + + @NotBlank + @Schema(defaultValue = "이도현", description = "창업자 이름") + private String founder; + + public Startup toEntity() { + return Startup.builder() + .serviceName(serviceName) + .companyName(companyName) + .imageUrl(imageUrl) + .serviceUrl(serviceUrl) + .generation(generation) + .founder(founder) + .build(); + } + +} diff --git a/src/main/java/ceos/backend/domain/startup/dto/response/StartupResponse.java b/src/main/java/ceos/backend/domain/startup/dto/response/StartupResponse.java new file mode 100644 index 0000000..51bf04b --- /dev/null +++ b/src/main/java/ceos/backend/domain/startup/dto/response/StartupResponse.java @@ -0,0 +1,47 @@ +package ceos.backend.domain.startup.dto.response; + +import ceos.backend.domain.startup.domain.Startup; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class StartupResponse { + + private Long startupId; + + private String serviceName; + + private String companyName; + + private String imageUrl; + + private String serviceUrl; + + private Integer generation; + + private String founder; + + @Builder + public StartupResponse(Long startupId, String serviceName, String companyName, String imageUrl, String serviceUrl, Integer generation, String founder) { + this.startupId = startupId; + this.serviceName = serviceName; + this.companyName = companyName; + this.imageUrl = imageUrl; + this.serviceUrl = serviceUrl; + this.generation = generation; + this.founder = founder; + } + + public static StartupResponse fromEntity(Startup startup) { + return StartupResponse.builder() + .startupId(startup.getId()) + .serviceName(startup.getServiceName()) + .companyName(startup.getCompanyName()) + .imageUrl(startup.getImageUrl()) + .serviceUrl(startup.getServiceUrl()) + .generation(startup.getGeneration()) + .founder(startup.getFounder()) + .build(); + } + +} diff --git a/src/main/java/ceos/backend/domain/startup/dto/response/StartupsResponse.java b/src/main/java/ceos/backend/domain/startup/dto/response/StartupsResponse.java new file mode 100644 index 0000000..c2ad7e4 --- /dev/null +++ b/src/main/java/ceos/backend/domain/startup/dto/response/StartupsResponse.java @@ -0,0 +1,30 @@ +package ceos.backend.domain.startup.dto.response; + +import ceos.backend.domain.startup.domain.Startup; +import ceos.backend.global.common.dto.PageInfo; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.data.domain.Page; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class StartupsResponse { + + public List startups; + public PageInfo pageInfo; + + public static StartupsResponse fromPageable(Page startups) { + return new StartupsResponse( + startups.map(StartupResponse::fromEntity).toList(), + PageInfo.of( + startups.getNumber(), + startups.getSize(), + startups.getTotalPages(), + startups.getTotalElements() + ) + ); + } + +} diff --git a/src/main/java/ceos/backend/domain/startup/exception/StartupErrorCode.java b/src/main/java/ceos/backend/domain/startup/exception/StartupErrorCode.java new file mode 100644 index 0000000..c439363 --- /dev/null +++ b/src/main/java/ceos/backend/domain/startup/exception/StartupErrorCode.java @@ -0,0 +1,26 @@ +package ceos.backend.domain.startup.exception; + +import ceos.backend.global.common.dto.ErrorReason; +import ceos.backend.global.error.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +@Getter +@AllArgsConstructor +public enum StartupErrorCode implements BaseErrorCode { + + STARTUP_NOT_FOUND(BAD_REQUEST, "STARTUP_404_1", "존재하지 않는 창업 서비스입니다."); + + private HttpStatus status; + private String code; + private String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status.value(), code, reason); + } + +} diff --git a/src/main/java/ceos/backend/domain/startup/exception/StartupNotFound.java b/src/main/java/ceos/backend/domain/startup/exception/StartupNotFound.java new file mode 100644 index 0000000..33ff73f --- /dev/null +++ b/src/main/java/ceos/backend/domain/startup/exception/StartupNotFound.java @@ -0,0 +1,14 @@ +package ceos.backend.domain.startup.exception; + + +import ceos.backend.global.error.BaseErrorException; + +public class StartupNotFound extends BaseErrorException { + + public static final StartupNotFound EXCEPTION = new StartupNotFound(); + + public StartupNotFound() { + super(StartupErrorCode.STARTUP_NOT_FOUND); + } + +} diff --git a/src/main/java/ceos/backend/domain/startup/repository/StartupRepository.java b/src/main/java/ceos/backend/domain/startup/repository/StartupRepository.java new file mode 100644 index 0000000..3e3e9e4 --- /dev/null +++ b/src/main/java/ceos/backend/domain/startup/repository/StartupRepository.java @@ -0,0 +1,8 @@ +package ceos.backend.domain.startup.repository; + +import ceos.backend.domain.startup.domain.Startup; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface StartupRepository extends JpaRepository { + +} diff --git a/src/main/java/ceos/backend/domain/startup/service/StartupService.java b/src/main/java/ceos/backend/domain/startup/service/StartupService.java new file mode 100644 index 0000000..30434dc --- /dev/null +++ b/src/main/java/ceos/backend/domain/startup/service/StartupService.java @@ -0,0 +1,72 @@ +package ceos.backend.domain.startup.service; + +import ceos.backend.domain.startup.domain.Startup; +import ceos.backend.domain.startup.dto.request.StartupRequest; +import ceos.backend.domain.startup.dto.response.StartupResponse; +import ceos.backend.domain.startup.dto.response.StartupsResponse; +import ceos.backend.domain.startup.exception.StartupNotFound; +import ceos.backend.domain.startup.repository.StartupRepository; +import ceos.backend.global.common.dto.AwsS3Url; +import ceos.backend.infra.s3.AwsS3UrlHandler; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class StartupService { + + private final StartupRepository startupRepository; + private final AwsS3UrlHandler awsS3UrlHandler; + + @Transactional + public void createStartup(StartupRequest startupRequest) { + startupRepository.save(startupRequest.toEntity()); + } + + @Transactional(readOnly = true) + public StartupsResponse getStartups(Integer pageNum, Integer limit) { + PageRequest pageRequest = PageRequest.of(pageNum, limit, Sort.by("id").descending()); + + Page startups = startupRepository.findAll(pageRequest); + + return StartupsResponse.fromPageable(startups); + } + + @Transactional(readOnly = true) + public StartupResponse getStartup(Long startupId) { + Startup startup = startupRepository.findById(startupId).orElseThrow( + () -> StartupNotFound.EXCEPTION + ); + + return StartupResponse.fromEntity(startup); + } + + @Transactional + public StartupResponse updateStartup(Long startupId, StartupRequest startupRequest) { + Startup startup = startupRepository.findById(startupId).orElseThrow( + () -> StartupNotFound.EXCEPTION + ); + + startup.update(startupRequest); + return StartupResponse.fromEntity(startup); + } + + @Transactional + public void deleteStartup(Long startupId) { + Startup startup = startupRepository.findById(startupId).orElseThrow( + () -> StartupNotFound.EXCEPTION + ); + + startupRepository.delete(startup); + } + + @Transactional(readOnly = true) + public AwsS3Url getImageUrl() { + return awsS3UrlHandler.handle("startups"); + } + +} diff --git a/src/main/java/ceos/backend/global/config/WebSecurityConfig.java b/src/main/java/ceos/backend/global/config/WebSecurityConfig.java index 725e7df..f9cd4f0 100644 --- a/src/main/java/ceos/backend/global/config/WebSecurityConfig.java +++ b/src/main/java/ceos/backend/global/config/WebSecurityConfig.java @@ -80,7 +80,8 @@ public class WebSecurityConfig { "/awards/**", "/managements/**", "/faq/**", - "/sponsors/**" + "/sponsors/**", + "/start-ups/**" }; private final String[] GetPermittedPatterns = { @@ -94,7 +95,8 @@ public class WebSecurityConfig { "/applications/question", "/applications/document", "/applications/final", - "/admin/reissue" + "/admin/reissue", + "/start-ups/**" }; private final String[] PostPermittedPatterns = {"/applications"};