Skip to content

Commit

Permalink
Merge pull request #15 from Leets-Official/feat/#6/이벤트-기반-아키텍쳐-매칭-로직-구현
Browse files Browse the repository at this point in the history
Feat/#15/이벤트 기반 아키텍쳐 매칭 로직 구현
  • Loading branch information
soyesenna authored Jan 13, 2025
2 parents 045f5cc + c8a0bb2 commit 03083dc
Show file tree
Hide file tree
Showing 36 changed files with 1,096 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ out/

### VS Code ###
.vscode/

src/main/resources/.env
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
runtimeOnly 'com.mysql:mysql-connector-j'

// Kafka
implementation 'org.springframework.kafka:spring-kafka'

// Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.gachtaxi.domain.matching.algorithm.dto;

import lombok.Builder;

@Builder
public record FindRoomResult(
Long roomId,
Integer currentMembers,
Integer maxCapacity
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.gachtaxi.domain.matching.algorithm.service;

import com.gachtaxi.domain.matching.algorithm.dto.FindRoomResult;
import com.gachtaxi.domain.matching.common.entity.enums.Tags;
import java.util.List;
import java.util.Optional;

public interface MatchingAlgorithmService {

/**
* 방을 찾는 메서드
* 이미 방에 들어가있는 멤버가 다시 요청했을 때 Optional.empty()를 반환하도록 로직을 구성해야함
* @param userId 방에 들어가려는 사용자 ID
* @param startPoint 매칭 시작 지점 좌표
* @param destinationPoint 도착지 좌표
* @param criteria 방 검색에 필요한 기타 조건 (태그 등)
* @return Optional<FindRoomResult> - 매칭 가능한 방 정보가 있으면 값이 있고, 없으면 empty
*/
Optional<FindRoomResult> findRoom(Long userId, String startPoint, String destinationPoint, List<Tags> criteria);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.gachtaxi.domain.matching.algorithm.service;

import com.gachtaxi.domain.matching.algorithm.dto.FindRoomResult;
import com.gachtaxi.domain.matching.common.entity.MatchingRoom;
import com.gachtaxi.domain.matching.common.entity.enums.Tags;
import com.gachtaxi.domain.matching.common.repository.MatchingRoomRepository;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class MockMatchingAlgorithmService implements MatchingAlgorithmService {

private final MatchingRoomRepository matchingRoomRepository;

@Override
public Optional<FindRoomResult> findRoom(Long userId, String startPoint, String destinationPoint, List<Tags> criteria) {
List<MatchingRoom> matchingRoomList = this.matchingRoomRepository.findAll();
if (!matchingRoomList.isEmpty()) {
MatchingRoom matchingRoom = matchingRoomList.get(0);
return Optional.of(
FindRoomResult.builder()
.roomId(matchingRoom.getId())
.maxCapacity(matchingRoom.getCapacity())
.build());
}
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.gachtaxi.domain.matching.common.controller;

import com.gachtaxi.domain.matching.common.dto.request.AutoMatchingPostRequest;
import com.gachtaxi.domain.matching.common.dto.response.AutoMatchingPostResponse;
import com.gachtaxi.domain.matching.common.service.AutoMatchingService;
import com.gachtaxi.global.common.response.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/matching/auto")
public class AutoMatchingController {

private final AutoMatchingService autoMatchingService;

@GetMapping(value = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter subscribeSse(@RequestParam Long memberId) {
// TODO: 인가 로직 완성되면 해당 멤버의 아이디를 가져오도록 변경
// Long memberId = 1L;

return this.autoMatchingService.handleSubscribe(memberId);
}

@PostMapping("/request")
public ApiResponse<AutoMatchingPostResponse> requestMatching(
@RequestParam Long memberId,
@RequestBody AutoMatchingPostRequest autoMatchingPostRequest
) {
// TODO: 인가 로직 완성되면 해당 멤버의 아이디를 가져오도록 변경
// Long memberId = 1L;
if (!this.autoMatchingService.isSseSubscribed(memberId)) {
return ApiResponse.response(
HttpStatus.BAD_REQUEST,
ResponseMessage.NOT_SUBSCRIBED_SSE.getMessage()
);
}

return ApiResponse.response(
HttpStatus.OK,
ResponseMessage.AUTO_MATCHING_REQUEST_ACCEPTED.getMessage(),
this.autoMatchingService.handlerAutoRequestMatching(memberId, autoMatchingPostRequest)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.gachtaxi.domain.matching.common.controller;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public enum ResponseMessage {

// sse
SUBSCRIBE_SUCCESS("SSE 구독에 성공했습니다."),

// auto matching
AUTO_MATCHING_REQUEST_ACCEPTED("자동 매칭 요청 전송에 성공했습니다."),
NOT_SUBSCRIBED_SSE("SSE 구독 후 자동 매칭을 요청할 수 있습니다.");

private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.gachtaxi.domain.matching.common.dto.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum AutoMatchingStatus {
REQUESTED("REQUESTED"),
REJECTED("REJECTED");

private final String value;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.gachtaxi.domain.matching.common.dto.request;

import com.gachtaxi.domain.matching.common.entity.enums.Tags;
import java.util.List;

public record AutoMatchingPostRequest(
String startPoint,
String startName,
String destinationPoint,
String destinationName,
List<String> criteria,
Integer expectedTotalCharge
) {

public List<Tags> getCriteria() {
return this.criteria.stream()
.map(Tags::valueOf)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.gachtaxi.domain.matching.common.dto.response;

import com.gachtaxi.domain.matching.common.dto.enums.AutoMatchingStatus;

public record AutoMatchingPostResponse(
String autoMatchingStatus

) {

public static AutoMatchingPostResponse of(AutoMatchingStatus autoMatchingStatus) {
return new AutoMatchingPostResponse(autoMatchingStatus.getValue());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.gachtaxi.domain.matching.common.entity;

import com.gachtaxi.domain.matching.common.entity.enums.MatchingRoomStatus;
import com.gachtaxi.domain.members.entity.Members;
import com.gachtaxi.global.common.entity.BaseEntity;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.util.List;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "matching_room")
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class MatchingRoom extends BaseEntity {

@OneToMany(mappedBy = "matchingRoom")
private List<MatchingRoomTagInfo> matchingRoomTagInfo;

@Column(name = "capacity", nullable = false, columnDefinition = "INT CHECK (capacity BETWEEN 1 AND 4)")
@Getter
private Integer capacity;

// 팀원들 정보
@OneToMany(mappedBy = "matchingRoom", fetch = FetchType.LAZY)
private List<MemberMatchingRoomChargingInfo> memberMatchingRoomChargingInfo;

@ManyToOne(cascade = CascadeType.PERSIST, optional = false)
private Members roomMaster;

@Column(name = "title", nullable = false)
private String title;

@Column(name = "description", nullable = false)
private String description;

@ManyToOne(fetch = FetchType.LAZY)
private Route route;

@Column(name = "total_charge")
@Getter
private Integer totalCharge;

@Enumerated(EnumType.STRING)
private MatchingRoomStatus matchingRoomStatus;

public boolean isActiveMatchingRoom() {
return this.matchingRoomStatus == MatchingRoomStatus.ACTIVE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.gachtaxi.domain.matching.common.entity;

import com.gachtaxi.domain.matching.common.entity.enums.Tags;
import com.gachtaxi.global.common.entity.BaseEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "matching_room_tag_info")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
public class MatchingRoomTagInfo extends BaseEntity {

@ManyToOne
private MatchingRoom matchingRoom;

@Enumerated(EnumType.STRING)
private Tags tags;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.gachtaxi.domain.matching.common.entity;

import com.gachtaxi.domain.matching.common.entity.enums.PaymentStatus;
import com.gachtaxi.domain.members.entity.Members;
import com.gachtaxi.global.common.entity.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Table(
name = "member_matching_room_charging_info",
uniqueConstraints = {
@UniqueConstraint(columnNames = {"members_id", "matching_room_id"})
}
)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
public class MemberMatchingRoomChargingInfo extends BaseEntity {

@ManyToOne(fetch = FetchType.LAZY)
private Members members;

@ManyToOne(fetch = FetchType.LAZY)
private MatchingRoom matchingRoom;

@Column(name = "charge")
@Setter
private Integer charge;

@Enumerated(EnumType.STRING)
private PaymentStatus paymentStatus = PaymentStatus.NOT_PAYED;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.gachtaxi.domain.matching.common.entity;

import com.gachtaxi.global.common.entity.BaseEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "route")
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class Route extends BaseEntity {

private String startLocationCoordinate;
private String startLocationName;

private String endLocationCoordinate;
private String endLocationName;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.gachtaxi.domain.matching.common.entity.enums;

public enum MatchingRoomStatus {
PROCESS, COMPLETE, CANCELED, ACTIVE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.gachtaxi.domain.matching.common.entity.enums;

public enum PaymentStatus {
PAYED, NOT_PAYED, FAILED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.gachtaxi.domain.matching.common.entity.enums;

public enum Tags {
NO_SMOKE,
SAME_GENDER
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.gachtaxi.domain.matching.common.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public enum ErrorMessage {

NO_SUCH_MATCHING_ROOM("해당 매칭 방이 존재하지 않습니다."),
NOT_ACTIVE_MATCHING_ROOM("열린 매칭 방이 아닙니다.");

private final String message;
}
Loading

0 comments on commit 03083dc

Please sign in to comment.