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 @@ -12,7 +12,7 @@
import org.sopt.kareer.domain.member.dto.request.MemberOnboardRequest;
import org.sopt.kareer.domain.member.dto.request.MypageRequest;
import org.sopt.kareer.domain.member.dto.response.*;
import org.sopt.kareer.domain.member.entity.constants.Major;
import org.sopt.kareer.domain.member.entity.constants.*;
import org.sopt.kareer.domain.member.entity.enums.Country;
import org.sopt.kareer.domain.member.service.MemberService;
import org.sopt.kareer.domain.roadmap.dto.response.RoadmapTestResponse;
Expand Down Expand Up @@ -58,6 +58,15 @@ public ResponseEntity<BaseResponse<Void>> onboardMember(@AuthenticationPrincipal
.body(BaseResponse.ok("회원 온보딩이 완료되었습니다."));
}

@GetMapping("/onboard/universities")
@Operation(summary = "온보딩 대학교 목록 조회", description = "회원 온보딩 시 선택할 수 있는 대학교 목록을 조회합니다.")
public ResponseEntity<BaseResponse<OnboardUniversitiesResponse>> getOnboardUniversities() {
return ResponseEntity
.status(HttpStatus.OK)
.body(BaseResponse.ok(OnboardUniversitiesResponse.from(University.UNIVERSITY_LIST),
"온보딩 대학교 목록 조회에 성공하였습니다."));
}

@GetMapping("/onboard/countries")
@Operation(summary = "온보딩 국가 목록 조회", description = "회원 온보딩 시 선택할 수 있는 국가 목록을 조회합니다.")
public ResponseEntity<BaseResponse<OnboardCountriesResponse>> getOnboardCountries() {
Expand All @@ -77,11 +86,20 @@ public ResponseEntity<BaseResponse<OnboardMajorsResponse>> getOnboardMajors() {
);
}

@GetMapping("/onboard/fields")
@Operation(summary = "온보딩 관심 분야 목록 조회", description = "회원 온보딩 시 선택할 수 있는 관심 분야 목록을 조회합니다.")
public ResponseEntity<BaseResponse<OnboardFieldsResponse>> getOnboardFields() {
return ResponseEntity
.status(HttpStatus.OK)
.body(BaseResponse.ok(OnboardFieldsResponse.from(Field.FIELD_LIST), "온보딩 관심 분야 목록 조회에 성공하였습니다.")
);
}

@Operation(summary = "AI 로드맵 생성 API", description = "사용자가 온보딩에 입력한 정보를 통해 로드맵을 생성합니다.")
@CustomExceptionDescription(CREATE_ROADMAP)
@PostMapping("roadmap")
public ResponseEntity<BaseResponse<Void>> generateRoadmap(
@AuthenticationPrincipal Long memberId){
@AuthenticationPrincipal Long memberId) {

roadMapService.createRoadmap(memberId);

Expand All @@ -93,7 +111,7 @@ public ResponseEntity<BaseResponse<Void>> generateRoadmap(
@CustomExceptionDescription(CREATE_ROADMAP)
@PostMapping("roadmap/test")
public ResponseEntity<BaseResponse<RoadmapTestResponse>> generateRoadmapForTest(
@AuthenticationPrincipal Long memberId){
@AuthenticationPrincipal Long memberId) {

return ResponseEntity.status(HttpStatus.OK)
.body(BaseResponse.ok(roadMapService.createRoadmapTest(memberId), "AI 로드맵 생성에 성공하였습니다."));
Expand All @@ -114,10 +132,12 @@ public ResponseEntity<BaseResponse<MypageResponse>> getMypage(@AuthenticationPri
return ResponseEntity.status(HttpStatus.OK)
.body(BaseResponse.ok(memberService.getMypage(memberId), "마이페이지 조회에 성공하였습니다."));
}

@Operation(summary = "마이페이지 수정", description = "마이페이지에서 유저 프로필을 수정합니다.")
@CustomExceptionDescription(UPDATE_MYPAGE)
@PutMapping("mypage")
public ResponseEntity<BaseResponse<Void>> updateMypage(@AuthenticationPrincipal Long memberId, @Valid @RequestBody MypageRequest request){
public ResponseEntity<BaseResponse<Void>> updateMypage(@AuthenticationPrincipal Long memberId,
@Valid @RequestBody MypageRequest request) {
memberService.updateMypage(memberId, request.toCommand());
return ResponseEntity.status(HttpStatus.OK)
.body(BaseResponse.ok("마이페이지 수정에 성공하였습니다."));
Expand All @@ -127,8 +147,8 @@ public ResponseEntity<BaseResponse<Void>> updateMypage(@AuthenticationPrincipal
@CustomExceptionDescription(MEMBER_DELETE)
@DeleteMapping("/me")
public ResponseEntity<BaseResponse<Void>> deleteMember(@AuthenticationPrincipal Long memberId,
HttpServletRequest request,
HttpServletResponse response) {
HttpServletRequest request,
HttpServletResponse response) {
memberService.deleteMember(memberId);
authService.signOut(request, response);
return ResponseEntity.status(HttpStatus.OK)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.sopt.kareer.domain.member.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.sopt.kareer.domain.member.dto.request.*;
import org.sopt.kareer.domain.member.service.MemberService;
import org.sopt.kareer.global.annotation.CustomExceptionDescription;
import org.sopt.kareer.global.config.swagger.SwaggerResponseDescription;
import org.sopt.kareer.global.response.BaseResponse;
import org.springframework.http.*;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v2/members")
@Tag(name = "Member API V2", description = "회원 API 버전 2")
public class MemberControllerV2 {

private final MemberService memberService;

@PostMapping("/onboard")
@Operation(summary = "회원 온보딩 V2", description = "PENDING 상태의 회원의 온보딩 결과를 저장합니다.")
@CustomExceptionDescription(SwaggerResponseDescription.MEMBER_ONBOARD)
public ResponseEntity<BaseResponse<Void>> onboardMember(@AuthenticationPrincipal Long memberId,
@Valid @RequestBody MemberOnboardV2Request request) {
memberService.onboardMemberV2(request, memberId);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return ResponseEntity
.status(HttpStatus.OK)
.body(BaseResponse.ok("회원 온보딩이 완료되었습니다."));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.sopt.kareer.domain.member.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import java.time.LocalDate;
import java.util.List;
import org.sopt.kareer.domain.member.entity.enums.*;

public record MemberOnboardV2Request(
@NotBlank(message = "이름은 필수 입력값입니다.")
String name,

@NotNull(message = "생년월일은 필수 입력값입니다.")
LocalDate birthDate,

@NotNull(message = "대학교는 필수 입력값입니다.")
String university,

@NotNull(message = "국가는 필수 입력값입니다.")
Country country,

@NotNull(message = "언어 능력은 필수 입력값입니다.")
LanguageLevel languageLevel,

@NotNull(message = "영어 능력은 필수 입력값입니다.")
EnglishLevel englishLevel,

@NotNull(message = "학위는 필수 입력값입니다.")
Degree degree,

@NotNull(message = "비자 유형은 필수 입력값입니다.")
VisaType visaType,

@Schema(description = "예상 졸업일, D2 비자인 경우만", type = "string", format = "date", example = "2025-08-31")
LocalDate expectedGraduationDate,

@NotNull(message = "비자 시작일은 필수 입력값입니다.")
LocalDate visaStartDate,

@NotNull(message = "비자 만료일은 필수 입력값입니다.")
LocalDate visaExpiredAt,

@Schema(description = "비자 점수, D10 비자인 경우만", example = "50")
Integer visaPoint,

@NotBlank(message = "제1전공은 필수 입력값입니다.")
String primaryMajor,

String secondaryMajor,

@NotNull(message = "관심 분야는 필수 입력값입니다.")
@Size(min = 1, max = 5, message = "관심 분야는 최소 1개, 최대 5개까지 선택할 수 있습니다.")
List<String> fieldsOfInterests,

List<String> preparationStatuses,

@NotBlank(message = "희망 직무는 필수 입력값입니다.")
String targetJob,

String targetJobSkill,

@NotBlank(message = "개인 배경은 필수 입력값입니다.")
@Size(max = 1000, message = "개인 배경은 최대 1000자까지 입력할 수 있습니다.")
String personalBackground
) {
@AssertTrue(message = "visaPoint는 D10 비자인 경우에만 입력할 수 있습니다.")
@Schema(hidden = true)
public boolean isVisaPointValid() {
if (visaType == null) {
return visaPoint == null;
}
if (visaType == VisaType.D10) {
return visaPoint != null;
}
return visaPoint == null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.sopt.kareer.domain.member.dto.response;

import java.util.List;

public record OnboardFieldsResponse(
List<String> fields
) {
public static OnboardFieldsResponse from(List<String> fields) {
return new OnboardFieldsResponse(fields);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.sopt.kareer.domain.member.dto.response;

import java.util.List;

public record OnboardUniversitiesResponse(
List<String> universities
) {
public static OnboardUniversitiesResponse from(List<String> universities) {
return new OnboardUniversitiesResponse(universities);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,21 @@ public class Member extends BaseEntity {
@Column(length = 1000)
private String targetJobSkill;

private String preparationStatus;

private String fieldsOfInterest;

@Enumerated(EnumType.STRING)
@Column(nullable = false)
private RoadmapStatus roadmapStatus;

public void updateInfo(String name,
LocalDate birthDate,
Country country,
String university,
EnglishLevel englishLevel,
String fieldsOfInterests,
String preparationStatuses,
LanguageLevel languageLevel,
Degree degree,
LocalDate expectedGraduationDate,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.sopt.kareer.domain.member.entity.constants;

import java.util.List;

public class Field {

private Field() {
}

public static final List<String> FIELD_LIST = List.of(
"Automotive",
"Aerospace",
"Energy",
"Oil & Gas",
"Renewable Energy",
"Manufacturing",
"Construction",
"Engineering",
"Healthcare",
"Pharmaceuticals",
"Biotechnology",
"Medical Devices",
"Education",
"EdTech",
"Finance",
"Banking",
"Insurance",
"FinTech",
"Investment & Asset Management",
"Retail",
"E-commerce",
"Consumer Goods",
"Telecommunications",
"Media",
"Entertainment",
"Gaming",
"Agriculture",
"Food & Beverage",
"Hospitality",
"Travel & Tourism",
"Logistics",
"Supply Chain",
"Real Estate",
"Consulting",
"Legal",
"Government & Public Sector",
"Nonprofit & NGO",
"Information Technology",
"Software Development",
"Artificial Intelligence",
"Data & Analytics",
"Cybersecurity",
"Cloud Computing",
"Blockchain",
"Internet of Things (IoT)",
"Robotics",
"Semiconductors",
"Electronics",
"Hardware",
"Design",
"Marketing & Advertising",
"Human Resources",
"Sports",
"Fashion & Apparel",
"Beauty & Cosmetics",
"Environment",
"Sustainability",
"Smart City",
"Defense",
"Space Industry"
);
}
Loading
Loading