diff --git a/build.gradle b/build.gradle index e21c873..b0de8ef 100644 --- a/build.gradle +++ b/build.gradle @@ -3,31 +3,34 @@ plugins { id 'org.springframework.boot' version '3.2.0' id 'io.spring.dependency-management' version '1.1.4' } - group = 'cotato' version = '0.0.1-SNAPSHOT' - java { sourceCompatibility = '17' } - configurations { compileOnly { extendsFrom annotationProcessor } } - repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-data-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' + + } tasks.named('test') { useJUnitPlatform() -} +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/ManagementController.java b/src/main/java/cotato/cotatomanage/management/ManagementController.java new file mode 100644 index 0000000..1fd735a --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/ManagementController.java @@ -0,0 +1,46 @@ +package cotato.cotatomanage.management; + +import cotato.cotatomanage.management.domain.Part; +import cotato.cotatomanage.management.dto.request.RegisterRequest; +import cotato.cotatomanage.management.dto.response.MemberResponse; +import cotato.cotatomanage.management.dto.response.PartResponse; +import cotato.cotatomanage.management.service.ManagementService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping(value = "/management") +public class ManagementController { + + private final ManagementService managementService; + + @PostMapping("/register") + public void registerMember(RegisterRequest registerRequest) { + log.info("멤버 등록"); + managementService.registerMember(registerRequest); + } + + @GetMapping("/part") + public MemberResponse getPartAbility( + @RequestParam Part part, + @RequestParam int periodNow + ) { + log.info("특정 파트 실력"); + return managementService.getPartAbility(part, periodNow); + } + + @GetMapping("/stats") + public PartResponse getPartStatAbility(@RequestParam int periodNow) { + log.info("파트별 실력 통계"); + return managementService.getPartStatsAbility(periodNow); + } + + @GetMapping + public MemberResponse getAllAbility(@RequestParam int periodNow) { + log.info("모든 부원 실력"); + return managementService.getAllAbility(periodNow); + } +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/domain/Member.java b/src/main/java/cotato/cotatomanage/management/domain/Member.java new file mode 100644 index 0000000..7862cbc --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/domain/Member.java @@ -0,0 +1,73 @@ +package cotato.cotatomanage.management.domain; + +import cotato.cotatomanage.management.dto.request.RegisterRequest; +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +public class Member { + + private static final int MAX_AGE = 27; + private static final int PERIOD_INCREASING_ABILITY = 2; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Size(max = 20) + private String name; + + @NotNull + private int period; + + @NotNull + private int age; + + @NotNull + @Enumerated(EnumType.STRING) + private Part part; + + // 생성자 + @Builder + private Member( + String name, + int period, + int age, + Part part) { + + this.name = name; + this.period = period; + this.age = age; + this.part = part; + } + + public static Member of(RegisterRequest registerRequest) { + return Member.builder() + .name(registerRequest.getName()) + .period(registerRequest.getPeriod()) + .age(registerRequest.getAge()) + .part(registerRequest.getPart()) + .build(); + } + + private int ability(int periodNow) { + return PERIOD_INCREASING_ABILITY * (periodNow - this.getPeriod()); + } + + public int getAbility(int periodNow) { + int ability = this.getAge() + ability(periodNow); + + if (this.getAge() <= MAX_AGE) { + return ability + Month.ability(this); + } + return ability; + } +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/domain/Month.java b/src/main/java/cotato/cotatomanage/management/domain/Month.java new file mode 100644 index 0000000..83bf119 --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/domain/Month.java @@ -0,0 +1,46 @@ +package cotato.cotatomanage.management.domain; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Stream; + +import static cotato.cotatomanage.management.domain.Part.*; + +@Getter +@RequiredArgsConstructor +public enum Month { + + PRODUCT_MONTH(PRODUCT, List.of(1, 5, 9), 10), + DESIGNER_MONTH(DESIGNER, List.of(2, 6, 10), 10), + FRONTEND_MONTH(FRONTEND, List.of(3, 7, 11), 10), + BACKEND_MONTH(BACKEND, List.of(4, 8, 12), 10); + + private Part part; + private List months; + private int number; + + Month(Part part, List months, int number) { + this.part = part; + this.months = months; + this.number = number; + } + + public static Month partParsing(Part part) { + return Stream.of(Month.values()) + .filter(partMonth -> partMonth.getPart().equals(part)) + .findFirst() + .orElse(null); + } + + public static int ability(Member member) { + Month partMonth = partParsing(member.getPart()); + int monthNow = LocalDate.now().getDayOfMonth(); + if (partMonth.months.contains(monthNow)) { + return partMonth.getNumber(); + } + return 0; + } +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/domain/Part.java b/src/main/java/cotato/cotatomanage/management/domain/Part.java new file mode 100644 index 0000000..dc6373a --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/domain/Part.java @@ -0,0 +1,28 @@ +package cotato.cotatomanage.management.domain; + + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.stream.Stream; + +@Getter +@RequiredArgsConstructor +public enum Part { + DESIGNER("디자이너"), + PRODUCT("기획"), + BACKEND("백엔드"), + FRONTEND("프론트엔드"); + + @JsonValue private final String part; + + @JsonCreator + public static Part parsing(String inputValue) { + return Stream.of(Part.values()) + .filter(part -> part.getPart().equals(inputValue)) + .findFirst() + .orElse(null); + } +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/dto/request/RegisterRequest.java b/src/main/java/cotato/cotatomanage/management/dto/request/RegisterRequest.java new file mode 100644 index 0000000..3c50ef0 --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/dto/request/RegisterRequest.java @@ -0,0 +1,16 @@ +package cotato.cotatomanage.management.dto.request; + +import cotato.cotatomanage.management.domain.Part; +import lombok.Getter; + +@Getter +public class RegisterRequest { + + private String name; + + private int period; + + private int age; + + private Part part; +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/dto/response/MemberResponse.java b/src/main/java/cotato/cotatomanage/management/dto/response/MemberResponse.java new file mode 100644 index 0000000..2860f1d --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/dto/response/MemberResponse.java @@ -0,0 +1,22 @@ +package cotato.cotatomanage.management.dto.response; + +import cotato.cotatomanage.management.vo.MemberVo; +import lombok.Builder; + +import java.util.List; + +public class MemberResponse { + + List memberInfoVoList; + + @Builder + private MemberResponse(List memberInfoVoList) { + this.memberInfoVoList = memberInfoVoList; + } + + public static MemberResponse from(List memberInfoVoList) { + return MemberResponse.builder() + .memberInfoVoList(memberInfoVoList) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/dto/response/PartResponse.java b/src/main/java/cotato/cotatomanage/management/dto/response/PartResponse.java new file mode 100644 index 0000000..ad866e8 --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/dto/response/PartResponse.java @@ -0,0 +1,22 @@ +package cotato.cotatomanage.management.dto.response; + +import cotato.cotatomanage.management.vo.PartVo; +import lombok.Builder; + +import java.util.List; + +public class PartResponse { + + List partInfoVoList; + + @Builder + private PartResponse(List partInfoVoList) { + this.partInfoVoList = partInfoVoList; + } + + public static PartResponse from(List partInfoVoList) { + return PartResponse.builder() + .partInfoVoList(partInfoVoList) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/helper/ManagementHelper.java b/src/main/java/cotato/cotatomanage/management/helper/ManagementHelper.java new file mode 100644 index 0000000..b18d129 --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/helper/ManagementHelper.java @@ -0,0 +1,31 @@ +package cotato.cotatomanage.management.helper; + +import cotato.cotatomanage.management.domain.Part; +import cotato.cotatomanage.management.vo.MemberVo; +import cotato.cotatomanage.management.vo.PartVo; +import org.springframework.stereotype.Component; + +import java.util.Comparator; +import java.util.List; + +@Component +public class ManagementHelper { + + public void sortPart(List partInfoVoList) { + partInfoVoList.sort(Comparator + .comparing(PartVo::getAbility) + .reversed() + .thenComparing(PartVo::getCount) + .thenComparing(PartVo::getPart, Comparator.comparing(Part::ordinal)) + ); + } + + public void sortAll(List memberInfoList) { + memberInfoList.sort(Comparator + .comparing(MemberVo::getAbility) + .reversed() + .thenComparing(MemberVo::getAge, Comparator.reverseOrder()) + .thenComparing(MemberVo::getPeriod) + .thenComparing(MemberVo::getName)); + } +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/mapper/ManagementMapper.java b/src/main/java/cotato/cotatomanage/management/mapper/ManagementMapper.java new file mode 100644 index 0000000..2a2137a --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/mapper/ManagementMapper.java @@ -0,0 +1,48 @@ +package cotato.cotatomanage.management.mapper; + +import cotato.cotatomanage.management.domain.Member; +import cotato.cotatomanage.management.domain.Part; +import cotato.cotatomanage.management.dto.response.PartResponse; +import cotato.cotatomanage.management.helper.ManagementHelper; +import cotato.cotatomanage.management.repository.MemberRepository; +import cotato.cotatomanage.management.vo.MemberVo; +import cotato.cotatomanage.management.dto.response.MemberResponse; +import cotato.cotatomanage.management.vo.PartVo; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +@RequiredArgsConstructor +public class ManagementMapper { + + private final MemberRepository memberRepository; + private final ManagementHelper managementHelper; + + public MemberResponse toMemberInfoListResponse(List memberList, int periodNow) { + List memberInfoList = + memberList.stream() + .map(member -> MemberVo.of(member, periodNow)) + .toList(); + managementHelper.sortAll(memberInfoList); + return MemberResponse.from(memberInfoList); + } + + public PartResponse toPartInfoListResponse(List partInfoVoList) { + managementHelper.sortPart(partInfoVoList); + return PartResponse.from(partInfoVoList); + } + + public List toPartInfoVoList(int periodNow) { + List partInfoVoList = new ArrayList<>(); + Part[] parts = Part.class.getEnumConstants(); + for (Part part : parts) { + List memberList = memberRepository.findAllByPart(part); + PartVo partInfoVo = PartVo.of(part, memberList, periodNow); + partInfoVoList.add(partInfoVo); + } + return partInfoVoList; + } +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/repository/MemberRepository.java b/src/main/java/cotato/cotatomanage/management/repository/MemberRepository.java new file mode 100644 index 0000000..4946660 --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/repository/MemberRepository.java @@ -0,0 +1,13 @@ +package cotato.cotatomanage.management.repository; + + +import cotato.cotatomanage.management.domain.Member; +import cotato.cotatomanage.management.domain.Part; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface MemberRepository extends JpaRepository { + + List findAllByPart(Part part); +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/service/ManagementService.java b/src/main/java/cotato/cotatomanage/management/service/ManagementService.java new file mode 100644 index 0000000..0b71a0f --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/service/ManagementService.java @@ -0,0 +1,50 @@ +package cotato.cotatomanage.management.service; + +import cotato.cotatomanage.management.domain.Member; +import cotato.cotatomanage.management.domain.Part; +import cotato.cotatomanage.management.dto.request.RegisterRequest; +import cotato.cotatomanage.management.dto.response.MemberResponse; +import cotato.cotatomanage.management.dto.response.PartResponse; +import cotato.cotatomanage.management.mapper.ManagementMapper; +import cotato.cotatomanage.management.repository.MemberRepository; +import cotato.cotatomanage.management.vo.PartVo; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ManagementService { + + private final MemberRepository memberRepository; + private final ManagementMapper managementMapper; + + @Transactional + public void registerMember(RegisterRequest registerRequest) { + Member member = Member.of(registerRequest); + memberRepository.save(member); + } + + @Transactional + public MemberResponse getPartAbility(Part part, int periodNow) { + List memberList = memberRepository.findAllByPart(part); + return managementMapper.toMemberInfoListResponse(memberList, periodNow); + } + + @Transactional + public PartResponse getPartStatsAbility(int periodNow) { + List partInfoVoList = managementMapper.toPartInfoVoList(periodNow); + return managementMapper.toPartInfoListResponse(partInfoVoList); + } + + @Transactional + public MemberResponse getAllAbility(int periodNow) { + List memberList = memberRepository.findAll(); + return managementMapper.toMemberInfoListResponse(memberList, periodNow); + } + +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/vo/MemberVo.java b/src/main/java/cotato/cotatomanage/management/vo/MemberVo.java new file mode 100644 index 0000000..9338bdd --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/vo/MemberVo.java @@ -0,0 +1,35 @@ +package cotato.cotatomanage.management.vo; + +import cotato.cotatomanage.management.domain.Member; +import cotato.cotatomanage.management.domain.Part; +import lombok.Builder; +import lombok.Getter; + +@Getter +public class MemberVo { + + private String name; + private int period; + private int age; + private Part part; + private int ability; + + @Builder + private MemberVo(String name, int period, int age, Part part, int ability) { + this.name = name; + this.period = period; + this.age = age; + this.part = part; + this.ability = ability; + } + + public static MemberVo of(Member member, int periodNow) { + return MemberVo.builder() + .name(member.getName()) + .period(member.getPeriod()) + .age(member.getAge()) + .part(member.getPart()) + .ability(member.getAbility(periodNow)) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/cotato/cotatomanage/management/vo/PartVo.java b/src/main/java/cotato/cotatomanage/management/vo/PartVo.java new file mode 100644 index 0000000..661e952 --- /dev/null +++ b/src/main/java/cotato/cotatomanage/management/vo/PartVo.java @@ -0,0 +1,42 @@ +package cotato.cotatomanage.management.vo; + +import cotato.cotatomanage.management.domain.Member; +import cotato.cotatomanage.management.domain.Part; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +public class PartVo { + private Part part; + private int ability; + private int count; + + @Builder + private PartVo(Part part, int ability, int count) { + this.part = part; + this.ability = ability; + this.count = getCount(); + } + + public static PartVo of(Part part, List memberList, int periodNow) { + return PartVo.builder() + .part(part) + .ability(calcAbilityStats(memberList, periodNow)) + .count(memberList.size()) + .build(); + } + + private static int calcAbilityStats(List memberList, int periodNow) { + if (memberList == null || memberList.isEmpty()) { + return 0; + } + + int sum = 0; + for (Member member : memberList) { + sum += member.getAbility(periodNow); + } + return (int) sum / memberList.size(); + } +} \ No newline at end of file