Skip to content
Open
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
@@ -1,5 +1,9 @@
package com.comatching.common.domain.enums;

import java.util.Arrays;

import com.fasterxml.jackson.annotation.JsonCreator;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

Expand All @@ -13,4 +17,23 @@ public enum ContactFrequency {

private final String code;
private final String description;

@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
public static ContactFrequency from(String value) {
if (value == null) {
return null;
}

String normalizedValue = value.trim();
if (normalizedValue.isEmpty()) {
throw new IllegalArgumentException("Contact frequency value is blank");
}

return Arrays.stream(values())
.filter(frequency -> frequency.name().equalsIgnoreCase(normalizedValue)
|| frequency.code.equals(normalizedValue)
|| frequency.description.equals(normalizedValue))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("Unknown contact frequency: " + value));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.comatching.common.dto.member;

public record RealNameResponseDto(
String realName
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.comatching.common.domain.enums;

import static org.assertj.core.api.Assertions.*;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.databind.json.JsonMapper;

@DisplayName("ContactFrequency Enum 테스트")
class ContactFrequencyTest {

private final JsonMapper objectMapper = JsonMapper.builder().build();

@Test
@DisplayName("영문 enum 이름으로 역직렬화할 수 있다")
void shouldDeserializeFromEnumName() throws Exception {
ContactFrequency result = objectMapper.readValue("\"FREQUENT\"", ContactFrequency.class);

assertThat(result).isEqualTo(ContactFrequency.FREQUENT);
}

@Test
@DisplayName("한글 코드로 역직렬화할 수 있다")
void shouldDeserializeFromKoreanCode() throws Exception {
ContactFrequency result = objectMapper.readValue("\"자주\"", ContactFrequency.class);

assertThat(result).isEqualTo(ContactFrequency.FREQUENT);
}

@Test
@DisplayName("한글 설명으로 역직렬화할 수 있다")
void shouldDeserializeFromKoreanDescription() throws Exception {
ContactFrequency result = objectMapper.readValue("\"보통 연락\"", ContactFrequency.class);

assertThat(result).isEqualTo(ContactFrequency.NORMAL);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public CompleteSignupResponse completeSignup(MemberInfo memberInfo, ProfileCreat

return CompleteSignupResponse.builder()
.profile(profileResponse)
.isOnboardingFinished(false)
.isOnboardingFinished(true)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
import com.comatching.common.domain.enums.SocialAccountType;
import com.comatching.common.dto.member.HobbyDto;
import com.comatching.common.dto.member.ProfileTagDto;
import com.fasterxml.jackson.annotation.JsonAlias;

public record ProfileUpdateRequest(
String nickname,
String intro,
String mbti,
@JsonAlias("profileImageKey")
String profileImageUrl,
Gender gender,
LocalDate birthDate,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public void withdraw() {
this.email = "withdrawn_" + this.id + "_" + uuid + "@deleted.com";

this.password = null;
this.realName = null;

this.socialType = null;
this.socialId = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,15 @@ public void clearProfileData() {
this.nickname = "탈퇴한 사용자";
this.intro = null;
this.profileImageUrl = null;
this.birthDate = null;
this.birthDate = LocalDate.of(1970, 1, 1);
this.mbti = "UNKNOWN";
this.university = "(알 수 없음)";
this.socialAccountType = null;
this.socialAccountId = null;
this.major = "(알 수 없음)";
this.song = null;
this.point = 0;
this.isMatchable = false;
}

public void update(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.comatching.common.domain.enums.DefaultHobby;
import com.comatching.common.domain.enums.Gender;
import com.comatching.common.domain.enums.ProfileTagItem;
import com.comatching.common.domain.enums.SocialAccountType;
import com.comatching.common.dto.member.HobbyDto;
import com.comatching.common.dto.member.ProfileCreateRequest;
import com.comatching.common.dto.member.ProfileTagDto;
Expand Down Expand Up @@ -98,14 +99,19 @@ public class DummyUserSeedRunner implements ApplicationRunner {
"자유전공학부"
);
private static final List<String> IMAGE_KEY_POOL = List.of(
"default_dog", "default_cat", "default_bear", "default_fox", "default_rabbit", "default_otter"
"default",
"default_dog", "default_cat", "default_bear", "default_fox", "default_rabbit", "default_otter",
"default_wolf", "default_horse", "default_snake", "default_dinosaur"
);
private static final List<String> INTRO_POOL = List.of(
"hello there",
"coffee and coding",
"weekend hiking",
"always learning",
"music and books"
"새로운 사람을 알아가는 시간을 좋아해요.",
"대화가 잘 통하는 사람과 편하게 친해지고 싶어요.",
"주말에는 카페나 산책으로 기분 전환하는 편이에요.",
"취향을 나누고 같이 웃을 수 있는 만남을 기대해요.",
"천천히 알아가면서 좋은 인연을 만들고 싶어요.",
"맛있는 음식과 좋은 음악 이야기를 좋아해요.",
"처음엔 조용해도 친해지면 장난기가 많아요.",
"서로의 일상을 자연스럽게 응원하는 관계가 좋아요."
);

private final DummyUserSeedService dummyUserSeedService;
Expand Down Expand Up @@ -136,6 +142,12 @@ public class DummyUserSeedRunner implements ApplicationRunner {
@Value("${dummy.seed.raw-password:Dummy!1234}")
private String rawPassword;

@Value("${dummy.seed.balanced-gender:false}")
private boolean balancedGender;

@Value("${dummy.seed.instagram-account-ratio:0.0}")
private double instagramAccountRatio;

@Value("${dummy.seed.require-allowed-profile:true}")
private boolean requireAllowedProfile;

Expand All @@ -160,6 +172,8 @@ public void run(ApplicationArguments args) {
List<String> universities = resolveValuePool(universitiesRaw, DEFAULT_UNIVERSITIES);
List<String> majors = resolveValuePool(majorsRaw, DEFAULT_MAJORS);
Random random = new Random();
List<Gender> genderPlan = createGenderPlan(random, targetCount);
List<Boolean> instagramAccountPlan = createInstagramAccountPlan(random, targetCount);
String encodedPassword = passwordEncoder.encode(rawPassword);

int created = 0;
Expand All @@ -179,7 +193,11 @@ public void run(ApplicationArguments args) {
sequence++;

try {
createSingleDummyUser(random, email, nickname, encodedPassword, universities, majors);
Gender gender = resolveGender(random, genderPlan, created);
boolean useInstagramAccount = resolveInstagramAccount(instagramAccountPlan, created);
createSingleDummyUser(
random, email, nickname, encodedPassword, universities, majors, gender, useInstagramAccount
);
created++;

if (created % 100 == 0) {
Expand Down Expand Up @@ -215,17 +233,19 @@ private void createSingleDummyUser(
String nickname,
String encodedPassword,
List<String> universities,
List<String> majors
List<String> majors,
Gender gender,
boolean useInstagramAccount
) {
ProfileCreateRequest request = ProfileCreateRequest.builder()
.nickname(nickname)
.gender(randomEnum(random, Gender.values()))
.gender(gender)
.birthDate(randomBirthDate(random))
.mbti(randomElement(random, MBTI_POOL))
.intro(randomElement(random, INTRO_POOL))
.profileImageKey(randomElement(random, IMAGE_KEY_POOL))
.socialType(null)
.socialAccountId(null)
.socialType(useInstagramAccount ? SocialAccountType.INSTAGRAM : null)
.socialAccountId(useInstagramAccount ? buildInstagramAccountId(nickname) : null)
.university(randomElement(random, universities))
.major(randomElement(random, majors))
.contactFrequency(randomEnum(random, ContactFrequency.values()))
Expand All @@ -243,10 +263,67 @@ private List<HobbyDto> randomHobbies(Random random) {

int hobbyCount = 2 + random.nextInt(4); // 2~5
return pool.subList(0, hobbyCount).stream()
.map(hobby -> new HobbyDto(hobby.getCategory(), hobby.name()))
.map(hobby -> new HobbyDto(hobby.getCategory(), removeLeadingEmoji(hobby.getDisplayName())))
.toList();
}

private List<Gender> createGenderPlan(Random random, int count) {
if (!balancedGender) {
return List.of();
}

List<Gender> genders = new ArrayList<>(count);
int maleCount = count / 2;
int femaleCount = count / 2;
if (count % 2 == 1) {
if (random.nextBoolean()) {
maleCount++;
} else {
femaleCount++;
}
}

for (int i = 0; i < maleCount; i++) {
genders.add(Gender.MALE);
}
for (int i = 0; i < femaleCount; i++) {
genders.add(Gender.FEMALE);
}
Collections.shuffle(genders, random);
return genders;
}

private Gender resolveGender(Random random, List<Gender> genderPlan, int createdCount) {
if (!balancedGender) {
return randomEnum(random, Gender.values());
}
return genderPlan.get(createdCount);
}

private List<Boolean> createInstagramAccountPlan(Random random, int count) {
if (instagramAccountRatio <= 0) {
return List.of();
}

int instagramAccountCount = (int) Math.round(count * Math.min(instagramAccountRatio, 1.0));
List<Boolean> instagramAccountPlan = new ArrayList<>(count);
for (int i = 0; i < instagramAccountCount; i++) {
instagramAccountPlan.add(true);
}
for (int i = instagramAccountCount; i < count; i++) {
instagramAccountPlan.add(false);
}
Collections.shuffle(instagramAccountPlan, random);
return instagramAccountPlan;
}

private boolean resolveInstagramAccount(List<Boolean> instagramAccountPlan, int createdCount) {
if (instagramAccountRatio <= 0) {
return false;
}
return instagramAccountPlan.get(createdCount);
}

private List<ProfileTagDto> randomTags(Random random) {
List<ProfileTagItem> pool = new ArrayList<>(Arrays.asList(ProfileTagItem.values()));
Collections.shuffle(pool, random);
Expand Down Expand Up @@ -321,6 +398,18 @@ private String buildNickname(long sequence) {
return String.format("%s_%06d", nicknamePrefix, sequence);
}

private static String buildInstagramAccountId(String nickname) {
return nickname.toLowerCase(Locale.ROOT).replace("_", ".") + ".ig";
}

private static String removeLeadingEmoji(String displayName) {
int firstSpaceIndex = displayName.indexOf(' ');
if (firstSpaceIndex < 0) {
return displayName;
}
return displayName.substring(firstSpaceIndex + 1).trim();
}

private static <T> T randomElement(Random random, List<T> list) {
return list.get(random.nextInt(list.size()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@ public interface MemberService {

void updateRealName(Long memberId, String realName);

String getRealName(Long memberId);

OrdererInfoDto getOrdererInfo(Long memberId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.StringUtils;

import com.comatching.common.domain.enums.MemberRole;
import com.comatching.common.domain.enums.MemberStatus;
import com.comatching.common.dto.auth.MemberLoginDto;
import com.comatching.common.dto.member.OrdererInfoDto;
import com.comatching.common.dto.auth.SocialLoginRequestDto;
import com.comatching.common.dto.member.OrdererInfoDto;
import com.comatching.common.exception.BusinessException;
import com.comatching.user.domain.event.UserEventPublisher;
import com.comatching.user.domain.member.entity.Member;
Expand Down Expand Up @@ -77,8 +79,21 @@ public void withdrawMember(Long memberId) {
String email = member.getEmail();
member.withdraw();

// Kafka 이벤트 발행
eventPublisher.sendWithdrawEvent(memberId, email);
publishWithdrawEventAfterCommit(memberId, email);
}

private void publishWithdrawEventAfterCommit(Long memberId, String email) {
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
eventPublisher.sendWithdrawEvent(memberId, email);
return;
}

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
eventPublisher.sendWithdrawEvent(memberId, email);
}
});
}

@Override
Expand All @@ -99,6 +114,14 @@ public void updateRealName(Long memberId, String realName) {
member.updateRealName(realName.trim());
}

@Override
public String getRealName(Long memberId) {
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new BusinessException(UserErrorCode.USER_NOT_EXIST));

return member.getRealName();
}

@Override
public OrdererInfoDto getOrdererInfo(Long memberId) {
Member member = memberRepository.findById(memberId)
Expand Down
Loading