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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@
import com.sku.refit.global.response.BaseResponse;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@RestController
@RequiredArgsConstructor
@Slf4j
public class CommentControllerImpl implements CommentController {

private final CommentService commentService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
@Service
@RequiredArgsConstructor
@Slf4j
@Transactional(readOnly = true)
public class CommentServiceImpl implements CommentService {

private final CommentRepository commentRepository;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (c) SKU 다시입을Lab
*/
package com.sku.refit.domain.exchange.controller;

import java.util.List;

import jakarta.validation.Valid;

import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;

import com.sku.refit.domain.exchange.dto.request.ExchangePostRequest;
import com.sku.refit.domain.exchange.dto.response.ExchangePostCardResponse;
import com.sku.refit.domain.exchange.dto.response.ExchangePostDetailResponse;
import com.sku.refit.global.page.response.PageResponse;
import com.sku.refit.global.response.BaseResponse;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.tags.Tag;

@Tag(name = "교환 게시글", description = "교환 게시글 관련 API")
@RequestMapping("/api/exchanges")
public interface ExchangeController {

@PostMapping(value = "/new", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "새 게시글 작성", description = "새 게시글을 작성합니다.")
ResponseEntity<BaseResponse<ExchangePostDetailResponse>> createExchangePost(
@Parameter(
description = "게시글 이미지 리스트",
content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA_VALUE))
@RequestPart(value = "imageList")
List<MultipartFile> imageList,
@Parameter(
description = "게시글 내용",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))
@RequestPart(value = "request")
@Valid
ExchangePostRequest request);

@GetMapping
@Operation(summary = "교환 게시글 목록(페이지) 조회 (위치 기반)")
ResponseEntity<BaseResponse<PageResponse<ExchangePostCardResponse>>> getExchangePostsByLocation(
@Parameter(description = "페이지 번호", example = "1") @RequestParam Integer pageNum,
@Parameter(description = "페이지 크기", example = "4") @RequestParam Integer pageSize,
@Parameter(description = "위도", example = "37.544018")
@RequestParam(defaultValue = "37.544018")
Double latitude,
@Parameter(description = "경도", example = "126.951592")
@RequestParam(defaultValue = "126.951592")
Double longitude);

@GetMapping("/{exchangePostId}")
@Operation(summary = "교환 게시글 단일 조회", description = "교환 게시글 ID로 단일 게시글을 조회합니다.")
ResponseEntity<BaseResponse<ExchangePostDetailResponse>> getExchangePost(
@Parameter(description = "교환 게시글 ID", example = "1") @PathVariable Long exchangePostId);

@PutMapping(value = "/{exchangePostId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "교환 게시글 수정", description = "특정 교환 게시글을 수정합니다.")
ResponseEntity<BaseResponse<ExchangePostDetailResponse>> updateExchangePost(
@Parameter(description = "교환 게시글 ID", example = "1") @PathVariable Long exchangePostId,
@Parameter(
description = "수정할 게시글 이미지 리스트",
content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA_VALUE))
@RequestPart(value = "imageList", required = false)
List<MultipartFile> imageList,
@Parameter(
description = "수정할 게시글 내용",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))
@RequestPart(value = "request")
@Valid
ExchangePostRequest request);

@DeleteMapping("/{exchangePostId}")
@Operation(summary = "교환 게시글 삭제", description = "특정 교환 게시글을 삭제합니다.")
ResponseEntity<BaseResponse<Void>> deleteExchangePost(
@Parameter(description = "교환 게시글 ID", example = "1") @PathVariable Long exchangePostId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (c) SKU 다시입을Lab
*/
package com.sku.refit.domain.exchange.controller;

import java.util.List;

import jakarta.validation.Valid;

import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.sku.refit.domain.exchange.dto.request.ExchangePostRequest;
import com.sku.refit.domain.exchange.dto.response.ExchangePostCardResponse;
import com.sku.refit.domain.exchange.dto.response.ExchangePostDetailResponse;
import com.sku.refit.domain.exchange.service.ExchangeService;
import com.sku.refit.global.exception.CustomException;
import com.sku.refit.global.page.exception.PageErrorStatus;
import com.sku.refit.global.page.response.PageResponse;
import com.sku.refit.global.response.BaseResponse;

import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
public class ExchangeControllerImpl implements ExchangeController {

private final ExchangeService exchangeService;

@Override
public ResponseEntity<BaseResponse<ExchangePostDetailResponse>> createExchangePost(
List<MultipartFile> imageList, @Valid ExchangePostRequest request) {

ExchangePostDetailResponse response = exchangeService.createExchangePost(imageList, request);

return ResponseEntity.ok(BaseResponse.success(response));
}

@Override
public ResponseEntity<BaseResponse<PageResponse<ExchangePostCardResponse>>>
getExchangePostsByLocation(
Integer pageNum, Integer pageSize, Double latitude, Double longitude) {

if (pageNum < 1) {
throw new CustomException(PageErrorStatus.PAGE_NOT_FOUND);
}
if (pageSize < 1) {
throw new CustomException(PageErrorStatus.PAGE_SIZE_ERROR);
}

Pageable pageable = PageRequest.of(pageNum - 1, pageSize);
PageResponse<ExchangePostCardResponse> exchangePostCardResponsePageResponse;

exchangePostCardResponsePageResponse =
exchangeService.getExchangePostsByLocation(pageable, latitude, longitude);

return ResponseEntity.ok(BaseResponse.success(exchangePostCardResponsePageResponse));
}

@Override
public ResponseEntity<BaseResponse<ExchangePostDetailResponse>> getExchangePost(
Long exchangePostId) {

ExchangePostDetailResponse response = exchangeService.getExchangePost(exchangePostId);

return ResponseEntity.ok(BaseResponse.success(response));
}

@Override
public ResponseEntity<BaseResponse<ExchangePostDetailResponse>> updateExchangePost(
Long exchangePostId, List<MultipartFile> imageList, @Valid ExchangePostRequest request) {

ExchangePostDetailResponse response =
exchangeService.updateExchangePost(exchangePostId, imageList, request);

return ResponseEntity.ok(BaseResponse.success(response));
}

@Override
public ResponseEntity<BaseResponse<Void>> deleteExchangePost(Long exchangePostId) {

exchangeService.deleteExchangePost(exchangePostId);

return ResponseEntity.ok(BaseResponse.success(null));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) SKU 다시입을Lab
*/
package com.sku.refit.domain.exchange.dto.request;

import java.util.List;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;

import com.sku.refit.domain.exchange.entity.ClothSize;
import com.sku.refit.domain.exchange.entity.ClothStatus;
import com.sku.refit.domain.exchange.entity.ExchangeCategory;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(title = "ExchangePostRequest DTO", description = "새 교환 게시물 등록을 위한 데이터 전송")
public class ExchangePostRequest {

@NotBlank(message = "교환 게시글 제목은 필수입니다.")
@Schema(description = "교환 게시글 제목", example = "오버핏 흰색 셔츠")
private String title;

@NotNull(message = "교환 카테고리는 필수입니다.") @Schema(description = "교환 카테고리", example = "OUTER")
private ExchangeCategory exchangeCategory;

@NotNull(message = "옷 상태는 필수입니다.") @Schema(description = "옷 상태", example = "GOOD")
private ClothStatus clothStatus;

@NotNull(message = "옷 사이즈는 필수입니다.") @Schema(description = "옷 사이즈", example = "M")
private ClothSize clothSize;

@NotBlank(message = "교환 게시글 설명은 필수입니다.")
@Schema(description = "교환게시글 설명", example = "아이템에 대한 상세 설명을 작성해주세요.")
private String description;

@NotEmpty(message = "교환 선호 카테고리는 필수입니다.")
@Schema(description = "선호 카테고리")
private List<ExchangeCategory> preferCategoryList;

@NotBlank(message = "교환 희망 스팟은 필수입니다.")
@Schema(description = "교환 희망 스팟", example = "서울역")
private String exchangeSpot;

@NotNull(message = "교환 희망 스팟 위도는 필수입니다.") @Schema(description = "교환 희망 스팟 위도", example = "37.544018")
private Double spotLatitude;

@NotNull(message = "교환 희망 스팟 경도는 필수입니다.") @Schema(description = "교환 희망 스팟 경도", example = "126.951592")
private Double spotLongitude;

@NotBlank(message = "교환 편지는 필수입니다.")
@Schema(description = "교환 편지", example = "이젠 안녕! 청바지야.")
private String letter;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) SKU 다시입을Lab
*/
package com.sku.refit.domain.exchange.dto.response;

import com.sku.refit.domain.exchange.entity.ExchangeCategory;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
@Schema(title = "ExchangePostCardResponse DTO", description = "교환글 카드 형식 응답 반환")
public class ExchangePostCardResponse {

@Schema(description = "썸네일 이미지 URL")
private String thumbnailImageUrl;

@Schema(description = "교환 카테고리", example = "PANTS")
private ExchangeCategory category;

@Schema(description = "교환 제목", example = "스판 여성용 빈티지 청바지")
private String title;

@Schema(description = "교환 희망 스팟", example = "서울역")
private String exchangeSpot;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) SKU 다시입을Lab
*/
package com.sku.refit.domain.exchange.dto.response;

import java.time.LocalDateTime;
import java.util.List;

import com.sku.refit.domain.exchange.entity.ClothSize;
import com.sku.refit.domain.exchange.entity.ClothStatus;
import com.sku.refit.domain.exchange.entity.ExchangeCategory;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
@Schema(title = "ExchangePostDetailResponse DTO", description = "교환글 상세 정보 응답 반환")
public class ExchangePostDetailResponse {

@Schema(description = "교환 게시글 식별자", example = "1")
private Long exchangePostId;

@Schema(description = "게시글 작성자", example = "김다입")
private String nickname;

@Schema(description = "이미지 URL 리스트")
private List<String> imageUrlList;

@Schema(description = "교환 카테고리", example = "PANTS")
private ExchangeCategory category;

@Schema(description = "교환 제목", example = "스판 여성용 빈티지 청바지")
private String title;

@Schema(description = "옷 사이즈", example = "M")
private ClothSize size;

@Schema(description = "옷 상태", example = "GOOD")
private ClothStatus status;

@Schema(description = "선호 카테고리")
private List<ExchangeCategory> preferCategoryList;

@Schema(description = "교환 희망 스팟", example = "서울역")
private String exchangeSpot;

@Schema(description = "교환 희망 스팟 위도", example = "37.544018")
private Double spotLatitude;

@Schema(description = "교환 희망 스팟 경도", example = "126.951592")
private Double spotLongitude;

@Schema(description = "작성자 본인 여부", example = "true")
private Boolean isAuthor;

@Schema(description = "게시글 작성 시간", example = "2025-12-03T14:37:17")
private LocalDateTime createdAt;
}
32 changes: 32 additions & 0 deletions src/main/java/com/sku/refit/domain/exchange/entity/ClothSize.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) SKU 다시입을Lab
*/
package com.sku.refit.domain.exchange.entity;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
@Schema(description = "옷 사이즈 Enum")
public enum ClothSize {
@Schema(description = "FREE")
FREE,
@Schema(description = "2XS")
XS2,
@Schema(description = "XS")
XS,
@Schema(description = "S")
S,
@Schema(description = "M")
M,
@Schema(description = "L")
L,
@Schema(description = "XL")
XL,
@Schema(description = "XL2")
XL2,
@Schema(description = "XL3")
XL3;
}
Loading