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
@@ -1,16 +1,16 @@
package com.example.userservice.domain.subscriber.controller;


import com.example.userservice.domain.member.dto.response.CreateMemberResponseDto;
import com.example.userservice.domain.subscriber.dto.request.SubscribeRequestDto;
import com.example.userservice.domain.subscriber.service.SubscriberService;
import com.example.userservice.global.common.CommonResDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RestController
@RequiredArgsConstructor
@Slf4j
Expand All @@ -20,8 +20,8 @@ public class SubscriberController {
private final SubscriberService subscriberService;

@PostMapping("/subscribe")
public ResponseEntity<?> subscribe(@RequestBody SubscribeRequestDto subscribeRequestDto) {
subscriberService.subscribe(subscribeRequestDto.getEmail());
public ResponseEntity<?> subscribe(@Valid @RequestBody SubscribeRequestDto subscribeRequestDto) {
subscriberService.subscribe(subscribeRequestDto.getEmail(), subscribeRequestDto.getKeywords());
return ResponseEntity.status(HttpStatus.CREATED).body("구독 완료: " + subscribeRequestDto.getEmail());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
package com.example.userservice.domain.subscriber.dto.request;

import com.example.userservice.domain.subscriber.dto.validator.ValidKeywords;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.apache.commons.lang.StringUtils;

import javax.validation.constraints.Size;
import java.util.List;
import java.util.stream.Collectors;

@NoArgsConstructor
@Getter
public class SubscribeRequestDto {
@Getter
private String email;

@ValidKeywords(minSize = 1, maxSize = 3, keywordMinLength = 2, keywordMaxLength = 20,
message = "Keywords must be 1~3, each 2~20 characters and unique.")
private List<String> keywords;

// 중복 제거
public List<String> getKeywords() {
return keywords.stream()
.map(String::trim)
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.example.userservice.domain.subscriber.dto.validator;

import org.apache.commons.lang.StringUtils;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class KeywordsValidator implements ConstraintValidator<ValidKeywords, List<String>> {

private static final Pattern VALID_KEYWORD_PATTERN = Pattern.compile("^[a-zA-Z0-9가-힣 ]+$");

private int minSize;
private int maxSize;
private int keywordMinLength;
private int keywordMaxLength;

@Override
public void initialize(ValidKeywords constraintAnnotation) {
this.minSize = constraintAnnotation.minSize();
this.maxSize = constraintAnnotation.maxSize();
this.keywordMinLength = constraintAnnotation.keywordMinLength();
this.keywordMaxLength = constraintAnnotation.keywordMaxLength();
}

@Override
public boolean isValid(List<String> value, ConstraintValidatorContext constraintValidatorContext) {
if (value == null) return false;

List<String> processed = value.stream()
.map(String::trim)
.filter(StringUtils::isNotBlank)
.distinct()
.collect(Collectors.toList());

if (processed.size() < minSize || processed.size() > maxSize) {
return false;
}

return processed.stream()
.allMatch(s -> s.length() >= keywordMinLength
&& s.length() <= keywordMaxLength
&& VALID_KEYWORD_PATTERN.matcher(s).matches());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.example.userservice.domain.subscriber.dto.validator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = KeywordsValidator.class)
public @interface ValidKeywords {

String message() default "Invalid keywords.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};

int minSize() default 1;
int maxSize() default 3;
int keywordMinLength() default 1;
int keywordMaxLength() default 200;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.example.userservice.domain.subscriber.entity;

import com.example.userservice.global.entity.BaseTimeEntity;
import lombok.*;

import javax.persistence.*;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Interest extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String keyword;

@ManyToOne
@JoinColumn(name = "subscriber_id")
private Subscriber subscriber;

public Interest(String keyword) {
this.keyword = keyword;
}

public void setSubscriber(Subscriber subscriber) {
this.subscriber = subscriber;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import com.example.userservice.global.entity.BaseTimeEntity;
import lombok.*;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;

import javax.persistence.*;
import java.util.Objects;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
Expand All @@ -27,12 +27,29 @@ public class Subscriber extends BaseTimeEntity {
@Column(nullable = false)
private boolean subscribed = true; // 구독 상태 (true: 구독 중, false: 구독 취소)

@OneToMany(mappedBy = "subscriber", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Interest> interests = new ArrayList<>();

public Subscriber(String email) {
this.email = email;
}

public Subscriber(String email, List<String> keywords) {
this.email = email;

keywords.stream()
.map(Interest::new)
.forEach(this::add);
}

public void unsubscribe() {
this.subscribed = false;
}

// 연관관계 메서드
public void add(Interest interest) {
interests.add(interest);
interest.setSubscriber(this);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.example.userservice.domain.subscriber.service;

import java.util.List;

public interface SubscriberService {
void subscribe(String email);
void subscribe(String email, List<String> keywords);
void unsubscribe(String email);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.example.userservice.domain.subscriber.service.impl;

import com.example.userservice.domain.member.dto.request.SignUpRequestDto;
import com.example.userservice.domain.subscriber.entity.Subscriber;
import com.example.userservice.domain.subscriber.repository.SubscriberRepository;
import com.example.userservice.domain.subscriber.service.SubscriberService;
Expand All @@ -25,15 +24,15 @@ public class SubscriberServiceImpl implements SubscriberService {

@Override
@Transactional
public void subscribe(String email) {
public void subscribe(String email, List<String> keywords) {
// 이메일 중복 체크
subscriberRepository.findByEmail(email).ifPresent(subscriber -> {
throw new IllegalArgumentException("이미 구독된 이메일입니다.");
});
emailVerifyCheck(email);

// 새 구독자 저장
Subscriber subscriber = new Subscriber(email);
// 관심 키워드와 함께 새 구독자 저장
Subscriber subscriber = new Subscriber(email, keywords);
subscriberRepository.save(subscriber);
}

Expand Down
Loading