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
9 changes: 9 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"mcp__sequential-thinking__sequentialthinking"
],
"deny": [],
"ask": []
}
}
2 changes: 2 additions & 0 deletions src/main/java/com/example/umc/UmcApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 UmcApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.umc.domain.category.exception;

import com.example.umc.global.apiPayload.code.BaseErrorCode;
import com.example.umc.global.exception.GeneralException;

public class CategoryException extends GeneralException {
public CategoryException(BaseErrorCode code) {
super(code);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.example.umc.domain.category.exception.code;

import com.example.umc.global.apiPayload.code.BaseErrorCode;
import com.example.umc.global.apiPayload.code.ErrorReasonDto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum CategoryErrorCode implements BaseErrorCode {

CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "CATEGORY404_1", "해당 카테고리를 찾지 못했습니다."),
;

private final HttpStatus status;
private final String code;
private final String message;

@Override
public ErrorReasonDto getReason() {
return ErrorReasonDto.builder()
.isSuccess(false)
.code(code)
.message(message)
.build();
}

@Override
public ErrorReasonDto getReasonHttpStatus() {
return ErrorReasonDto.builder()
.isSuccess(false)
.httpStatus(status)
.code(code)
.message(message)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.umc.domain.category.repository;

import com.example.umc.domain.category.entity.PreferCategory;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface PreferCategoryRepository extends JpaRepository<PreferCategory, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.example.umc.domain.member.controller;

import com.example.umc.domain.member.dto.MemberReqDTO;
import com.example.umc.domain.member.dto.MemberResDTO;
import com.example.umc.domain.member.exception.code.MemberSuccessCode;
import com.example.umc.domain.member.service.MemberCommandService;
import com.example.umc.global.apiPayload.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/members")
@RequiredArgsConstructor
@Tag(name = "회원", description = "회원 관련 API")
public class MemberController {

private final MemberCommandService memberCommandService;

// 회원가입
@PostMapping("/signup")
@Operation(summary = "회원가입", description = "새로운 회원을 등록합니다.")
public ApiResponse<MemberResDTO.JoinDTO> signUp(
@RequestBody @Valid MemberReqDTO.JoinDTO dto
) {
MemberResDTO.JoinDTO response = memberCommandService.signup(dto);
return ApiResponse.onSuccess(MemberSuccessCode.MEMBER_CREATED, response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.example.umc.domain.member.converter;

import com.example.umc.domain.member.dto.MemberReqDTO;
import com.example.umc.domain.member.dto.MemberResDTO;
import com.example.umc.domain.user.entity.User;

public class MemberConverter {

// Entity -> DTO
public static MemberResDTO.JoinDTO toJoinDTO(User member) {
return MemberResDTO.JoinDTO.builder()
.memberId(member.getUserId())
.createdAt(member.getCreatedAt())
.build();
}

// DTO -> Entity
public static User toMember(MemberReqDTO.JoinDTO dto) {
return User.builder()
.name(dto.name())
.birth(dto.birth())
.address(dto.address() != null ? dto.address().toString() : null)
.gender(dto.gender())
.build();
}
}
17 changes: 17 additions & 0 deletions src/main/java/com/example/umc/domain/member/dto/MemberReqDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example.umc.domain.member.dto;

import com.example.umc.domain.user.enums.Gender;

import java.time.LocalDate;
import java.util.List;

public class MemberReqDTO {
public record JoinDTO(
String name,
Gender gender,
LocalDate birth,
String address,
String specAddress,
List<Long> preferCategory
) {}
}
13 changes: 13 additions & 0 deletions src/main/java/com/example/umc/domain/member/dto/MemberResDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.umc.domain.member.dto;

import lombok.Builder;

import java.time.LocalDateTime;

public class MemberResDTO {
@Builder
public record JoinDTO(
Long memberId,
LocalDateTime createdAt
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.umc.domain.member.exception;

import com.example.umc.global.apiPayload.code.BaseErrorCode;
import com.example.umc.global.exception.GeneralException;

public class MemberException extends GeneralException {
public MemberException(BaseErrorCode code) {
super(code);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.example.umc.domain.member.exception.code;

import com.example.umc.global.apiPayload.code.BaseErrorCode;
import com.example.umc.global.apiPayload.code.ErrorReasonDto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum MemberErrorCode implements BaseErrorCode {

MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER404_1", "해당 사용자를 찾지 못했습니다."),
;

private final HttpStatus status;
private final String code;
private final String message;

@Override
public ErrorReasonDto getReason() {
return ErrorReasonDto.builder()
.isSuccess(false)
.code(code)
.message(message)
.build();
}

@Override
public ErrorReasonDto getReasonHttpStatus() {
return ErrorReasonDto.builder()
.isSuccess(false)
.httpStatus(status)
.code(code)
.message(message)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.example.umc.domain.member.exception.code;

import com.example.umc.global.apiPayload.code.BaseCode;
import com.example.umc.global.apiPayload.code.ReasonDto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum MemberSuccessCode implements BaseCode {

MEMBER_CREATED(HttpStatus.CREATED, "MEMBER201_1", "성공적으로 사용자가 생성되었습니다."),
;

private final HttpStatus status;
private final String code;
private final String message;

@Override
public ReasonDto getReason() {
return ReasonDto.builder()
.isSuccess(true)
.code(code)
.message(message)
.build();
}

@Override
public ReasonDto getReasonHttpStatus() {
return ReasonDto.builder()
.isSuccess(true)
.httpStatus(status)
.code(code)
.message(message)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.umc.domain.member.repository;

import com.example.umc.domain.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MemberRepository extends JpaRepository<User, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.umc.domain.member.service;

import com.example.umc.domain.member.dto.MemberReqDTO;
import com.example.umc.domain.member.dto.MemberResDTO;

public interface MemberCommandService {
// 회원가입
MemberResDTO.JoinDTO signup(MemberReqDTO.JoinDTO dto);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.example.umc.domain.member.service;

import com.example.umc.domain.member.converter.MemberConverter;
import com.example.umc.domain.member.dto.MemberReqDTO;
import com.example.umc.domain.member.dto.MemberResDTO;
import com.example.umc.domain.member.repository.MemberRepository;
import com.example.umc.domain.user.entity.User;
import com.example.umc.domain.user.entity.UserPrefer;
import com.example.umc.domain.user.repository.UserPreferRepository;
import com.example.umc.domain.category.entity.PreferCategory;
import com.example.umc.domain.category.repository.PreferCategoryRepository;
import com.example.umc.domain.category.exception.CategoryException;
import com.example.umc.domain.category.exception.code.CategoryErrorCode;
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
public class MemberCommandServiceImpl implements MemberCommandService {

private final MemberRepository memberRepository;
private final UserPreferRepository userPreferRepository;
private final PreferCategoryRepository preferCategoryRepository;

@Override
@Transactional
public MemberResDTO.JoinDTO signup(MemberReqDTO.JoinDTO dto) {
// 사용자 생성
User member = MemberConverter.toMember(dto);

// DB 적용
memberRepository.save(member);

// 선호 음식 존재 여부 확인
if (dto.preferCategory() != null && dto.preferCategory().size() > 0) {
List<UserPrefer> userPreferList = dto.preferCategory().stream()
.map(id -> {
// 선호 카테고리 존재 여부 검증
PreferCategory preferCategory = preferCategoryRepository.findById(id)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런거는 메서드로 따로 빼둬도 될듯요? (취향차이이긴 함)

.orElseThrow(() -> new CategoryException(CategoryErrorCode.CATEGORY_NOT_FOUND));

// UserPrefer 엔티티 생성
return UserPrefer.builder()
.user(member)
.preferCategory(preferCategory)
.build();
})
.collect(Collectors.toList());

// 모든 선호 음식 추가: DB 적용
userPreferRepository.saveAll(userPreferList);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스터디 때도 말했지만, 한 번에 많은 데이터 저장할 때 다량의 insert 쿼리 발생하는거 조심

}

// 응답 DTO 생성
return MemberConverter.toJoinDTO(member);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.example.umc.domain.mission.controller;

import com.example.umc.domain.mission.dto.MissionReqDTO;
import com.example.umc.domain.mission.dto.MissionResDTO;
import com.example.umc.domain.mission.exception.code.MissionSuccessCode;
import com.example.umc.domain.mission.service.MissionCommandService;
import com.example.umc.global.apiPayload.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1/missions")
@RequiredArgsConstructor
@Tag(name = "미션", description = "미션 관련 API")
public class MissionController {

private final MissionCommandService missionCommandService;

// 미션 도전하기
@PostMapping("/challenge")
@Operation(summary = "미션 도전하기", description = "가게의 미션을 도전 중인 미션에 추가합니다.")
public ApiResponse<MissionResDTO.ChallengeMissionDTO> challengeMission(
@RequestBody @Valid MissionReqDTO.ChallengeMissionDTO dto
) {
MissionResDTO.ChallengeMissionDTO response = missionCommandService.challengeMission(dto);
return ApiResponse.onSuccess(MissionSuccessCode.MISSION_CHALLENGED, response);
}

// 미션 추가하기
@PostMapping("")
@Operation(summary = "미션 추가", description = "가게에 미션을 추가합니다.")
public ApiResponse<MissionResDTO.CreateMissionDTO> createMission(
@RequestBody @Valid MissionReqDTO.CreateMissionDTO dto
) {
MissionResDTO.CreateMissionDTO response = missionCommandService.createMission(dto);
return ApiResponse.onSuccess(MissionSuccessCode.MISSION_CREATED, response);
}
}
Loading