Skip to content

Commit a7e1ff5

Browse files
committed
[Feat] #29 프로필 조회 및 수정 구현
1 parent 05cfb15 commit a7e1ff5

8 files changed

Lines changed: 191 additions & 11 deletions

File tree

src/main/java/com/leets7th/job_is_be/domain/user/controller/ProfileController.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.leets7th.job_is_be.domain.user.dto.ProfileDraftRequest;
44
import com.leets7th.job_is_be.domain.user.dto.ProfileDraftResponse;
5+
import com.leets7th.job_is_be.domain.user.dto.ProfileResponse;
6+
import com.leets7th.job_is_be.domain.user.dto.ProfileUpdateRequest;
57
import com.leets7th.job_is_be.domain.user.service.ProfileService;
68
import com.leets7th.job_is_be.global.response.ApiResponse;
79
import com.leets7th.job_is_be.global.status.SuccessStatus;
@@ -11,6 +13,7 @@
1113
import org.springframework.web.bind.annotation.GetMapping;
1214
import org.springframework.web.bind.annotation.PutMapping;
1315
import org.springframework.web.bind.annotation.PostMapping;
16+
import org.springframework.web.bind.annotation.PatchMapping;
1417
import org.springframework.web.bind.annotation.RequestBody;
1518
import org.springframework.web.bind.annotation.RequestMapping;
1619
import org.springframework.web.bind.annotation.RestController;
@@ -25,6 +28,27 @@ public ProfileController(ProfileService profileService) {
2528
this.profileService = profileService;
2629
}
2730

31+
@GetMapping
32+
public ResponseEntity<ApiResponse<ProfileResponse>> getProfile(
33+
@AuthenticationPrincipal Jwt jwt
34+
) {
35+
return ApiResponse.success(
36+
SuccessStatus.PROFILE_GET_SUCCESS,
37+
profileService.getProfile(userId(jwt))
38+
);
39+
}
40+
41+
@PatchMapping
42+
public ResponseEntity<ApiResponse<ProfileResponse>> updateProfile(
43+
@AuthenticationPrincipal Jwt jwt,
44+
@RequestBody ProfileUpdateRequest request
45+
) {
46+
return ApiResponse.success(
47+
SuccessStatus.PROFILE_UPDATE_SUCCESS,
48+
profileService.updateProfile(userId(jwt), request)
49+
);
50+
}
51+
2852
@GetMapping("/draft")
2953
public ResponseEntity<ApiResponse<ProfileDraftResponse>> getDraft(
3054
@AuthenticationPrincipal Jwt jwt

src/main/java/com/leets7th/job_is_be/domain/user/dto/ProfileDraftResponse.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,12 @@
77

88
public record ProfileDraftResponse(
99
OnboardingStep onboardingStep,
10-
List<JobCategoryItem> jobCategories,
11-
RegionItem region,
10+
List<ProfileJobCategoryResponse> jobCategories,
11+
ProfileRegionResponse region,
1212
CareerLevel careerLevel,
1313
List<String> preferenceNotes,
1414
List<String> excludeKeywords,
1515
List<String> techStacks,
1616
boolean jobTestCompleted
1717
) {
18-
public record JobCategoryItem(Long id, String name, boolean primary) {
19-
}
20-
21-
public record RegionItem(Long id, String name) {
22-
}
2318
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.leets7th.job_is_be.domain.user.dto;
2+
3+
public record ProfileJobCategoryResponse(
4+
Long id,
5+
String name,
6+
boolean primary
7+
) {
8+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.leets7th.job_is_be.domain.user.dto;
2+
3+
public record ProfileRegionResponse(Long id, String name) {
4+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.leets7th.job_is_be.domain.user.dto;
2+
3+
import com.leets7th.job_is_be.domain.user.enums.CareerLevel;
4+
5+
import java.time.LocalDateTime;
6+
import java.util.List;
7+
8+
public record ProfileResponse(
9+
Long userId,
10+
List<ProfileJobCategoryResponse> jobCategories,
11+
ProfileRegionResponse region,
12+
CareerLevel careerLevel,
13+
List<String> preferenceNotes,
14+
List<String> excludeKeywords,
15+
List<String> techStacks,
16+
boolean jobTestCompleted,
17+
boolean onboardingCompleted,
18+
LocalDateTime onboardingCompletedAt
19+
) {
20+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.leets7th.job_is_be.domain.user.dto;
2+
3+
import com.leets7th.job_is_be.domain.user.enums.CareerLevel;
4+
5+
import java.util.List;
6+
7+
public record ProfileUpdateRequest(
8+
List<Long> jobCategoryIds,
9+
Long primaryJobCategoryId,
10+
Long regionId,
11+
CareerLevel careerLevel,
12+
List<String> preferenceNotes,
13+
List<String> excludeKeywords,
14+
List<String> techStacks
15+
) {
16+
}

src/main/java/com/leets7th/job_is_be/domain/user/service/ProfileService.java

Lines changed: 115 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
import com.leets7th.job_is_be.domain.job.repository.RegionRepository;
77
import com.leets7th.job_is_be.domain.user.dto.ProfileDraftRequest;
88
import com.leets7th.job_is_be.domain.user.dto.ProfileDraftResponse;
9+
import com.leets7th.job_is_be.domain.user.dto.ProfileJobCategoryResponse;
10+
import com.leets7th.job_is_be.domain.user.dto.ProfileRegionResponse;
11+
import com.leets7th.job_is_be.domain.user.dto.ProfileResponse;
12+
import com.leets7th.job_is_be.domain.user.dto.ProfileUpdateRequest;
913
import com.leets7th.job_is_be.domain.user.entity.User;
1014
import com.leets7th.job_is_be.domain.user.entity.UserJobCategory;
1115
import com.leets7th.job_is_be.domain.user.entity.UserProfile;
@@ -132,6 +136,68 @@ public void completeOnboarding(Long userId) {
132136
profile.completeOnboarding(LocalDateTime.now());
133137
}
134138

139+
@Transactional(readOnly = true)
140+
public ProfileResponse getProfile(Long userId) {
141+
UserProfile profile = findCompletedProfile(userId);
142+
return toProfileResponse(profile, findJobCategories(userId), findRegion(userId));
143+
}
144+
145+
@Transactional
146+
public ProfileResponse updateProfile(Long userId, ProfileUpdateRequest request) {
147+
UserProfile profile = findCompletedProfile(userId);
148+
if (request == null) {
149+
return toProfileResponse(profile, findJobCategories(userId), findRegion(userId));
150+
}
151+
152+
User user = profile.getUser();
153+
List<UserJobCategory> currentSelections = findJobCategories(userId);
154+
UserRegion currentRegion = findRegion(userId);
155+
156+
if (request.jobCategoryIds() != null || request.primaryJobCategoryId() != null) {
157+
List<Long> categoryIds = request.jobCategoryIds() != null
158+
? request.jobCategoryIds()
159+
: currentSelections.stream()
160+
.map(selection -> selection.getJobCategory().getId())
161+
.toList();
162+
Long primaryId = request.primaryJobCategoryId() != null
163+
? request.primaryJobCategoryId()
164+
: currentSelections.stream()
165+
.filter(UserJobCategory::isPrimary)
166+
.map(selection -> selection.getJobCategory().getId())
167+
.findFirst()
168+
.orElse(null);
169+
List<JobCategory> categories = resolveJobCategories(categoryIds, primaryId);
170+
if (categories.isEmpty()) {
171+
throw new GeneralException(ErrorStatus.PROFILE_REQUIRED_FIELDS_MISSING);
172+
}
173+
replaceJobCategories(user, categories, primaryId);
174+
}
175+
176+
if (request.regionId() != null) {
177+
replaceRegion(user, resolveRegion(request.regionId()));
178+
}
179+
180+
profile.updateProfile(
181+
request.careerLevel() != null ? request.careerLevel() : profile.getCareerLevel(),
182+
request.preferenceNotes() != null
183+
? valueCodec.encode(request.preferenceNotes())
184+
: profile.getPreferenceNote(),
185+
request.excludeKeywords() != null
186+
? valueCodec.encode(request.excludeKeywords())
187+
: profile.getExcludeKeywords(),
188+
request.techStacks() != null
189+
? valueCodec.encode(request.techStacks())
190+
: profile.getTechStack()
191+
);
192+
193+
List<UserJobCategory> updatedSelections = findJobCategories(userId);
194+
UserRegion updatedRegion = findRegion(userId);
195+
if (updatedSelections.isEmpty() || updatedRegion == null || profile.getCareerLevel() == null) {
196+
throw new GeneralException(ErrorStatus.PROFILE_REQUIRED_FIELDS_MISSING);
197+
}
198+
return toProfileResponse(profile, updatedSelections, updatedRegion);
199+
}
200+
135201
private List<JobCategory> resolveJobCategories(List<Long> requestedIds, Long primaryId) {
136202
List<Long> ids = requestedIds == null ? List.of() : requestedIds;
137203
LinkedHashSet<Long> uniqueIds = new LinkedHashSet<>(ids);
@@ -209,16 +275,16 @@ private ProfileDraftResponse toDraftResponse(
209275
return left.getJobCategory().getId().compareTo(right.getJobCategory().getId());
210276
});
211277

212-
List<ProfileDraftResponse.JobCategoryItem> jobCategories = orderedSelections.stream()
213-
.map(selection -> new ProfileDraftResponse.JobCategoryItem(
278+
List<ProfileJobCategoryResponse> jobCategories = orderedSelections.stream()
279+
.map(selection -> new ProfileJobCategoryResponse(
214280
selection.getJobCategory().getId(),
215281
selection.getJobCategory().getName(),
216282
selection.isPrimary()
217283
))
218284
.toList();
219-
ProfileDraftResponse.RegionItem region = userRegion == null
285+
ProfileRegionResponse region = userRegion == null
220286
? null
221-
: new ProfileDraftResponse.RegionItem(
287+
: new ProfileRegionResponse(
222288
userRegion.getRegion().getId(),
223289
userRegion.getRegion().getName()
224290
);
@@ -234,4 +300,49 @@ private ProfileDraftResponse toDraftResponse(
234300
profile.isJobTestCompleted()
235301
);
236302
}
303+
304+
private UserProfile findCompletedProfile(Long userId) {
305+
UserProfile profile = userProfileRepository.findByUserId(userId)
306+
.orElseThrow(() -> new GeneralException(ErrorStatus.PROFILE_NOT_FOUND));
307+
if (!profile.isOnboardingCompleted()) {
308+
throw new GeneralException(ErrorStatus.PROFILE_NOT_FOUND);
309+
}
310+
return profile;
311+
}
312+
313+
private ProfileResponse toProfileResponse(
314+
UserProfile profile,
315+
List<UserJobCategory> selections,
316+
UserRegion userRegion
317+
) {
318+
List<ProfileJobCategoryResponse> jobCategories = selections.stream()
319+
.sorted((left, right) -> {
320+
if (left.isPrimary() != right.isPrimary()) {
321+
return left.isPrimary() ? -1 : 1;
322+
}
323+
return left.getJobCategory().getId().compareTo(right.getJobCategory().getId());
324+
})
325+
.map(selection -> new ProfileJobCategoryResponse(
326+
selection.getJobCategory().getId(),
327+
selection.getJobCategory().getName(),
328+
selection.isPrimary()
329+
))
330+
.toList();
331+
ProfileRegionResponse region = new ProfileRegionResponse(
332+
userRegion.getRegion().getId(),
333+
userRegion.getRegion().getName()
334+
);
335+
return new ProfileResponse(
336+
profile.getUser().getId(),
337+
jobCategories,
338+
region,
339+
profile.getCareerLevel(),
340+
valueCodec.decode(profile.getPreferenceNote()),
341+
valueCodec.decode(profile.getExcludeKeywords()),
342+
valueCodec.decode(profile.getTechStack()),
343+
profile.isJobTestCompleted(),
344+
profile.isOnboardingCompleted(),
345+
profile.getOnboardingCompletedAt()
346+
);
347+
}
237348
}

src/main/java/com/leets7th/job_is_be/global/status/SuccessStatus.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public enum SuccessStatus implements BaseStatus {
2525
/**
2626
* Profile
2727
*/
28+
PROFILE_GET_SUCCESS(HttpStatus.OK, "PROFILE_200_1", "프로필을 조회했습니다."),
29+
PROFILE_UPDATE_SUCCESS(HttpStatus.OK, "PROFILE_200_2", "프로필을 수정했습니다."),
2830
PROFILE_DRAFT_GET_SUCCESS(HttpStatus.OK, "PROFILE_200_3", "온보딩 임시저장을 조회했습니다."),
2931
PROFILE_DRAFT_SAVE_SUCCESS(HttpStatus.OK, "PROFILE_200_4", "온보딩 내용을 임시저장했습니다."),
3032
PROFILE_ONBOARDING_COMPLETE_SUCCESS(HttpStatus.OK, "PROFILE_200_5", "온보딩을 완료했습니다."),

0 commit comments

Comments
 (0)