diff --git a/recruitment-service/src/main/java/com/example/recruitment/config/RestTemplateConfig.java b/recruitment-service/src/main/java/com/example/recruitment/config/RestTemplateConfig.java new file mode 100644 index 0000000..c8d3e52 --- /dev/null +++ b/recruitment-service/src/main/java/com/example/recruitment/config/RestTemplateConfig.java @@ -0,0 +1,13 @@ +package com.example.recruitment.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java b/recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java index 9e15016..2e14d55 100644 --- a/recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java +++ b/recruitment-service/src/main/java/com/example/recruitment/controller/RecruitmentController.java @@ -1,21 +1,29 @@ package com.example.recruitment.controller; +import com.example.recruitment.service.UserClient; +import com.example.recruitment.common.ApiResponse; import com.example.recruitment.dto.RecruitmentDetailDto; import com.example.recruitment.dto.RecruitmentRequestDto; +import com.example.recruitment.dto.RecruitmentResponseDto; import com.example.recruitment.dto.order.OrderRequestDto; import com.example.recruitment.entity.Recruitment; import com.example.recruitment.entity.RecruitmentParticipant; -import com.example.recruitment.entity.User; +import com.example.recruitment.entity.Store; +import com.example.recruitment.exception.CustomException; +import com.example.recruitment.exception.ErrorCode; import com.example.recruitment.repository.RecruitmentParticipantRepository; import com.example.recruitment.repository.RecruitmentRepository; +import com.example.recruitment.repository.StoreRepository; import com.example.recruitment.service.RecruitmentService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.time.LocalDateTime; import java.util.List; +import java.util.Map; @RestController @RequiredArgsConstructor @@ -25,78 +33,90 @@ public class RecruitmentController { private final RecruitmentService recruitmentService; private final RecruitmentRepository recruitmentRepository; private final RecruitmentParticipantRepository participantRepository; + private final StoreRepository storeRepository; + private final UserClient userClient; // 유저 서비스 연동용 Feign Client - //모집글 생성 (Order 서버에 주문 생성 포함) @PostMapping - public ResponseEntity createRecruitment(@Valid @RequestBody RecruitmentRequestDto dto) { - recruitmentService.createRecruitment(dto); - return ResponseEntity.ok("모집글 생성 완료"); + public ResponseEntity>> createRecruitment(@Valid @RequestBody RecruitmentRequestDto dto) { + Long recruitmentId = recruitmentService.createRecruitment(dto); + return ResponseEntity + .status(HttpStatus.CREATED) + .body(ApiResponse.created(Map.of("recruitmentId", recruitmentId), "모집글 생성 완료")); } - //모집글 참여 (Order 서버에 주문 생성 포함) @PostMapping("/{recruitmentId}/join") - public ResponseEntity joinRecruitment(@PathVariable Long recruitmentId, - @RequestParam Long userId, - @RequestBody OrderRequestDto orderRequestDto) { + public ResponseEntity> joinRecruitment(@PathVariable Long recruitmentId, + @RequestParam Long userId, + @Valid @RequestBody OrderRequestDto orderRequestDto) { recruitmentService.joinRecruitment(recruitmentId, userId, orderRequestDto); - return ResponseEntity.ok("모집글 참여 완료"); + return ResponseEntity.ok(ApiResponse.ok(null, "모집글 참여 완료")); } - // 모집글 전체 조회 @GetMapping - public List getAll() { - return recruitmentRepository.findAll(); + public ResponseEntity>> getAll() { + List recruitments = recruitmentRepository.findAll(); + List response = recruitments.stream() + .map(RecruitmentResponseDto::new) + .toList(); + return ResponseEntity.ok(ApiResponse.ok(response, "모든 모집글 조회 성공")); } - // 상태별 조회 @GetMapping(params = "status") - public List getByStatus(@RequestParam String status) { - return recruitmentRepository.findByStatus(status); + public ResponseEntity>> getByStatus(@RequestParam String status) { + List recruitments = recruitmentRepository.findByStatus(status); + List response = recruitments.stream() + .map(RecruitmentResponseDto::new) + .toList(); + return ResponseEntity.ok(ApiResponse.ok(response, "상태별 모집글 조회 성공")); } - // 모집글 상세 조회 @GetMapping("/{recruitmentId}") - public ResponseEntity getRecruitmentDetail(@PathVariable Long recruitmentId) { - Recruitment recruitment = recruitmentRepository.findById(recruitmentId).orElseThrow(); + public ResponseEntity> getRecruitmentDetail(@PathVariable Long recruitmentId) { + Recruitment recruitment = recruitmentRepository.findById(recruitmentId) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); + + // ✅ 작성자 정보 → 유저 서비스에서 가져옴 + RecruitmentDetailDto.UserDto writer = userClient.getUserById(recruitment.getUser().getId()); - List participants = - participantRepository.findByRecruitmentId(recruitmentId); - List participantUsers = participants.stream() - .map(RecruitmentParticipant::getUser) + // ✅ 참여자 정보 + List participants = participantRepository.findByRecruitmentId(recruitmentId); + List participantUsers = participants.stream() + .map(p -> userClient.getUserById(p.getUser().getId())) .toList(); - RecruitmentDetailDto dto = new RecruitmentDetailDto(); - dto.setId(recruitment.getId()); - dto.setTitle(recruitment.getTitle()); - dto.setDescription(recruitment.getDescription()); - dto.setStatus(recruitment.getStatus()); - dto.setDeadlineTime(recruitment.getDeadlineTime()); - dto.setUser(recruitment.getUser()); - dto.setStore(recruitment.getStore()); - dto.setParticipants(participantUsers); - - return ResponseEntity.ok(dto); + List orderIds = participants.stream() + .map(RecruitmentParticipant::getOrderId) + .toList(); + + RecruitmentDetailDto dto = new RecruitmentDetailDto(recruitment, writer, participantUsers, orderIds); + + return ResponseEntity.ok(ApiResponse.ok(dto, "모집 상세 조회 성공")); } - // 유저가 만든 모집글 @GetMapping("/user/{userId}/created-recruitments") - public List getRecruitmentsCreatedByUser(@PathVariable Long userId) { - return recruitmentRepository.findByUserId(userId); + public ResponseEntity>> getRecruitmentsCreatedByUser(@PathVariable Long userId) { + List list = recruitmentRepository.findByUserId(userId); + List response = list.stream() + .map(RecruitmentResponseDto::new) + .toList(); + return ResponseEntity.ok(ApiResponse.ok(response, "작성한 모집글 조회 성공")); } - // 유저가 참여한 모집글 @GetMapping("/user/{userId}/joined-recruitments") - public List getRecruitmentsJoinedByUser(@PathVariable Long userId) { + public ResponseEntity>> getRecruitmentsJoinedByUser(@PathVariable Long userId) { List participantList = participantRepository.findByUserId(userId); - return participantList.stream() + List response = participantList.stream() .map(RecruitmentParticipant::getRecruitment) + .map(RecruitmentResponseDto::new) .toList(); + return ResponseEntity.ok(ApiResponse.ok(response, "참여한 모집글 조회 성공")); } - // 모집 상태 업데이트 @PatchMapping("/{recruitmentId}/status") - public ResponseEntity updateRecruitmentStatus(@PathVariable Long recruitmentId) { - Recruitment recruitment = recruitmentRepository.findById(recruitmentId).orElseThrow(); + public ResponseEntity> updateRecruitmentStatus(@PathVariable Long recruitmentId) { + Recruitment recruitment = recruitmentRepository.findById(recruitmentId) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); + LocalDateTime now = LocalDateTime.now(); long participantCount = participantRepository.countByRecruitmentId(recruitmentId); @@ -107,51 +127,57 @@ public ResponseEntity updateRecruitmentStatus(@PathVariable Long recruitmentI recruitment.setStatus("FAILED"); } recruitmentRepository.save(recruitment); - return ResponseEntity.ok("상태가 " + recruitment.getStatus() + "로 변경되었습니다."); + return ResponseEntity.ok(ApiResponse.ok(null, "상태가 " + recruitment.getStatus() + "로 변경되었습니다.")); } else { - return ResponseEntity.ok("아직 마감 시간이 지나지 않았습니다."); + return ResponseEntity.ok(ApiResponse.ok(null, "아직 마감 시간이 지나지 않았습니다.")); } } - // 주문 수락 상태 변경 @PatchMapping("/{recruitmentId}/accept") - public ResponseEntity acceptRecruitment(@PathVariable Long recruitmentId) { - Recruitment recruitment = recruitmentRepository.findById(recruitmentId).orElseThrow(); + public ResponseEntity> acceptRecruitment(@PathVariable Long recruitmentId) { + Recruitment recruitment = recruitmentRepository.findById(recruitmentId) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); + if (!"CONFIRMED".equals(recruitment.getStatus())) { - return ResponseEntity.badRequest().body("주문 수락은 CONFIRMED 상태에서만 가능합니다."); + return ResponseEntity.badRequest() + .body(ApiResponse.fail(4001, "주문 수락은 CONFIRMED 상태에서만 가능합니다.", HttpStatus.BAD_REQUEST)); } recruitment.setStatus("ACCEPTED"); recruitmentRepository.save(recruitment); - return ResponseEntity.ok("상태가 ACCEPTED로 변경되었습니다."); + return ResponseEntity.ok(ApiResponse.ok(null, "상태가 ACCEPTED로 변경되었습니다.")); } - // 배달 완료 상태 변경 @PatchMapping("/{recruitmentId}/deliver") - public ResponseEntity completeDelivery(@PathVariable Long recruitmentId) { - Recruitment recruitment = recruitmentRepository.findById(recruitmentId).orElseThrow(); + public ResponseEntity> completeDelivery(@PathVariable Long recruitmentId) { + Recruitment recruitment = recruitmentRepository.findById(recruitmentId) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); + if (!"ACCEPTED".equals(recruitment.getStatus())) { - return ResponseEntity.badRequest().body("배달 완료는 ACCEPTED 상태에서만 가능합니다."); + return ResponseEntity.badRequest() + .body(ApiResponse.fail(4002, "배달 완료는 ACCEPTED 상태에서만 가능합니다.", HttpStatus.BAD_REQUEST)); } recruitment.setStatus("DELIVERED"); recruitmentRepository.save(recruitment); - return ResponseEntity.ok("상태가 DELIVERED로 변경되었습니다."); + return ResponseEntity.ok(ApiResponse.ok(null, "상태가 DELIVERED로 변경되었습니다.")); } - // 모집글 삭제 @DeleteMapping("/{recruitmentId}") - public ResponseEntity deleteRecruitment(@PathVariable Long recruitmentId) { - Recruitment recruitment = recruitmentRepository.findById(recruitmentId).orElseThrow(); + public ResponseEntity> deleteRecruitment(@PathVariable Long recruitmentId) { + Recruitment recruitment = recruitmentRepository.findById(recruitmentId) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); recruitmentRepository.delete(recruitment); - return ResponseEntity.ok("모집글이 삭제되었습니다."); + return ResponseEntity.ok(ApiResponse.ok(null, "모집글이 삭제되었습니다.")); } - // 모집글 수정 @PutMapping("/{recruitmentId}") - public ResponseEntity updateRecruitment(@PathVariable Long recruitmentId, - @Valid @RequestBody RecruitmentRequestDto dto) { - Recruitment recruitment = recruitmentRepository.findById(recruitmentId).orElseThrow(); + public ResponseEntity> updateRecruitment(@PathVariable Long recruitmentId, + @Valid @RequestBody RecruitmentRequestDto dto) { + Recruitment recruitment = recruitmentRepository.findById(recruitmentId) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); + if (!recruitment.getUser().getId().equals(dto.getUserId())) { - return ResponseEntity.status(403).body("작성자만 수정할 수 있습니다."); + return ResponseEntity.status(HttpStatus.FORBIDDEN) + .body(ApiResponse.fail(4031, "작성자만 수정할 수 있습니다.", HttpStatus.FORBIDDEN)); } recruitment.setTitle(dto.getTitle()); @@ -159,10 +185,12 @@ public ResponseEntity updateRecruitment(@PathVariable Long recruitmentId, recruitment.setDeadlineTime(dto.getDeadlineTime()); if (dto.getStoreId() != null) { - recruitment.setStore(recruitment.getStore()); + Store store = storeRepository.findById(dto.getStoreId()) + .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND)); + recruitment.setStore(store); } recruitmentRepository.save(recruitment); - return ResponseEntity.ok("모집글이 수정되었습니다."); + return ResponseEntity.ok(ApiResponse.ok(null, "모집글이 수정되었습니다.")); } } diff --git a/recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentDetailDto.java b/recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentDetailDto.java index 258a3d3..ed7d6bf 100644 --- a/recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentDetailDto.java +++ b/recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentDetailDto.java @@ -1,21 +1,72 @@ package com.example.recruitment.dto; +import com.example.recruitment.entity.Recruitment; import com.example.recruitment.entity.Store; -import com.example.recruitment.entity.User; import lombok.Getter; import lombok.Setter; import java.time.LocalDateTime; import java.util.List; -@Getter @Setter +@Getter +@Setter public class RecruitmentDetailDto { + private Long id; private String title; private String description; private String status; private LocalDateTime deadlineTime; - private User user; - private Store store; - private List participants; + + private UserDto user; + private StoreDto store; + private List participants; + private List orderIds; + + public RecruitmentDetailDto( + Recruitment recruitment, + UserDto writer, + List participantUsers, + List orderIds + ) { + this.id = recruitment.getId(); + this.title = recruitment.getTitle(); + this.description = recruitment.getDescription(); + this.status = recruitment.getStatus(); + this.deadlineTime = recruitment.getDeadlineTime(); + this.user = writer; // ✅ 유저 서비스로부터 가져온 작성자 정보 + this.store = new StoreDto(recruitment.getStore()); + this.participants = participantUsers; + this.orderIds = orderIds; + } + + @Getter + @Setter + public static class UserDto { + private Long id; + private String name; + private String email; + + public UserDto(Long id, String name, String email) { + this.id = id; + this.name = name; + this.email = email; + } + } + + @Getter + @Setter + public static class StoreDto { + private Long id; + private String name; + private String category; + private String location; + + public StoreDto(Store store) { + this.id = store.getId(); + this.name = store.getName(); + this.category = store.getCategory(); + this.location = store.getLocation(); + } + } } diff --git a/recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentRequestDto.java b/recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentRequestDto.java index a97aa2c..fdbc393 100644 --- a/recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentRequestDto.java +++ b/recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentRequestDto.java @@ -33,9 +33,11 @@ public class RecruitmentRequestDto { private String category; // 주문용 메뉴 정보 + @NotNull(message = "메뉴 정보는 필수입니다.") + @Size(min = 1, message = "최소 하나 이상의 메뉴를 선택해야 합니다.") private List menus; - //OrderRequestDto 변환 메서드 + // OrderRequestDto 변환 메서드 public OrderRequestDto toOrderRequestDto() { OrderRequestDto dto = new OrderRequestDto(); dto.setUserId(this.userId); diff --git a/recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentResponseDto.java b/recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentResponseDto.java new file mode 100644 index 0000000..fc492c2 --- /dev/null +++ b/recruitment-service/src/main/java/com/example/recruitment/dto/RecruitmentResponseDto.java @@ -0,0 +1,63 @@ +package com.example.recruitment.dto; + +import com.example.recruitment.entity.Recruitment; +import com.example.recruitment.entity.Store; +import com.example.recruitment.entity.User; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class RecruitmentResponseDto { + + private Long id; + private String title; + private String description; + private String status; + private LocalDateTime deadlineTime; + + private UserDto user; + private StoreDto store; + + public RecruitmentResponseDto(Recruitment recruitment) { + this.id = recruitment.getId(); + this.title = recruitment.getTitle(); + this.description = recruitment.getDescription(); + this.status = recruitment.getStatus(); + this.deadlineTime = recruitment.getDeadlineTime(); + this.user = new UserDto(recruitment.getUser()); + this.store = new StoreDto(recruitment.getStore()); + } + + @Getter + @Setter + public static class UserDto { + private Long id; + private String name; + private String email; + + public UserDto(User user) { + this.id = user.getId(); + this.name = user.getName(); + this.email = user.getEmail(); + } + } + + @Getter + @Setter + public static class StoreDto { + private Long id; + private String name; + private String category; + private String location; + + public StoreDto(Store store) { + this.id = store.getId(); + this.name = store.getName(); + this.category = store.getCategory(); + this.location = store.getLocation(); + } + } +} diff --git a/recruitment-service/src/main/java/com/example/recruitment/entity/Recruitment.java b/recruitment-service/src/main/java/com/example/recruitment/entity/Recruitment.java index 6c1b1ff..ccc82a6 100644 --- a/recruitment-service/src/main/java/com/example/recruitment/entity/Recruitment.java +++ b/recruitment-service/src/main/java/com/example/recruitment/entity/Recruitment.java @@ -8,42 +8,55 @@ import java.util.List; @Entity -@Getter @Setter +@Getter +@Setter @NoArgsConstructor @AllArgsConstructor public class Recruitment { - @Id @GeneratedValue + @Id + @GeneratedValue private Long id; - @ManyToOne + // 작성자 (필수) + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) private User user; - @ManyToOne + // 가게 (필수) + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "store_id", nullable = false) private Store store; + // 참여자 목록 (양방향 연관 관계) @OneToMany(mappedBy = "recruitment", cascade = CascadeType.ALL, orphanRemoval = true) - private List participants; + private List participants = new ArrayList<>(); + // 모집글 정보 + @Column(nullable = false) private String title; + + @Column(nullable = false) private String description; - private String status; // RECRUITING, CONFIRMED 등 + @Column(nullable = false) + private String status; // e.g., RECRUITING, CONFIRMED, FAILED + @Column(nullable = false) private LocalDateTime deadlineTime; - //카테고리 추가가 @Column(nullable = false) private String category; + // 대표 주문 ID (모집자 주문) @Column(name = "order_id") private Long orderId; + // 참여자 주문 ID 목록 @ElementCollection private List orderIds = new ArrayList<>(); public void addOrderId(Long orderId) { this.orderIds.add(orderId); -} - + } } diff --git a/recruitment-service/src/main/java/com/example/recruitment/entity/RecruitmentParticipant.java b/recruitment-service/src/main/java/com/example/recruitment/entity/RecruitmentParticipant.java index 23b03cd..b90197b 100644 --- a/recruitment-service/src/main/java/com/example/recruitment/entity/RecruitmentParticipant.java +++ b/recruitment-service/src/main/java/com/example/recruitment/entity/RecruitmentParticipant.java @@ -6,7 +6,8 @@ import java.time.LocalDateTime; @Entity -@Getter @Setter +@Getter +@Setter @NoArgsConstructor @AllArgsConstructor public class RecruitmentParticipant { @@ -15,19 +16,29 @@ public class RecruitmentParticipant { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - - @ManyToOne + // 참여자 (필수) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private User user; - @ManyToOne + // 모집글 (필수) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "recruitment_id", nullable = false) private Recruitment recruitment; + // 참여 시간 (필수) @Column(name = "joined_at", nullable = false) private LocalDateTime joinedAt; + // 주문 ID (선택적으로 null 허용) @Column(name = "order_id") private Long orderId; -} + // 참여 시점 자동 설정 (선택) + @PrePersist + protected void onCreate() { + if (this.joinedAt == null) { + this.joinedAt = LocalDateTime.now(); + } + } +} diff --git a/recruitment-service/src/main/java/com/example/recruitment/entity/User.java b/recruitment-service/src/main/java/com/example/recruitment/entity/User.java index b04e733..4fdee5d 100644 --- a/recruitment-service/src/main/java/com/example/recruitment/entity/User.java +++ b/recruitment-service/src/main/java/com/example/recruitment/entity/User.java @@ -1,17 +1,25 @@ package com.example.recruitment.entity; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import jakarta.persistence.*; import lombok.*; @Entity -@Getter @Setter +@Table(name = "`user`") // ← 백틱으로 감싸서 예약어 충돌 방지 +@Getter +@Setter @NoArgsConstructor @AllArgsConstructor +@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) // ← 직렬화 에러 방지 public class User { - @Id @GeneratedValue + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(nullable = false) private String name; + + @Column(nullable = false, unique = true) private String email; } diff --git a/recruitment-service/src/main/java/com/example/recruitment/exception/GlobalExceptionHandler.java b/recruitment-service/src/main/java/com/example/recruitment/exception/GlobalExceptionHandler.java index dd5a88a..d3e0aca 100644 --- a/recruitment-service/src/main/java/com/example/recruitment/exception/GlobalExceptionHandler.java +++ b/recruitment-service/src/main/java/com/example/recruitment/exception/GlobalExceptionHandler.java @@ -3,6 +3,7 @@ import com.example.recruitment.common.ApiResponse; import com.example.recruitment.common.FieldErrorDetail; import jakarta.servlet.http.HttpServletRequest; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -13,6 +14,7 @@ @RestControllerAdvice public class GlobalExceptionHandler { + // 커스텀 예외 처리 @ExceptionHandler(CustomException.class) public ResponseEntity> handleCustomException(CustomException e) { ErrorCode ec = e.getErrorCode(); @@ -34,14 +36,23 @@ public ResponseEntity> handleValidationException(MethodArgum .body(ApiResponse.validationFail(errorList)); } + // ❗ IllegalArgumentException 처리 추가 + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity> handleIllegalArgument(IllegalArgumentException e) { + return ResponseEntity + .badRequest() + .body(ApiResponse.fail(4000, e.getMessage(), HttpStatus.BAD_REQUEST)); + } + // 그 외 모든 예외 처리 @ExceptionHandler(Exception.class) public ResponseEntity> handleGeneralException(Exception e, HttpServletRequest request) throws Exception { String path = request.getRequestURI(); + // Swagger 요청은 무시 if (path != null && (path.contains("/v3/api-docs") || path.contains("/swagger"))) { - throw e; // Swagger 요청은 예외 처리하지 않고 Spring에게 맡김 -} + throw e; + } return ResponseEntity .status(ErrorCode.INTERNAL_ERROR.getStatus()) diff --git a/recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java b/recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java index 0921560..a0d55ec 100644 --- a/recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java +++ b/recruitment-service/src/main/java/com/example/recruitment/service/OrderClient.java @@ -12,29 +12,42 @@ public class OrderClient { private final RestTemplate restTemplate = new RestTemplate(); - //직접 값 선언 + // 직접 값 선언 private final String serverUrl = "http://54.66.149.225:8100"; private final String path = "/api/v1/orders"; public Long createOrder(OrderRequestDto requestDto) { String fullUrl = serverUrl + path; - ResponseEntity response = restTemplate.postForEntity(fullUrl, requestDto, Map.class); + try { + ResponseEntity response = restTemplate.postForEntity(fullUrl, requestDto, Map.class); - if (response.getStatusCode().is2xxSuccessful()) { - Map responseBody = response.getBody(); + if (response.getStatusCode().is2xxSuccessful()) { + Map responseBody = response.getBody(); + + if (responseBody == null || !responseBody.containsKey("data")) { + throw new RuntimeException("Order 서버 응답에 'data' 항목이 없음"); + } - if (responseBody != null && responseBody.containsKey("data")) { Map data = (Map) responseBody.get("data"); + if (data == null || !data.containsKey("orderId")) { + throw new RuntimeException("Order 서버 응답 'data'에 'orderId'가 없음"); + } - if (data != null && data.containsKey("orderId")) { - return Long.valueOf(data.get("orderId").toString()); + Object orderIdObj = data.get("orderId"); + try { + return Long.valueOf(orderIdObj.toString()); + } catch (NumberFormatException e) { + throw new RuntimeException("orderId를 Long으로 변환 실패: " + orderIdObj); } - } - throw new RuntimeException("응답에 orderId 없음"); + } else { + throw new RuntimeException("Order 서버 응답 실패: " + response.getStatusCode()); + } + } catch (Exception e) { + // 서버 로깅 시스템 있다면 여기에 log.error(...) 사용 권장 + System.err.println("Order 서버 호출 중 예외 발생: " + e.getMessage()); + throw new RuntimeException("Order 서버 호출 실패", e); } - - throw new RuntimeException("주문 생성 실패: " + response.getStatusCode()); } } diff --git a/recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java b/recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java index 7b96fc9..92d7fe1 100644 --- a/recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java +++ b/recruitment-service/src/main/java/com/example/recruitment/service/RecruitmentService.java @@ -22,7 +22,7 @@ public class RecruitmentService { private final UserRepository userRepository; private final StoreRepository storeRepository; private final RecruitmentParticipantRepository participantRepository; - private final OrderClient orderClient; // 주문 서버와 통신할 client + private final OrderClient orderClient; /** * 모집글 생성 시: @@ -30,14 +30,16 @@ public class RecruitmentService { * - 모집글 저장 * - 주문 서버에 주문 생성 요청 * - 생성된 orderId를 모집글에 저장 + * - 모집자도 자동 참여자로 등록 */ @Transactional - public void createRecruitment(RecruitmentRequestDto dto) { + public Long createRecruitment(RecruitmentRequestDto dto) { User user = userRepository.findById(dto.getUserId()) .orElseThrow(() -> new RuntimeException("사용자 없음")); Store store = storeRepository.findById(dto.getStoreId()) .orElseThrow(() -> new RuntimeException("가게 없음")); + // 모집글 저장 Recruitment recruitment = new Recruitment(); recruitment.setUser(user); recruitment.setStore(store); @@ -46,16 +48,25 @@ public void createRecruitment(RecruitmentRequestDto dto) { recruitment.setDeadlineTime(dto.getDeadlineTime()); recruitment.setStatus("RECRUITING"); recruitment.setCategory(dto.getCategory()); - recruitmentRepository.save(recruitment); + recruitmentRepository.save(recruitment); // ID 생성됨 // 주문 서버에 주문 생성 요청 OrderRequestDto orderDto = dto.toOrderRequestDto(); orderDto.setGroupId(recruitment.getId()); Long orderId = orderClient.createOrder(orderDto); - // orderId를 모집글에 저장 + // 모집글에 orderId 저장 recruitment.setOrderId(orderId); recruitmentRepository.save(recruitment); + + // 모집자도 자동으로 참여자로 등록 + RecruitmentParticipant participant = new RecruitmentParticipant(); + participant.setRecruitment(recruitment); + participant.setUser(user); + participant.setOrderId(orderId); + participantRepository.save(participant); + + return recruitment.getId(); } /** @@ -77,12 +88,12 @@ public void joinRecruitment(Long recruitmentId, Long userId, OrderRequestDto ord throw new RuntimeException("이미 참여한 모집입니다."); } - // 주문 서버에 주문 생성 요청 + // 주문 생성 orderDto.setGroupId(recruitmentId); orderDto.setUserId(userId); Long orderId = orderClient.createOrder(orderDto); - // 참여자 등록 + 주문 ID 저장 + // 참여자 등록 RecruitmentParticipant participant = new RecruitmentParticipant(); participant.setRecruitment(recruitment); participant.setUser(user); diff --git a/recruitment-service/src/main/java/com/example/recruitment/service/UserClient.java b/recruitment-service/src/main/java/com/example/recruitment/service/UserClient.java new file mode 100644 index 0000000..cbb5cec --- /dev/null +++ b/recruitment-service/src/main/java/com/example/recruitment/service/UserClient.java @@ -0,0 +1,25 @@ +package com.example.recruitment.service; + +import com.example.recruitment.dto.RecruitmentDetailDto.UserDto; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +@RequiredArgsConstructor +public class UserClient { + + private final RestTemplate restTemplate; + + @Value("${user.server.url}") + private String userServerUrl; + + + public UserDto getUserById(Long userId) { + String url = userServerUrl + "/api/v1/users/" + userId; + ResponseEntity response = restTemplate.getForEntity(url, UserDto.class); + return response.getBody(); + } +} diff --git a/recruitment-service/src/main/resources/application.properties b/recruitment-service/src/main/resources/application.properties index dad7e22..5ae8fcc 100644 --- a/recruitment-service/src/main/resources/application.properties +++ b/recruitment-service/src/main/resources/application.properties @@ -8,3 +8,4 @@ spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true +user.server.url=http://localhost:8080 \ No newline at end of file