Skip to content

Commit

Permalink
feat: Feature User and Couple Domain (#53)
Browse files Browse the repository at this point in the history
* build: Add mapstruct dependency

* feat: Add User not existent ErrorType

유저를 찾기 위한 방법이 토큰 및 path 두가지 방법이 있는데 path의 경우는 존재하지 않는 자원에 접근한 것이기 때문에 404, 토큰의 경우 이미 삭제 혹은 어떠한 이유로 삭제된 유저에 대한 내용 추가

* fix: Change jwt authentication method to jws method

* feat: Create User DTO

* feat: Create User Mapper

* feat: Add update and find User Service Method

* feat: Add user update endpoint

* feat: Add Update User Profile Method

* feat: Create Couple Method
  • Loading branch information
jinsu4755 authored Jul 15, 2023
1 parent 32e4d0f commit 7ca2be1
Show file tree
Hide file tree
Showing 20 changed files with 414 additions and 10 deletions.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ dependencies {

//implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'

def mapstructVersion = "1.5.5.Final"
implementation "org.mapstruct:mapstruct:${mapstructVersion}"
annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
testAnnotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
Expand Down
43 changes: 43 additions & 0 deletions src/main/java/com/universe/uni/controller/CoupleController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.universe.uni.controller;

import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.universe.uni.dto.request.CreateCoupleRequestDto;
import com.universe.uni.dto.request.JoinCoupleRequestDto;
import com.universe.uni.dto.response.CoupleDto;
import com.universe.uni.service.CoupleServiceContract;

import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("api/couple")
public class CoupleController {

private final CoupleServiceContract coupleService;

@PostMapping("")
public CoupleDto createCoupleBy(
@AuthenticationPrincipal Long userId,
@RequestBody CreateCoupleRequestDto request
) {
return coupleService.createCoupleByStartDate(userId, request.startDate());
}

@PostMapping("join")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void joinCouple(
@AuthenticationPrincipal Long userId,
@RequestBody JoinCoupleRequestDto body
) {
coupleService.joinCouple(userId, body.inviteCode());
}
}
39 changes: 36 additions & 3 deletions src/main/java/com/universe/uni/controller/UserController.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,56 @@
package com.universe.uni.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.universe.uni.dto.UserDto;
import com.universe.uni.dto.request.UpdateUserNicknameRequestDto;
import com.universe.uni.dto.response.UserWishCouponResponseDto;
import com.universe.uni.service.UserService;
import com.universe.uni.service.CoupleServiceContract;
import com.universe.uni.service.UserServiceContract;

import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("api/user")
@RequiredArgsConstructor
@RequestMapping("/api/user")
public class UserController {

private final UserService userService;
private final UserServiceContract userService;
private final CoupleServiceContract coupleService;

@PatchMapping("")
@ResponseStatus(HttpStatus.OK)
public UserDto updateUserNickname(
@AuthenticationPrincipal long userId,
@RequestBody UpdateUserNicknameRequestDto requestDto
) {
return userService.updateUserNickname(userId, requestDto.nickname());
}

@PatchMapping(value = "/info", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseStatus(HttpStatus.OK)
public UserDto updateUserProfile(
@AuthenticationPrincipal long userId,
@RequestPart MultipartFile image,
@RequestParam String nickname,
@RequestParam String startDate
) {
final Long userCoupleId = userService.findUserCoupleId(userId);
coupleService.updateCoupleStartDate(userCoupleId, startDate);
return userService.updateUserNicknameAndImage(userId, "", nickname);
}

@GetMapping("/{userId}/wish")
@ResponseStatus(HttpStatus.OK)
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/universe/uni/domain/InviteCodeStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.universe.uni.domain;

public interface InviteCodeStrategy {
String generate();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.universe.uni.domain;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;

import org.springframework.stereotype.Component;

@Component
public class RandomInviteCodeGenerator implements InviteCodeStrategy {

public static final int MAX_CODE_LENGTH = 9;
private final Random random;

public RandomInviteCodeGenerator() throws NoSuchAlgorithmException {
this.random = SecureRandom.getInstanceStrong();
}

@Override
public String generate() {
return createRandomString();
}

private String createRandomString() {
StringBuilder randomBuf = new StringBuilder();
for (int i = 0; i < MAX_CODE_LENGTH; i++) {
if (random.nextBoolean()) {
randomBuf.append((char)((random.nextInt(26)) + 97));
} else {
randomBuf.append(random.nextInt(10));
}
}
return randomBuf.toString();
}
}
40 changes: 39 additions & 1 deletion src/main/java/com/universe/uni/domain/entity/Couple.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.hibernate.annotations.ColumnDefault;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand All @@ -21,6 +22,8 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Couple {

public static final int MAX_HEART_LIMIT = 5;

@Id
@Column(name = "couple_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -29,10 +32,45 @@ public class Couple {
@Column(name = "start_date", nullable = false)
private LocalDate startDate;

@Column(name = "invite_code", nullable = false)
@Column(name = "invite_code", nullable = false, unique = true)
private String inviteCode;

@Column(name = "heart_token", nullable = false)
@ColumnDefault("5")
private int heartToken;

@Builder
public Couple(LocalDate startDate, String inviteCode) {
this.startDate = startDate;
this.inviteCode = inviteCode;
}

public void updateStartDate(LocalDate startDate) {
this.startDate = startDate;
}

public boolean hasHeartToken() {
return heartToken > 0;
}

public boolean isMaxHeartToken() {
return this.heartToken >= 5;
}

public void increaseHeartTokenBy(int amount) {
this.heartToken += amount;
}

public void changeHeartTokenMaximum() {
if (!isMaxHeartToken()) {
this.heartToken = MAX_HEART_LIMIT;
}
}

public void decreaseHeartToken() throws IllegalStateException {
if (!this.hasHeartToken()) {
throw new IllegalStateException("Unable to decrease the heart token");
}
this.heartToken -= 1;
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/universe/uni/dto/UserDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.universe.uni.dto;

import java.io.Serializable;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import com.universe.uni.dto.response.CoupleDto;

/**
* DTO for {@link com.universe.uni.domain.entity.User}
*/
public record UserDto(
@NotNull Long id,
@Size(max = 10)
String nickname,
String image,
CoupleDto couple
) implements Serializable {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.universe.uni.dto.request;

public record CreateCoupleRequestDto(
String startDate
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.universe.uni.dto.request;

public record JoinCoupleRequestDto(
String inviteCode
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.universe.uni.dto.request;

public record UpdateUserNicknameRequestDto(
String nickname
) {
}
18 changes: 18 additions & 0 deletions src/main/java/com/universe/uni/dto/response/CoupleDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.universe.uni.dto.response;

import java.io.Serializable;
import java.time.LocalDate;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

/**
* DTO for {@link com.universe.uni.domain.entity.Couple}
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public record CoupleDto(
Long id,
LocalDate startDate,
String inviteCode,
int heartToken
) implements Serializable {
}
3 changes: 3 additions & 0 deletions src/main/java/com/universe/uni/exception/dto/ErrorType.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ public enum ErrorType {
"요청 시 토큰이 누락되어 토큰 값이 없는 경우입니다."),
ALREADY_GAME_CREATED(HttpStatus.BAD_REQUEST, "UE1003",
"이미 생성된 승부가 있습니다."),
USER_NOT_EXISTENT(HttpStatus.BAD_REQUEST, "UE1004", "존재하지 않는 유저의 요청"),
ALREADY_GAME_DONE(HttpStatus.BAD_REQUEST, "UE1005", "이미 종료된 라운드입니다."),
COUPLE_NOT_EXISTENT(HttpStatus.BAD_REQUEST, "UE1006", "존재하지 않는 커플 id 입니다"),
INVALID_INVITE_CODE(HttpStatus.BAD_REQUEST, "UE1007", "올바르지 않은 초대 코드입니다."),

/**
* 401 Unauthorized
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/universe/uni/mapper/CoupleMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.universe.uni.mapper;

import org.mapstruct.Mapper;
import org.mapstruct.MappingConstants;
import org.mapstruct.ReportingPolicy;

import com.universe.uni.domain.entity.Couple;
import com.universe.uni.dto.response.CoupleDto;

@Mapper(unmappedTargetPolicy = ReportingPolicy.WARN, componentModel = MappingConstants.ComponentModel.SPRING)
public interface CoupleMapper {

CoupleDto toCoupleDto(Couple couple);
}
11 changes: 11 additions & 0 deletions src/main/java/com/universe/uni/mapper/UserMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.universe.uni.mapper;

import org.mapstruct.Mapper;

import com.universe.uni.domain.entity.User;
import com.universe.uni.dto.UserDto;

@Mapper(componentModel = "spring")
public interface UserMapper {
UserDto toUserDto(User user);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.universe.uni.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

import com.universe.uni.domain.entity.Couple;

public interface CoupleRepository extends JpaRepository<Couple, Long> {
Optional<Couple> findByInviteCode(String inviteCode);
}

Loading

0 comments on commit 7ca2be1

Please sign in to comment.