diff --git a/.github/workflows/CD.yml b/.github/workflows/CD.yml index 80d88f4d6..b2d213b63 100644 --- a/.github/workflows/CD.yml +++ b/.github/workflows/CD.yml @@ -32,7 +32,7 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Build on Dockerhub + - name: Docker Build run: docker build -t teamwalkhub/walkhub-server . - name: Push on Dockerhub diff --git a/build.gradle b/build.gradle index 284bac5b7..8db748383 100644 --- a/build.gradle +++ b/build.gradle @@ -51,7 +51,6 @@ dependencies { implementation 'org.hibernate:hibernate-ehcache:5.4.30.Final' implementation 'org.hibernate:hibernate-jcache:5.6.5.Final' implementation 'org.ehcache:ehcache:3.9.2' - } test { diff --git a/src/main/java/com/walkhub/walkhub/domain/auth/exception/PhoneNumberNotFoundException.java b/src/main/java/com/walkhub/walkhub/domain/auth/exception/PhoneNumberNotFoundException.java new file mode 100644 index 000000000..75c924a7d --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/auth/exception/PhoneNumberNotFoundException.java @@ -0,0 +1,14 @@ +package com.walkhub.walkhub.domain.auth.exception; + +import com.walkhub.walkhub.global.error.exception.ErrorCode; +import com.walkhub.walkhub.global.error.exception.WalkhubException; + +public class PhoneNumberNotFoundException extends WalkhubException { + + public static final WalkhubException EXCEPTION = + new PhoneNumberNotFoundException(); + + private PhoneNumberNotFoundException() { + super(ErrorCode.PHONE_NUMBER_NOT_FOUND); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/auth/presentation/AuthController.java b/src/main/java/com/walkhub/walkhub/domain/auth/presentation/AuthController.java index b0ccd1e58..a72c98bcf 100644 --- a/src/main/java/com/walkhub/walkhub/domain/auth/presentation/AuthController.java +++ b/src/main/java/com/walkhub/walkhub/domain/auth/presentation/AuthController.java @@ -1,8 +1,12 @@ package com.walkhub.walkhub.domain.auth.presentation; +import com.walkhub.walkhub.domain.auth.presentation.dto.request.CheckAuthCodeRequest; +import com.walkhub.walkhub.domain.auth.presentation.dto.request.CheckAccountIdRequest; import com.walkhub.walkhub.domain.auth.presentation.dto.request.SignInRequest; import com.walkhub.walkhub.domain.auth.presentation.dto.response.UserAccessTokenResponse; import com.walkhub.walkhub.domain.auth.presentation.dto.response.UserTokenResponse; +import com.walkhub.walkhub.domain.auth.service.CheckAccountIdExistsService; +import com.walkhub.walkhub.domain.auth.service.CheckAuthCodeExistsService; import com.walkhub.walkhub.domain.auth.service.TokenRefreshService; import com.walkhub.walkhub.domain.auth.service.UserSignInService; import lombok.RequiredArgsConstructor; @@ -11,25 +15,38 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; @RequiredArgsConstructor @RestController -@RequestMapping("/users/token") +@RequestMapping("/users") public class AuthController { private final UserSignInService userSignInService; private final TokenRefreshService tokenRefreshService; + private final CheckAccountIdExistsService checkAccountIdExistsService; + private final CheckAuthCodeExistsService checkAuthCodeExistsService; - @PostMapping + @PostMapping("/token") public UserTokenResponse userSignIn(@RequestBody @Valid SignInRequest request) { return userSignInService.execute(request); } - @PatchMapping + @PatchMapping("/token") public UserAccessTokenResponse userTokenRefresh(@RequestHeader("Refresh-Token") String refreshToken) { return tokenRefreshService.execute(refreshToken); } + + @RequestMapping(value = "/account-id", method = RequestMethod.HEAD) + public void checkAccountIdExists(@RequestBody @Valid CheckAccountIdRequest request) { + checkAccountIdExistsService.execute(request); + } + + @RequestMapping(value = "/verification-codes", method = RequestMethod.HEAD) + public void checkAuthCodeExists(@RequestBody @Valid CheckAuthCodeRequest request) { + checkAuthCodeExistsService.execute(request); + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/request/CheckAccountIdRequest.java b/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/request/CheckAccountIdRequest.java new file mode 100644 index 000000000..061815b37 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/request/CheckAccountIdRequest.java @@ -0,0 +1,13 @@ +package com.walkhub.walkhub.domain.auth.presentation.dto.request; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Getter +@NoArgsConstructor +public class CheckAccountIdRequest { + @NotBlank(message = "account_id는 Null, 공백, 띄어쓰기를 허용하지 않습니다.") + private String accountId; +} diff --git a/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/request/CheckAuthCodeRequest.java b/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/request/CheckAuthCodeRequest.java new file mode 100644 index 000000000..b18ab1610 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/request/CheckAuthCodeRequest.java @@ -0,0 +1,18 @@ +package com.walkhub.walkhub.domain.auth.presentation.dto.request; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Getter +@NoArgsConstructor +public class CheckAuthCodeRequest { + + @NotBlank(message = "phone_number는 Null, 공백, 띄어쓰기를 허용하지 않습니다.") + private String phoneNumber; + + @NotBlank(message = "auth_code는 Null, 공백, 띄어쓰기를 허용하지 않습니다.") + private String authCode; + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/response/UserAccessTokenResponse.java b/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/response/UserAccessTokenResponse.java index 7b345fc87..6314d27ac 100644 --- a/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/response/UserAccessTokenResponse.java +++ b/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/response/UserAccessTokenResponse.java @@ -1,10 +1,10 @@ package com.walkhub.walkhub.domain.auth.presentation.dto.response; +import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Builder; import lombok.Getter; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; +import java.time.ZonedDateTime; @Getter @Builder @@ -12,6 +12,6 @@ public class UserAccessTokenResponse { private final String accessToken; - @DateTimeFormat(pattern = "yyyy-MM-ddThh:mm:SS") - private final LocalDateTime expiredAt; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private final ZonedDateTime expiredAt; } diff --git a/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/response/UserTokenResponse.java b/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/response/UserTokenResponse.java index d3dcb09d3..af404783c 100644 --- a/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/response/UserTokenResponse.java +++ b/src/main/java/com/walkhub/walkhub/domain/auth/presentation/dto/response/UserTokenResponse.java @@ -1,22 +1,25 @@ package com.walkhub.walkhub.domain.auth.presentation.dto.response; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.walkhub.walkhub.domain.user.domain.type.Sex; import com.walkhub.walkhub.global.enums.Authority; import lombok.Builder; import lombok.Getter; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; +import java.math.BigDecimal; +import java.time.ZonedDateTime; @Getter @Builder public class UserTokenResponse { private final String accessToken; - + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private final ZonedDateTime expiredAt; private final String refreshToken; - - @DateTimeFormat(pattern = "yyyy-MM-ddThh:mm:SS") - private final LocalDateTime expiredAt; - private final Authority authority; + private final BigDecimal height; + private final Integer weight; + private final Sex sex; + } diff --git a/src/main/java/com/walkhub/walkhub/domain/auth/service/CheckAccountIdExistsService.java b/src/main/java/com/walkhub/walkhub/domain/auth/service/CheckAccountIdExistsService.java new file mode 100644 index 000000000..caf71055c --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/auth/service/CheckAccountIdExistsService.java @@ -0,0 +1,17 @@ +package com.walkhub.walkhub.domain.auth.service; + +import com.walkhub.walkhub.domain.auth.presentation.dto.request.CheckAccountIdRequest; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class CheckAccountIdExistsService { + + private final UserFacade userFacade; + + public void execute(CheckAccountIdRequest request) { + userFacade.checkUserExists(request.getAccountId()); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/auth/service/CheckAuthCodeExistsService.java b/src/main/java/com/walkhub/walkhub/domain/auth/service/CheckAuthCodeExistsService.java new file mode 100644 index 000000000..9f396984c --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/auth/service/CheckAuthCodeExistsService.java @@ -0,0 +1,26 @@ +package com.walkhub.walkhub.domain.auth.service; + +import com.walkhub.walkhub.domain.auth.exception.PhoneNumberNotFoundException; +import com.walkhub.walkhub.domain.auth.presentation.dto.request.CheckAuthCodeRequest; +import com.walkhub.walkhub.domain.user.domain.UserAuthCode; +import com.walkhub.walkhub.domain.user.domain.repository.UserAuthCodeRepository; +import com.walkhub.walkhub.domain.user.exception.UserAuthCodeNotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class CheckAuthCodeExistsService { + + private final UserAuthCodeRepository userAuthCodeRepository; + + public void execute(CheckAuthCodeRequest request) { + UserAuthCode authCode = userAuthCodeRepository.findById(request.getPhoneNumber()) + .orElseThrow(() -> PhoneNumberNotFoundException.EXCEPTION); + + if (!authCode.getCode().equals(request.getAuthCode())) { + throw UserAuthCodeNotFoundException.EXCEPTION; + } + } + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/auth/service/TokenRefreshService.java b/src/main/java/com/walkhub/walkhub/domain/auth/service/TokenRefreshService.java index f31fcb0d6..101bf5fc8 100644 --- a/src/main/java/com/walkhub/walkhub/domain/auth/service/TokenRefreshService.java +++ b/src/main/java/com/walkhub/walkhub/domain/auth/service/TokenRefreshService.java @@ -10,7 +10,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; +import java.time.ZonedDateTime; @RequiredArgsConstructor @Service @@ -29,7 +29,7 @@ public UserAccessTokenResponse execute(String refreshToken) { String accessToken = jwtTokenProvider.generateAccessToken(redisRefreshToken.getAccountId()); return UserAccessTokenResponse.builder() .accessToken(accessToken) - .expiredAt(LocalDateTime.now().plusSeconds(jwtProperties.getAccessExp())) + .expiredAt(ZonedDateTime.now().plusSeconds(jwtProperties.getAccessExp())) .build(); } diff --git a/src/main/java/com/walkhub/walkhub/domain/auth/service/UserSignInService.java b/src/main/java/com/walkhub/walkhub/domain/auth/service/UserSignInService.java index 56786feec..8a52af71d 100644 --- a/src/main/java/com/walkhub/walkhub/domain/auth/service/UserSignInService.java +++ b/src/main/java/com/walkhub/walkhub/domain/auth/service/UserSignInService.java @@ -1,12 +1,11 @@ package com.walkhub.walkhub.domain.auth.service; -import com.walkhub.walkhub.domain.auth.domain.RefreshToken; -import com.walkhub.walkhub.domain.auth.domain.repository.RefreshTokenRepository; import com.walkhub.walkhub.domain.auth.exception.PasswordMismatchException; import com.walkhub.walkhub.domain.auth.presentation.dto.request.SignInRequest; import com.walkhub.walkhub.domain.auth.presentation.dto.response.UserTokenResponse; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.domain.repository.UserRepository; +import com.walkhub.walkhub.domain.user.domain.type.HealthInfo; import com.walkhub.walkhub.domain.user.exception.UserNotFoundException; import com.walkhub.walkhub.global.security.jwt.JwtProperties; import com.walkhub.walkhub.global.security.jwt.JwtTokenProvider; @@ -15,7 +14,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; +import java.time.ZonedDateTime; @RequiredArgsConstructor @Service @@ -23,7 +22,6 @@ public class UserSignInService { private final UserRepository userRepository; private final JwtProperties jwtProperties; - private final RefreshTokenRepository refreshTokenRepository; private final JwtTokenProvider jwtTokenProvider; private final PasswordEncoder passwordEncoder; @@ -31,7 +29,7 @@ public class UserSignInService { public UserTokenResponse execute(SignInRequest request) { User user = userRepository.findByAccountId(request.getAccountId()) .orElseThrow(() -> UserNotFoundException.EXCEPTION); - + HealthInfo healthInfo = user.getHealthInfo(); if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { throw PasswordMismatchException.EXCEPTION; } @@ -41,20 +39,14 @@ public UserTokenResponse execute(SignInRequest request) { String accessToken = jwtTokenProvider.generateAccessToken(user.getAccountId()); String refreshToken = jwtTokenProvider.generateRefreshToken(user.getAccountId()); - - refreshTokenRepository.save( - RefreshToken.builder() - .accountId(user.getAccountId()) - .token(refreshToken) - .timeToLive(jwtProperties.getRefreshExp()) - .build() - ); - return UserTokenResponse.builder() .accessToken(accessToken) - .expiredAt(LocalDateTime.now().plusSeconds(jwtProperties.getAccessExp())) + .expiredAt(ZonedDateTime.now().plusSeconds(jwtProperties.getAccessExp())) .refreshToken(refreshToken) .authority(user.getAuthority()) + .height(healthInfo.getHeight()) + .weight(healthInfo.getWeight()) + .sex(user.getSex()) .build(); } diff --git a/src/main/java/com/walkhub/walkhub/domain/badge/domain/Badge.java b/src/main/java/com/walkhub/walkhub/domain/badge/domain/Badge.java index ad71425d0..a8770a0d2 100644 --- a/src/main/java/com/walkhub/walkhub/domain/badge/domain/Badge.java +++ b/src/main/java/com/walkhub/walkhub/domain/badge/domain/Badge.java @@ -9,11 +9,12 @@ import org.hibernate.annotations.ColumnDefault; import javax.persistence.CascadeType; -import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import javax.persistence.OneToMany; import java.util.List; @@ -26,18 +27,24 @@ public class Badge extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(length = 20, nullable = false) + @NotNull + @Size(max = 20) private String name; @ColumnDefault(DefaultImage.BADGE_IMAGE) private String imageUrl; + @NotNull + private String unlockCondition; + @OneToMany(mappedBy = "badge", cascade = CascadeType.REMOVE) private List badgeCollections; @Builder - public Badge(String name, String imageUrl) { + public Badge(String name, String imageUrl, + String unlockCondition) { this.name = name; this.imageUrl = imageUrl; + this.unlockCondition = unlockCondition; } } diff --git a/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/BadgeCollectionRepository.java b/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/BadgeCollectionRepository.java index c6e317b40..6b312b25f 100644 --- a/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/BadgeCollectionRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/BadgeCollectionRepository.java @@ -5,9 +5,10 @@ import com.walkhub.walkhub.domain.user.domain.User; import org.springframework.data.repository.CrudRepository; +import java.util.List; import java.util.Optional; public interface BadgeCollectionRepository extends CrudRepository { - + List findAllByUserId(Long userId); Optional findByBadgeIdAndUser(Long badgeId, User user); } diff --git a/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/CustomBadgeRepository.java b/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/CustomBadgeRepository.java index b7a53862a..f138ae926 100644 --- a/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/CustomBadgeRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/CustomBadgeRepository.java @@ -1,9 +1,11 @@ package com.walkhub.walkhub.domain.badge.domain.repository; -import com.walkhub.walkhub.domain.badge.domain.repository.vo.ClaimBadgeVO; +import com.walkhub.walkhub.domain.badge.domain.repository.vo.DefaultBadgeVO; +import com.walkhub.walkhub.domain.badge.domain.repository.vo.MyBadgeVo; import java.util.List; public interface CustomBadgeRepository { - List findAllByBadgeCollectionsNotIn(Long userId); + List findAllByBadgeCollectionsNotIn(Long userId); + List findAllByBadgeCollections(Long userId); } diff --git a/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/CustomBadgeRepositoryImpl.java b/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/CustomBadgeRepositoryImpl.java index 43bce1bc4..6f9d92ece 100644 --- a/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/CustomBadgeRepositoryImpl.java +++ b/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/CustomBadgeRepositoryImpl.java @@ -1,8 +1,10 @@ package com.walkhub.walkhub.domain.badge.domain.repository; import com.querydsl.jpa.impl.JPAQueryFactory; -import com.walkhub.walkhub.domain.badge.domain.repository.vo.ClaimBadgeVO; -import com.walkhub.walkhub.domain.badge.domain.repository.vo.QClaimBadgeVO; +import com.walkhub.walkhub.domain.badge.domain.repository.vo.DefaultBadgeVO; +import com.walkhub.walkhub.domain.badge.domain.repository.vo.MyBadgeVo; +import com.walkhub.walkhub.domain.badge.domain.repository.vo.QDefaultBadgeVO; +import com.walkhub.walkhub.domain.badge.domain.repository.vo.QMyBadgeVo; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -19,9 +21,9 @@ public class CustomBadgeRepositoryImpl implements CustomBadgeRepository { private final JPAQueryFactory query; @Override - public List findAllByBadgeCollectionsNotIn(Long userId) { + public List findAllByBadgeCollectionsNotIn(Long userId) { return query - .select(new QClaimBadgeVO( + .select(new QDefaultBadgeVO( badge.id, badge.name, badge.imageUrl @@ -31,7 +33,22 @@ public List findAllByBadgeCollectionsNotIn(Long userId) { .on(badgeCollection.badge.eq(badge)) .leftJoin(user) .on(badgeCollection.user.eq(user)) - .where(user.isNull().or(user.id.eq(userId).not())) + .fetch(); + } + + @Override + public List findAllByBadgeCollections(Long userId) { + return query + .select(new QMyBadgeVo( + badge.id, + badge.name, + badge.imageUrl, + user.id.max().eq(userId) + )) + .from(badge) + .leftJoin(badge.badgeCollections, badgeCollection) + .leftJoin(badgeCollection.user, user).on(user.id.eq(userId)) + .groupBy(badge.id) .fetch(); } diff --git a/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/vo/ClaimBadgeVO.java b/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/vo/DefaultBadgeVO.java similarity index 78% rename from src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/vo/ClaimBadgeVO.java rename to src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/vo/DefaultBadgeVO.java index 61a2a78e2..4e75cb68e 100644 --- a/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/vo/ClaimBadgeVO.java +++ b/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/vo/DefaultBadgeVO.java @@ -4,13 +4,13 @@ import lombok.Getter; @Getter -public class ClaimBadgeVO { +public class DefaultBadgeVO { private final Long badgeId; private final String name; private final String imageUrl; @QueryProjection - public ClaimBadgeVO(Long badgeId, String name, String imageUrl) { + public DefaultBadgeVO(Long badgeId, String name, String imageUrl) { this.badgeId = badgeId; this.name = name; this.imageUrl = imageUrl; diff --git a/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/vo/MyBadgeVo.java b/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/vo/MyBadgeVo.java new file mode 100644 index 000000000..c5b74b2a8 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/badge/domain/repository/vo/MyBadgeVo.java @@ -0,0 +1,23 @@ +package com.walkhub.walkhub.domain.badge.domain.repository.vo; + +import com.querydsl.core.annotations.QueryProjection; +import lombok.Getter; + +@Getter +public class MyBadgeVo { + + private final Long badgeId; + private final String name; + private final String imageUrl; + private final boolean isMine; + + @QueryProjection + public MyBadgeVo(Long badgeId, String name, + String imageUrl, boolean isMine) { + this.badgeId = badgeId; + this.name = name; + this.imageUrl = imageUrl; + this.isMine = isMine; + } + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/badge/presentation/BadgeController.java b/src/main/java/com/walkhub/walkhub/domain/badge/presentation/BadgeController.java index ee5611b2b..90d104411 100644 --- a/src/main/java/com/walkhub/walkhub/domain/badge/presentation/BadgeController.java +++ b/src/main/java/com/walkhub/walkhub/domain/badge/presentation/BadgeController.java @@ -1,7 +1,11 @@ package com.walkhub.walkhub.domain.badge.presentation; +import com.walkhub.walkhub.domain.badge.presentation.dto.response.QueryUserBadgeListResponse; +import com.walkhub.walkhub.domain.badge.service.QueryUserBadgeListService; import com.walkhub.walkhub.domain.badge.presentation.dto.response.ClaimBadgeResponse; +import com.walkhub.walkhub.domain.badge.presentation.dto.response.QueryMyBadgeListResponse; import com.walkhub.walkhub.domain.badge.service.ClaimBadgeService; +import com.walkhub.walkhub.domain.badge.service.QueryMyBadgeListService; import com.walkhub.walkhub.domain.badge.service.SetTitleBadgeService; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -18,7 +22,9 @@ public class BadgeController { private final SetTitleBadgeService setTitleBadgeService; + private final QueryUserBadgeListService queryUserBadgeListService; private final ClaimBadgeService claimBadgeService; + private final QueryMyBadgeListService myBadgeListService; @ResponseStatus(HttpStatus.NO_CONTENT) @PutMapping("/{badge-id}") @@ -26,9 +32,19 @@ public void setTitleBadge(@PathVariable("badge-id") Long badgeId) { setTitleBadgeService.execute(badgeId); } + @GetMapping("/{user-id}") + public QueryUserBadgeListResponse queryUserBadgeList(@PathVariable(name = "user-id") Long userId) { + return queryUserBadgeListService.execute(userId); + } + @GetMapping("/new") public ClaimBadgeResponse claimBadge() { return claimBadgeService.execute(); } + @GetMapping + public QueryMyBadgeListResponse myBadgeList() { + return myBadgeListService.execute(); + } + } diff --git a/src/main/java/com/walkhub/walkhub/domain/badge/presentation/dto/response/QueryMyBadgeListResponse.java b/src/main/java/com/walkhub/walkhub/domain/badge/presentation/dto/response/QueryMyBadgeListResponse.java new file mode 100644 index 000000000..911e93901 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/badge/presentation/dto/response/QueryMyBadgeListResponse.java @@ -0,0 +1,13 @@ +package com.walkhub.walkhub.domain.badge.presentation.dto.response; + +import com.walkhub.walkhub.domain.badge.domain.repository.vo.MyBadgeVo; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class QueryMyBadgeListResponse { + private final List badgeLists; +} diff --git a/src/main/java/com/walkhub/walkhub/domain/badge/presentation/dto/response/QueryUserBadgeListResponse.java b/src/main/java/com/walkhub/walkhub/domain/badge/presentation/dto/response/QueryUserBadgeListResponse.java new file mode 100644 index 000000000..ecfa50ce7 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/badge/presentation/dto/response/QueryUserBadgeListResponse.java @@ -0,0 +1,22 @@ +package com.walkhub.walkhub.domain.badge.presentation.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class QueryUserBadgeListResponse { + + private final List userBadgeList; + + @Getter + @Builder + public static class DefaultBadgeResponse{ + private final String name; + private final String imageUrl; + private final String condition; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/badge/service/ClaimBadgeService.java b/src/main/java/com/walkhub/walkhub/domain/badge/service/ClaimBadgeService.java index 44e7bc8fc..c514bb5bd 100644 --- a/src/main/java/com/walkhub/walkhub/domain/badge/service/ClaimBadgeService.java +++ b/src/main/java/com/walkhub/walkhub/domain/badge/service/ClaimBadgeService.java @@ -1,8 +1,9 @@ package com.walkhub.walkhub.domain.badge.service; import com.walkhub.walkhub.domain.badge.domain.repository.BadgeRepository; -import com.walkhub.walkhub.domain.badge.domain.repository.vo.ClaimBadgeVO; +import com.walkhub.walkhub.domain.badge.domain.repository.vo.DefaultBadgeVO; import com.walkhub.walkhub.domain.badge.presentation.dto.response.ClaimBadgeResponse; +import com.walkhub.walkhub.domain.badge.presentation.dto.response.ClaimBadgeResponse.BadgeResponse; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.facade.UserFacade; import lombok.RequiredArgsConstructor; @@ -20,10 +21,10 @@ public class ClaimBadgeService { public ClaimBadgeResponse execute() { User user = userFacade.getCurrentUser(); - List badgeResponses = new ArrayList<>(); - List claimBadgeList = badgeRepository.findAllByBadgeCollectionsNotIn(user.getId()); + List badgeResponses = new ArrayList<>(); + List claimBadgeList = badgeRepository.findAllByBadgeCollectionsNotIn(user.getId()); - for (ClaimBadgeVO vo : claimBadgeList) { + for (DefaultBadgeVO vo : claimBadgeList) { // todo 뱃지가 추가되면 그때마다 if 처리를 하겠습니다. badgeResponses.add(buildBadgeResponse(vo)); } @@ -31,8 +32,8 @@ public ClaimBadgeResponse execute() { return new ClaimBadgeResponse(badgeResponses); } - private ClaimBadgeResponse.BadgeResponse buildBadgeResponse(ClaimBadgeVO vo) { - return ClaimBadgeResponse.BadgeResponse.builder() + private BadgeResponse buildBadgeResponse(DefaultBadgeVO vo) { + return BadgeResponse.builder() .badgeId(vo.getBadgeId()) .name(vo.getName()) .imageUrl(vo.getImageUrl()) diff --git a/src/main/java/com/walkhub/walkhub/domain/badge/service/QueryMyBadgeListService.java b/src/main/java/com/walkhub/walkhub/domain/badge/service/QueryMyBadgeListService.java new file mode 100644 index 000000000..72023e8e7 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/badge/service/QueryMyBadgeListService.java @@ -0,0 +1,27 @@ +package com.walkhub.walkhub.domain.badge.service; + +import com.walkhub.walkhub.domain.badge.domain.repository.BadgeRepository; +import com.walkhub.walkhub.domain.badge.domain.repository.vo.MyBadgeVo; +import com.walkhub.walkhub.domain.badge.presentation.dto.response.QueryMyBadgeListResponse; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@RequiredArgsConstructor +@Service +public class QueryMyBadgeListService { + + private final BadgeRepository badgeRepository; + private final UserFacade userFacade; + + @Transactional(readOnly = true) + public QueryMyBadgeListResponse execute() { + User user = userFacade.getCurrentUser(); + List badgeVOList = badgeRepository.findAllByBadgeCollections(user.getId()); + return new QueryMyBadgeListResponse(badgeVOList); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/badge/service/QueryUserBadgeListService.java b/src/main/java/com/walkhub/walkhub/domain/badge/service/QueryUserBadgeListService.java new file mode 100644 index 000000000..8bff41404 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/badge/service/QueryUserBadgeListService.java @@ -0,0 +1,33 @@ +package com.walkhub.walkhub.domain.badge.service; + +import com.walkhub.walkhub.domain.badge.domain.repository.BadgeCollectionRepository; +import com.walkhub.walkhub.domain.badge.presentation.dto.response.QueryUserBadgeListResponse; +import com.walkhub.walkhub.domain.badge.presentation.dto.response.QueryUserBadgeListResponse.DefaultBadgeResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class QueryUserBadgeListService { + + private final BadgeCollectionRepository badgeCollectionRepository; + + @Transactional(readOnly = true) + public QueryUserBadgeListResponse execute(Long userId) { + + List badgeList = badgeCollectionRepository.findAllByUserId(userId) + .stream() + .map(badgeCollection -> DefaultBadgeResponse.builder() + .name(badgeCollection.getBadge().getName()) + .imageUrl(badgeCollection.getBadge().getImageUrl()) + .condition(badgeCollection.getBadge().getUnlockCondition()) + .build()) + .collect(Collectors.toList()); + + return new QueryUserBadgeListResponse(badgeList); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/calorielevel/domain/CalorieLevel.java b/src/main/java/com/walkhub/walkhub/domain/calorielevel/domain/CalorieLevel.java index b71a1734b..13bf55f3c 100644 --- a/src/main/java/com/walkhub/walkhub/domain/calorielevel/domain/CalorieLevel.java +++ b/src/main/java/com/walkhub/walkhub/domain/calorielevel/domain/CalorieLevel.java @@ -1,54 +1,53 @@ package com.walkhub.walkhub.domain.calorielevel.domain; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.validation.constraints.NotNull; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.hibernate.validator.constraints.Length; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @Entity public class CalorieLevel { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - @NotNull - @Length(max = 10) - private String foodName; + @NotNull + @Column(length = 10) + private String foodName; - @NotNull - private Integer size; + @NotNull + private Integer size; - @NotNull - private String message; + @NotNull + private String message; - @NotNull - private String foodImageUrl; + @NotNull + private String foodImageUrl; - @NotNull - @Column(columnDefinition = "TINYINT") - private Integer level; + @NotNull + @Column(columnDefinition = "TINYINT") + private Integer level; - @NotNull - private Integer calorie; + @NotNull + private Double calorie; - @Builder - public CalorieLevel(String foodName, Integer size, String message, String foodImageUrl, Integer level, - Integer calorie) { - this.foodName = foodName; - this.size = size; - this.message = message; - this.foodImageUrl = foodImageUrl; - this.level = level; - this.calorie = calorie; - } + @Builder + public CalorieLevel(String foodName, Integer size, String message, String foodImageUrl, Integer level, Double calorie) { + this.foodName = foodName; + this.size = size; + this.message = message; + this.foodImageUrl = foodImageUrl; + this.level = level; + this.calorie = calorie; + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/calorielevel/domain/repository/CalorieLevelRepository.java b/src/main/java/com/walkhub/walkhub/domain/calorielevel/domain/repository/CalorieLevelRepository.java index a3e3e37dd..d849707a4 100644 --- a/src/main/java/com/walkhub/walkhub/domain/calorielevel/domain/repository/CalorieLevelRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/calorielevel/domain/repository/CalorieLevelRepository.java @@ -1,10 +1,13 @@ package com.walkhub.walkhub.domain.calorielevel.domain.repository; import com.walkhub.walkhub.domain.calorielevel.domain.CalorieLevel; +import java.util.Optional; import org.springframework.data.repository.CrudRepository; import java.util.List; public interface CalorieLevelRepository extends CrudRepository { List findAllBy(); + + Optional findByLevel(Integer level); } diff --git a/src/main/java/com/walkhub/walkhub/domain/calorielevel/service/CalorieLevelListService.java b/src/main/java/com/walkhub/walkhub/domain/calorielevel/service/CalorieLevelListService.java index 6a195724d..420e0a86e 100644 --- a/src/main/java/com/walkhub/walkhub/domain/calorielevel/service/CalorieLevelListService.java +++ b/src/main/java/com/walkhub/walkhub/domain/calorielevel/service/CalorieLevelListService.java @@ -3,6 +3,7 @@ import com.walkhub.walkhub.domain.calorielevel.domain.CalorieLevel; import com.walkhub.walkhub.domain.calorielevel.domain.repository.CalorieLevelRepository; import com.walkhub.walkhub.domain.user.presentation.dto.response.CalorieLevelListResponse; +import com.walkhub.walkhub.domain.user.presentation.dto.response.CalorieLevelListResponse.CalorieLevelResponse; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -18,7 +19,7 @@ public class CalorieLevelListService { @Transactional(readOnly = true) public CalorieLevelListResponse execute() { - List results = + List results = calorieLevelRepository.findAllBy() .stream() .map(this::calorieLevelResponse) @@ -27,9 +28,10 @@ public CalorieLevelListResponse execute() { return new CalorieLevelListResponse(results); } - private CalorieLevelListResponse.CalorieLevelResponse calorieLevelResponse(CalorieLevel calorieLevel) { - return CalorieLevelListResponse.CalorieLevelResponse.builder() + private CalorieLevelResponse calorieLevelResponse(CalorieLevel calorieLevel) { + return CalorieLevelResponse.builder() .levelId(calorieLevel.getId()) + .level(calorieLevel.getLevel()) .foodImageUrl(calorieLevel.getFoodImageUrl()) .foodName(calorieLevel.getFoodName()) .calorie(calorieLevel.getCalorie()) diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/Challenge.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/Challenge.java index df5442686..226b168e2 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/Challenge.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/Challenge.java @@ -11,7 +11,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.ColumnDefault; -import org.hibernate.validator.constraints.Length; import javax.persistence.CascadeType; import javax.persistence.Column; @@ -57,18 +56,18 @@ public class Challenge { private String award; @NotNull - @Length(max = 6) @Enumerated(EnumType.STRING) + @Column(length = 6) private UserScope userScope; @NotNull - @Length(max = 3) @Enumerated(EnumType.STRING) + @Column(length = 3) private GoalScope goalScope; @NotNull - @Length(max = 8) @Enumerated(EnumType.STRING) + @Column(length = 8) private GoalType goalType; @NotNull @@ -119,4 +118,8 @@ public void updateChallenge(UpdateChallengeRequest request) { public boolean isWriter(Long userId) { return user.getId().equals(userId); } + + public void setUserNull() { + this.user = null; + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/ChallengeStatus.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/ChallengeStatus.java index 8e0a84642..ae349ee14 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/ChallengeStatus.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/ChallengeStatus.java @@ -5,9 +5,15 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.hibernate.annotations.ColumnDefault; -import javax.persistence.*; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.validation.constraints.NotNull; +import java.time.LocalDate; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -25,15 +31,13 @@ public class ChallengeStatus { @JoinColumn(name = "user_id") private User user; - @ColumnDefault("1") - @Column(nullable = false) - private Long successCount; + @NotNull + private final LocalDate createdAt = LocalDate.now(); @Builder - public ChallengeStatus(Challenge challenge, User user, Long successCount) { + public ChallengeStatus(Challenge challenge, User user) { this.challenge = challenge; this.user = user; - this.successCount = successCount; } } diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeRepository.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeRepository.java index 1ca814cac..056a1eb0a 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeRepository.java @@ -2,13 +2,21 @@ import com.walkhub.walkhub.domain.challenge.domain.Challenge; import com.walkhub.walkhub.domain.school.domain.School; -import java.util.List; +import com.walkhub.walkhub.domain.user.domain.User; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; -public interface ChallengeRepository extends CrudRepository { +import java.time.LocalDate; +import java.util.List; + +public interface ChallengeRepository extends CrudRepository, ChallengeRepositoryCustom { + + @Query("select c from Challenge c where c.user.school = :school") + List findAllBySchool(@Param("school") School school); + + List findAllByUser(User user); + + void deleteAllByUserAndEndAtAfter(User user, LocalDate now); - @Query("select c from Challenge c where c.user.school = :school") - List findAllBySchool(@Param("school") School school); } diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeRepositoryCustom.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeRepositoryCustom.java new file mode 100644 index 000000000..cab38e825 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeRepositoryCustom.java @@ -0,0 +1,11 @@ +package com.walkhub.walkhub.domain.challenge.domain.repository; + +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.ShowChallengeVO; +import com.walkhub.walkhub.domain.user.domain.User; + +import java.util.List; + +public interface ChallengeRepositoryCustom { + List queryChallenge(User user); + public List queryClosedChallenge(User user); +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeRepositoryCustomImpl.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeRepositoryCustomImpl.java new file mode 100644 index 000000000..e5b842b2e --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeRepositoryCustomImpl.java @@ -0,0 +1,62 @@ +package com.walkhub.walkhub.domain.challenge.domain.repository; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.QShowChallengeVO; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.ShowChallengeVO; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.global.enums.UserScope; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDate; +import java.util.List; + +import static com.walkhub.walkhub.domain.challenge.domain.QChallenge.challenge; +import static com.walkhub.walkhub.domain.user.domain.QUser.user; + +@RequiredArgsConstructor +public class ChallengeRepositoryCustomImpl implements ChallengeRepositoryCustom { + + private final JPAQueryFactory query; + + @Override + public List queryChallenge(User userParam) { + return query + .select(showChallengeVO()) + .from(challenge) + .join(challenge.user, user) + .where( + (challenge.userScope.eq(UserScope.ALL).or(challenge.user.school.eq(userParam.getSchool()))), + (challenge.endAt.after(LocalDate.now())) + ) + .fetch(); + } + + @Override + public List queryClosedChallenge(User userParam) { + return query + .select(showChallengeVO()) + .from(challenge) + .join(challenge.user, user) + .where( + (challenge.userScope.eq(UserScope.ALL).or(challenge.user.school.eq(userParam.getSchool()))), + (challenge.endAt.before(LocalDate.now())) + ) + .fetch(); + } + + private QShowChallengeVO showChallengeVO() { + return new QShowChallengeVO( + challenge.id.as("challengeId"), + challenge.name, + challenge.startAt, + challenge.endAt, + challenge.imageUrl, + challenge.userScope, + challenge.goalScope, + challenge.goalType, + user.id.as("writerId"), + user.name.as("writerName"), + user.profileImageUrl.as("profileImageUrl") + ); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeStatusRepository.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeStatusRepository.java index 5a8d4426e..5ac769f31 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeStatusRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeStatusRepository.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.Optional; -public interface ChallengeStatusRepository extends CrudRepository, CustomChallengeRepository { +public interface ChallengeStatusRepository extends CrudRepository, ChallengeStatusRepositoryCustom { Optional findByChallengeAndUser(Challenge challenge, User user); List findAllByUser(User user); } diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeStatusRepositoryCustom.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeStatusRepositoryCustom.java new file mode 100644 index 000000000..e71d0bdac --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeStatusRepositoryCustom.java @@ -0,0 +1,21 @@ +package com.walkhub.walkhub.domain.challenge.domain.repository; + +import com.walkhub.walkhub.domain.challenge.domain.Challenge; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.ChallengeProgressVO; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.RelatedChallengeParticipantsVO; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.ShowChallengeVO; +import com.walkhub.walkhub.domain.challenge.domain.type.ChallengeParticipantsOrder; +import com.walkhub.walkhub.domain.challenge.domain.type.ChallengeParticipantsScope; +import com.walkhub.walkhub.domain.challenge.domain.type.SuccessScope; +import com.walkhub.walkhub.domain.school.domain.School; +import com.walkhub.walkhub.domain.user.domain.User; + +import java.util.List; + +public interface ChallengeStatusRepositoryCustom { + Integer getParticipantsCountByChallengeId(Long challengeId); + List getRelatedChallengeParticipantsList(Long challengeId, School school, Integer grade, Integer classNum); + List queryChallengeProgress(Challenge challenge, ChallengeParticipantsScope participantsScope, ChallengeParticipantsOrder participantsOrder, SuccessScope successScope, Long page); + List getAllChallengesByUser(User user); + void deleteNotOverChallengeStatusByUserId(Long userId); +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeStatusRepositoryCustomImpl.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeStatusRepositoryCustomImpl.java new file mode 100644 index 000000000..e1252ed19 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/ChallengeStatusRepositoryCustomImpl.java @@ -0,0 +1,254 @@ +package com.walkhub.walkhub.domain.challenge.domain.repository; + +import com.querydsl.core.types.Order; +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.types.dsl.CaseBuilder; +import com.querydsl.core.types.dsl.Expressions; +import com.querydsl.core.types.dsl.NumberExpression; +import com.querydsl.core.types.dsl.NumberPath; +import com.querydsl.jpa.JPAExpressions; +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.walkhub.walkhub.domain.challenge.domain.Challenge; +import com.walkhub.walkhub.domain.challenge.domain.ChallengeStatus; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.ChallengeProgressVO; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.QChallengeProgressVO; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.QRelatedChallengeParticipantsVO; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.QShowChallengeVO; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.RelatedChallengeParticipantsVO; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.ShowChallengeVO; +import com.walkhub.walkhub.domain.challenge.domain.type.ChallengeParticipantsOrder; +import com.walkhub.walkhub.domain.challenge.domain.type.ChallengeParticipantsScope; +import com.walkhub.walkhub.domain.challenge.domain.type.GoalScope; +import com.walkhub.walkhub.domain.challenge.domain.type.SuccessScope; +import com.walkhub.walkhub.domain.exercise.domain.type.GoalType; +import com.walkhub.walkhub.domain.school.domain.School; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.global.enums.Authority; +import com.walkhub.walkhub.global.enums.UserScope; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDate; +import java.util.List; + +import static com.querydsl.jpa.JPAExpressions.select; +import static com.walkhub.walkhub.domain.challenge.domain.QChallenge.challenge; +import static com.walkhub.walkhub.domain.challenge.domain.QChallengeStatus.challengeStatus; +import static com.walkhub.walkhub.domain.exercise.domain.QExerciseAnalysis.exerciseAnalysis; +import static com.walkhub.walkhub.domain.school.domain.QSchool.school; +import static com.walkhub.walkhub.domain.user.domain.QSection.section; +import static com.walkhub.walkhub.domain.user.domain.QUser.user; + +@RequiredArgsConstructor +public class ChallengeStatusRepositoryCustomImpl implements ChallengeStatusRepositoryCustom { + private static final int PARTICIPANTS_SIZE = 9; + + private final JPAQueryFactory queryFactory; + + @Override + public Integer getParticipantsCountByChallengeId(Long challengeId) { + List participantsList = queryFactory + .select(challengeStatus) + .from(challengeStatus) + .where(challengeStatus.challenge.id.eq(challengeId)) + .fetch(); + return participantsList.size(); + } + + @Override + public List getRelatedChallengeParticipantsList(Long challengeId, School school, Integer grade, Integer classNum) { + return queryFactory + .select(new QRelatedChallengeParticipantsVO( + user.id.as("userId"), + user.name, + user.profileImageUrl + )) + .from(user) + .join(challengeStatus) + .on(challengeStatus.user.eq(user)) + .join(section) + .on(section.id.eq(user.section.id)) + .where( + challengeStatus.challenge.id.eq(challengeId), + user.school.eq(school) + ) + .orderBy( + section.grade.subtract(grade).abs().asc(), + section.classNum.subtract(classNum).abs().asc(), + user.school.id.subtract(school.getId()).abs().asc() + ) + .limit(3) + .fetch(); + } + + @Override + public void deleteNotOverChallengeStatusByUserId(Long userId) { + queryFactory + .delete(challengeStatus) + .where(challengeStatus.user.id.eq(userId), + challengeStatus.challenge.id.in( + JPAExpressions + .select(challenge.id) + .from(challenge) + .where(challenge.userScope.eq(UserScope.CLASS).or(challenge.userScope.eq(UserScope.GRADE))) + .where(challenge.endAt.after(LocalDate.now()).and(challengeStatus.user.id.eq(userId))) + ) + ) + .execute(); + } + + @Override + public List getAllChallengesByUser(User user1) { + return queryFactory + .select(new QShowChallengeVO( + challenge.id.as("challengeId"), + challenge.name, + challenge.startAt, + challenge.endAt, + challenge.imageUrl, + challenge.userScope, + challenge.goalScope, + challenge.goalType, + user.id.as("userId"), + user.name.as("writerName"), + user.profileImageUrl.as("profileImageUrl") + )) + .from(challenge) + .join(challenge.user, user) + .join(challengeStatus) + .on(challengeStatus.challenge.eq(challenge)) + .where(challengeStatus.user.eq(user1)) + .fetch(); + } + + private BooleanExpression challengeDateFilter(Challenge challenge) { + return exerciseAnalysis.date.goe(challenge.getStartAt()) + .and(exerciseAnalysis.date.goe(challengeStatus.createdAt)) + .and(exerciseAnalysis.date.loe(challenge.getEndAt())); + } + + @Override + public List queryChallengeProgress( + Challenge challenge, + ChallengeParticipantsScope participantsScope, + ChallengeParticipantsOrder participantsOrder, + SuccessScope successScope, + Long page + ) { + return queryFactory.select(new QChallengeProgressVO( + user.id, + user.name, + section.grade, + section.classNum, + user.number, + school.name, + user.profileImageUrl, + Expressions.asNumber( + select(exerciseAnalysis.walkCount.sum()) + .from(exerciseAnalysis) + .where(exerciseAnalysis.user.eq(user), + challengeDateFilter(challenge)) + ).intValue(), + getChallengeProgress(challenge).multiply(100).round().longValue(), + exerciseAnalysis.date.count().goe(challenge.getSuccessStandard()), + new CaseBuilder() + .when(exerciseAnalysis.date.count().goe(challenge.getSuccessStandard())) + .then(exerciseAnalysis.date.max()) + .otherwise(Expressions.nullExpression()))) + .from(user) + .offset(page == null ? 0 : page * PARTICIPANTS_SIZE) + .limit(PARTICIPANTS_SIZE) + .leftJoin(user.section, section) + .join(user.school, school) + .join(user.challengeStatuses, challengeStatus) + .leftJoin(user.exerciseAnalyses, exerciseAnalysis) + .on(challengeDateFilter(challenge), + isChallengeSuccessFilter(challenge)) + .where(userScopeFilter(participantsScope)) + .orderBy(challengeParticipantsOrder(participantsOrder)) + .having(challengeSuccessFilter(successScope, challenge)) + .groupBy(user.id) + .fetch(); + } + + private BooleanExpression isChallengeSuccessFilter(Challenge challenge) { + if (challenge.getGoalScope() == GoalScope.DAY) { + return isChallengeSuccessInDayScope(challenge); + } + + return isChallengeSuccessInAllScope(challenge); + } + + private BooleanExpression isChallengeSuccessInDayScope(Challenge challenge) { + NumberPath exerciseAmount = exerciseAnalysis.distance; + + if (challenge.getGoalType() == GoalType.WALK) { + exerciseAmount = exerciseAnalysis.walkCount; + } + + return exerciseAmount.goe(challenge.getGoal()); + } + + private BooleanExpression isChallengeSuccessInAllScope(Challenge challenge) { + NumberPath exerciseAmount = exerciseAnalysis.distance; + + if (challenge.getGoalType() == GoalType.WALK) { + exerciseAmount = exerciseAnalysis.walkCount; + } + + return JPAExpressions.select(exerciseAmount.sum()) + .from(exerciseAnalysis) + .where( + exerciseAnalysis.user.eq(user), + challengeDateFilter(challenge) + ) + .goe(challenge.getGoal()); + } + + private BooleanExpression userScopeFilter(ChallengeParticipantsScope challengeParticipantsScope) { + if (challengeParticipantsScope == ChallengeParticipantsScope.STUDENT) { + return user.authority.eq(Authority.USER); + } else if (challengeParticipantsScope == ChallengeParticipantsScope.TEACHER) { + return user.authority.eq(Authority.TEACHER); + } else { + return null; + } + } + + private BooleanExpression challengeSuccessFilter(SuccessScope successScope, Challenge challenge) { + if (successScope == SuccessScope.TRUE) { + return exerciseAnalysis.date.count().goe(challenge.getSuccessStandard()); + } else if (successScope == SuccessScope.FALSE) { + return exerciseAnalysis.date.count().lt(challenge.getSuccessStandard()); + } else { + return null; + } + } + + private NumberExpression getChallengeProgress(Challenge challenge) { + NumberExpression successProgress; + + if (challenge.getGoalScope() == GoalScope.ALL) { + if (challenge.getGoalType() == GoalType.DISTANCE) { + successProgress = exerciseAnalysis.distance.sum(); + } else { + successProgress = exerciseAnalysis.walkCount.sum(); + } + return successProgress.divide(challenge.getGoal()).longValue(); + } else { + return exerciseAnalysis.date.count().divide(challenge.getSuccessStandard()); + } + } + + private OrderSpecifier challengeParticipantsOrder(ChallengeParticipantsOrder challengeParticipantsOrder) { + if (challengeParticipantsOrder == ChallengeParticipantsOrder.SUCCESS_DATE) { + return new OrderSpecifier<>(Order.DESC, exerciseAnalysis.date.max()); + } else if (challengeParticipantsOrder == ChallengeParticipantsOrder.PROGRESS) { + return new OrderSpecifier<>(Order.DESC, exerciseAnalysis.walkCount.sum().divide(exerciseAnalysis.date.count())); + } else if (challengeParticipantsOrder == ChallengeParticipantsOrder.SCHOOL_NAME) { + return new OrderSpecifier<>(Order.ASC, school.name); + } else { + return new OrderSpecifier<>(Order.ASC, user.name); + } + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/CustomChallengeRepository.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/CustomChallengeRepository.java deleted file mode 100644 index 6dd4d1f7e..000000000 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/CustomChallengeRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.walkhub.walkhub.domain.challenge.domain.repository; - -import com.walkhub.walkhub.domain.challenge.domain.Challenge; -import com.walkhub.walkhub.domain.challenge.domain.repository.vo.ChallengeParticipantsVO; -import com.walkhub.walkhub.domain.challenge.domain.type.SuccessScope; - -import java.util.List; - -public interface CustomChallengeRepository { - List queryChallengeParticipantsList(Challenge challenge, SuccessScope successScope); -} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/CustomChallengeRepositoryImpl.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/CustomChallengeRepositoryImpl.java deleted file mode 100644 index 6e278b290..000000000 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/CustomChallengeRepositoryImpl.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.walkhub.walkhub.domain.challenge.domain.repository; - -import com.querydsl.core.group.GroupBy; -import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.jpa.JPAExpressions; -import com.querydsl.jpa.impl.JPAQueryFactory; -import com.walkhub.walkhub.domain.challenge.domain.Challenge; -import com.walkhub.walkhub.domain.challenge.domain.repository.vo.ChallengeParticipantsVO; -import com.walkhub.walkhub.domain.challenge.domain.repository.vo.QChallengeParticipantsVO; -import com.walkhub.walkhub.domain.challenge.domain.type.GoalScope; -import com.walkhub.walkhub.domain.challenge.domain.type.SuccessScope; -import com.walkhub.walkhub.domain.exercise.domain.type.GoalType; -import lombok.RequiredArgsConstructor; - -import java.util.List; - -import static com.querydsl.core.group.GroupBy.groupBy; -import static com.walkhub.walkhub.domain.challenge.domain.QChallengeStatus.challengeStatus; -import static com.walkhub.walkhub.domain.exercise.domain.QExerciseAnalysis.exerciseAnalysis; -import static com.walkhub.walkhub.domain.school.domain.QSchool.school; -import static com.walkhub.walkhub.domain.user.domain.QSection.section; -import static com.walkhub.walkhub.domain.user.domain.QUser.user; - -@RequiredArgsConstructor -public class CustomChallengeRepositoryImpl implements CustomChallengeRepository { - - private final JPAQueryFactory jpaQueryFactory; - - @Override - public List queryChallengeParticipantsList(Challenge challenge, SuccessScope successScope) { - return jpaQueryFactory - .selectFrom(user) - .join(user.school, school) - .leftJoin(user.section, section) - .join(user.exerciseAnalyses, exerciseAnalysis) - .join(user.challengeStatuses, challengeStatus) - .join(challengeStatus.challenge) - .on(challengeStatus.challenge.eq(challenge)) - .where( - successScopeFilter(challenge, successScope), - goalTypeFilter(challenge), - exerciseAnalysis.date.between(challenge.getStartAt(), challenge.getEndAt()) - ) - .orderBy(user.name.asc(), user.id.asc()) - .transform(groupBy(user.name, user.id) - .list(new QChallengeParticipantsVO( - user.id.as("userId"), - user.section.grade, - user.section.classNum, - user.number, - user.name, - user.profileImageUrl, - user.school.name.as("schoolName"), - challengeStatus.successCount.goe(challenge.getSuccessStandard()).as("isSuccess"), - GroupBy.list(exerciseAnalysis.date)) - ) - ); - } - - private BooleanExpression successScopeFilter(Challenge challenge, SuccessScope successScope) { - switch (successScope) { - case TRUE: { - return challengeStatus.successCount.goe(challenge.getSuccessStandard()); - } - case FALSE: { - return challengeStatus.successCount.lt(challenge.getSuccessStandard()); - } - default: { - return null; - } - } - } - - private BooleanExpression goalTypeFilter(Challenge challenge) { - if (challenge.getGoalType() == GoalType.WALK) { - return isChallengeSuccessByWalkCount(challenge); - } - - return isChallengeSuccessByDistance(challenge); - } - - private BooleanExpression isChallengeSuccessByWalkCount(Challenge challenge) { - if (challenge.getGoalScope() == GoalScope.DAY) { - return exerciseAnalysis.walkCount.goe(challenge.getGoal()); - } - - return JPAExpressions.select(exerciseAnalysis.walkCount.sum()) - .from(exerciseAnalysis) - .where(exerciseAnalysis.date.between(challenge.getStartAt(), challenge.getEndAt())) - .goe(challenge.getGoal()); - } - - private BooleanExpression isChallengeSuccessByDistance(Challenge challenge) { - if (challenge.getGoalScope() == GoalScope.DAY) { - return exerciseAnalysis.distance.goe(challenge.getGoal()); - } - - return JPAExpressions.select(exerciseAnalysis.distance.sum()) - .from(exerciseAnalysis) - .where(exerciseAnalysis.date.between(challenge.getStartAt(), challenge.getEndAt())) - .goe(challenge.getGoal()); - } - -} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/ChallengeParticipantsVO.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/ChallengeParticipantsVO.java index c3b381ec5..740e5b752 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/ChallengeParticipantsVO.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/ChallengeParticipantsVO.java @@ -15,12 +15,13 @@ public class ChallengeParticipantsVO { private final String name; private final String profileImageUrl; private final String schoolName; - private final Boolean isSuccess; private final List exerciseAnalysesDates; + private final Boolean isSuccess; @QueryProjection public ChallengeParticipantsVO(Long userId, Integer grade, Integer classNum, Integer number, - String name, String profileImageUrl, String schoolName, Boolean isSuccess, List exerciseAnalysesDates) { + String name, String profileImageUrl, String schoolName, + Boolean isSuccess, List exerciseAnalysesDates) { this.userId = userId; this.grade = grade; this.classNum = classNum; @@ -28,7 +29,7 @@ public ChallengeParticipantsVO(Long userId, Integer grade, Integer classNum, Int this.name = name; this.profileImageUrl = profileImageUrl; this.schoolName = schoolName; - this.isSuccess = isSuccess; this.exerciseAnalysesDates = exerciseAnalysesDates; + this.isSuccess = isSuccess; } } diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/ChallengeProgressVO.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/ChallengeProgressVO.java new file mode 100644 index 000000000..d80f2ff49 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/ChallengeProgressVO.java @@ -0,0 +1,40 @@ +package com.walkhub.walkhub.domain.challenge.domain.repository.vo; + +import com.querydsl.core.annotations.QueryProjection; +import lombok.Getter; + +import java.time.LocalDate; + +@Getter +public class ChallengeProgressVO { + private final Long userId; + private final String userName; + private final Integer grade; + private final Integer classNum; + private final Integer number; + private final String schoolName; + private final String profileImageUrl; + private final Integer totalWalkCount; + private final Long progress; + private final Boolean isSuccess; + private final LocalDate successDate; + + @QueryProjection + public ChallengeProgressVO(Long userId, String userName, Integer grade, + Integer classNum, Integer number, String schoolName, + String profileImageUrl, Integer totalWalkCount, Long progress, + Boolean isSuccess, LocalDate successDate) { + this.userId = userId; + this.userName = userName; + this.grade = grade; + this.classNum = classNum; + this.number = number; + this.schoolName = schoolName; + this.profileImageUrl = profileImageUrl; + this.totalWalkCount = totalWalkCount; + this.progress = progress; + this.isSuccess = isSuccess; + this.successDate = successDate; + } + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/RelatedChallengeParticipantsVO.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/RelatedChallengeParticipantsVO.java new file mode 100644 index 000000000..9bbf9c9b1 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/RelatedChallengeParticipantsVO.java @@ -0,0 +1,18 @@ +package com.walkhub.walkhub.domain.challenge.domain.repository.vo; + +import com.querydsl.core.annotations.QueryProjection; +import lombok.Getter; + +@Getter +public class RelatedChallengeParticipantsVO { + private final Long userId; + private final String name; + private final String profileImageUrl; + + @QueryProjection + public RelatedChallengeParticipantsVO(Long userId, String name, String profileImageUrl) { + this.userId = userId; + this.name = name; + this.profileImageUrl = profileImageUrl; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/ShowChallengeVO.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/ShowChallengeVO.java new file mode 100644 index 000000000..dc8c8a4f6 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/repository/vo/ShowChallengeVO.java @@ -0,0 +1,42 @@ +package com.walkhub.walkhub.domain.challenge.domain.repository.vo; + +import com.querydsl.core.annotations.QueryProjection; +import com.walkhub.walkhub.domain.challenge.domain.type.GoalScope; +import com.walkhub.walkhub.domain.exercise.domain.type.GoalType; +import com.walkhub.walkhub.global.enums.UserScope; +import lombok.Getter; + +import java.time.LocalDate; + +@Getter +public class ShowChallengeVO { + + private final Long challengeId; + private final String name; + private final LocalDate startAt; + private final LocalDate endAt; + private final String imageUrl; + private final UserScope userScope; + private final GoalScope goalScope; + private final GoalType goalType; + private final Long writerId; + private final String writerName; + private final String profileImageUrl; + + @QueryProjection + public ShowChallengeVO(Long challengeId, String name, LocalDate startAt, LocalDate endAt, + String imageUrl, UserScope userScope, GoalScope goalScope, + GoalType goalType, Long writerId, String writerName, String profileImageUrl) { + this.challengeId = challengeId; + this.name = name; + this.startAt = startAt; + this.endAt = endAt; + this.imageUrl = imageUrl; + this.userScope = userScope; + this.goalScope = goalScope; + this.goalType = goalType; + this.writerId = writerId; + this.writerName = writerName; + this.profileImageUrl = profileImageUrl; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/type/ChallengeParticipantsOrder.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/type/ChallengeParticipantsOrder.java new file mode 100644 index 000000000..45a3dd8e6 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/type/ChallengeParticipantsOrder.java @@ -0,0 +1,8 @@ +package com.walkhub.walkhub.domain.challenge.domain.type; + +public enum ChallengeParticipantsOrder { + SCHOOL_NAME, + USER_NAME, + SUCCESS_DATE, + PROGRESS +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/domain/type/ChallengeParticipantsScope.java b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/type/ChallengeParticipantsScope.java new file mode 100644 index 000000000..fc49c1b3d --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/domain/type/ChallengeParticipantsScope.java @@ -0,0 +1,7 @@ +package com.walkhub.walkhub.domain.challenge.domain.type; + +public enum ChallengeParticipantsScope { + ALL, + STUDENT, + TEACHER +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/facade/ChallengeFacade.java b/src/main/java/com/walkhub/walkhub/domain/challenge/facade/ChallengeFacade.java index f7d36d459..3904a71bb 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/facade/ChallengeFacade.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/facade/ChallengeFacade.java @@ -3,6 +3,7 @@ import com.walkhub.walkhub.domain.challenge.domain.Challenge; import com.walkhub.walkhub.domain.challenge.domain.repository.ChallengeRepository; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.ShowChallengeVO; import com.walkhub.walkhub.domain.challenge.exception.ChallengeNotFoundException; import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeListResponse.ChallengeResponse; import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeListResponse.Writer; @@ -20,21 +21,21 @@ public Challenge getChallengeById(Long id) { .orElseThrow(() -> ChallengeNotFoundException.EXCEPTION); } - public ChallengeResponse challengeResponseBuilder(Challenge challenge) { + public ChallengeResponse challengeResponseBuilder(ShowChallengeVO vo) { return ChallengeResponse.builder() - .id(challenge.getId()) - .name(challenge.getName()) - .startAt(challenge.getStartAt()) - .endAt(challenge.getEndAt()) - .imageUrl(challenge.getImageUrl()) - .userScope(challenge.getUserScope()) - .goalScope(challenge.getGoalScope()) - .goalType(challenge.getGoalType()) - .writer(Writer.builder() - .userId(challenge.getUser().getId()) - .name(challenge.getUser().getName()) - .profileImageUrl(challenge.getUser().getProfileImageUrl()) - .build()) - .build(); + .challengeId(vo.getChallengeId()) + .name(vo.getName()) + .startAt(vo.getStartAt()) + .endAt(vo.getEndAt()) + .imageUrl(vo.getImageUrl()) + .userScope(vo.getUserScope()) + .goalScope(vo.getGoalScope()) + .goalType(vo.getGoalType()) + .writer(Writer.builder() + .userId(vo.getChallengeId()) + .name(vo.getWriterName()) + .profileImageUrl(vo.getProfileImageUrl()) + .build()) + .build(); } } diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/ChallengeController.java b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/ChallengeController.java index 0445be77f..ed644b3da 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/ChallengeController.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/ChallengeController.java @@ -1,31 +1,16 @@ package com.walkhub.walkhub.domain.challenge.presenstation; -import com.walkhub.walkhub.domain.challenge.domain.type.SuccessScope; import com.walkhub.walkhub.domain.challenge.presenstation.dto.request.CreateChallengeRequest; +import com.walkhub.walkhub.domain.challenge.presenstation.dto.request.QueryChallengeProgressRequest; import com.walkhub.walkhub.domain.challenge.presenstation.dto.request.UpdateChallengeRequest; import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeDetailsResponse; import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeListResponse; -import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeParticipantsForTeacherResponse; -import com.walkhub.walkhub.domain.challenge.service.CreateChallengeService; -import com.walkhub.walkhub.domain.challenge.service.ParticipateChallengeService; -import com.walkhub.walkhub.domain.challenge.service.QueryChallengeDetailsService; -import com.walkhub.walkhub.domain.challenge.service.QueryChallengeListService; -import com.walkhub.walkhub.domain.challenge.service.QueryChallengeParticipantsForTeacherService; -import com.walkhub.walkhub.domain.challenge.service.QueryParticipatedChallengeListService; -import com.walkhub.walkhub.domain.challenge.service.RemoveChallengeService; -import com.walkhub.walkhub.domain.challenge.service.UpdateChallengeService; +import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeParticipantsForStudentResponse; +import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeProgressResponse; +import com.walkhub.walkhub.domain.challenge.service.*; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.DeleteMapping; -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.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 org.springframework.web.bind.annotation.*; import javax.validation.Valid; @@ -37,11 +22,13 @@ public class ChallengeController { private final RemoveChallengeService removeChallengeService; private final CreateChallengeService createChallengeService; private final QueryChallengeListService queryChallengeListService; + private final QueryClosesChallengeService queryClosesChallengeService; private final UpdateChallengeService updateChallengeService; private final QueryChallengeDetailsService queryChallengeDetailsService; private final ParticipateChallengeService participateChallengeService; private final QueryParticipatedChallengeListService queryParticipatedChallengeListService; - private final QueryChallengeParticipantsForTeacherService queryChallengeParticipantsForTeacherService; + private final QueryChallengeParticipantsForStudentService queryChallengeParticipantsForStudentService; + private final QueryChallengeProgressService challengeProgressService; @ResponseStatus(HttpStatus.NO_CONTENT) @DeleteMapping("/{challenge-id}") @@ -60,6 +47,11 @@ public QueryChallengeListResponse queryChallengeList() { return queryChallengeListService.execute(); } + @GetMapping("/lists/closed") + public QueryChallengeListResponse queryClosedChallengeList() { + return queryClosesChallengeService.execute(); + } + @ResponseStatus(HttpStatus.NO_CONTENT) @PatchMapping("/{challenge-id}") public void updateChallenge(@PathVariable("challenge-id") Long id, @@ -77,16 +69,20 @@ public QueryChallengeDetailsResponse queryChallengeDetails(@PathVariable("challe public void participateChallenge(@PathVariable("challenge-id") Long challengeId) { participateChallengeService.execute(challengeId); } - + @GetMapping("/participated") public QueryChallengeListResponse queryParticipatedChallengeList() { return queryParticipatedChallengeListService.execute(); } - @GetMapping("/{challenge-id}/participants/teachers") - public QueryChallengeParticipantsForTeacherResponse queryChallengeParticipantsForTeacher(@PathVariable("challenge-id") Long challengeId, - @RequestParam SuccessScope successScope) { - return queryChallengeParticipantsForTeacherService.execute(challengeId, successScope); + @GetMapping("/{challenge-id}/participants/students") + public QueryChallengeParticipantsForStudentResponse queryChallengeParticipantsForStudent(@PathVariable("challenge-id") Long challengeId) { + return queryChallengeParticipantsForStudentService.execute(challengeId); } + @GetMapping("/{challenge-id}/progress") + public QueryChallengeProgressResponse queryChallengeProgress(@PathVariable("challenge-id") Long challengeId, + QueryChallengeProgressRequest request) { + return challengeProgressService.execute(challengeId, request); + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/request/CreateChallengeRequest.java b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/request/CreateChallengeRequest.java index ff23aad90..ec10442c6 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/request/CreateChallengeRequest.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/request/CreateChallengeRequest.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import javax.validation.constraints.FutureOrPresent; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.time.LocalDate; @@ -22,9 +23,11 @@ public class CreateChallengeRequest { private String imageUrl; @NotNull(message = "start_at은 Null일 수 없습니다.") + @FutureOrPresent(message = "start_at은 과거일 수 없습니다.") private LocalDate startAt; @NotNull(message = "end_at은 Null일 수 없습니다.") + @FutureOrPresent(message = "end_at은 과거일 수 없습니다.") private LocalDate endAt; @NotBlank(message = "award는 Null, 공백, 띄어쓰기를 허용하지 않습니다.") diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/request/QueryChallengeProgressRequest.java b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/request/QueryChallengeProgressRequest.java new file mode 100644 index 000000000..cc0230b8a --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/request/QueryChallengeProgressRequest.java @@ -0,0 +1,16 @@ +package com.walkhub.walkhub.domain.challenge.presenstation.dto.request; + +import com.walkhub.walkhub.domain.challenge.domain.type.ChallengeParticipantsOrder; +import com.walkhub.walkhub.domain.challenge.domain.type.ChallengeParticipantsScope; +import com.walkhub.walkhub.domain.challenge.domain.type.SuccessScope; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class QueryChallengeProgressRequest { + private final ChallengeParticipantsScope participantsScope; + private final ChallengeParticipantsOrder participantsOrder; + private final SuccessScope successScope; + private final Long page; +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/request/UpdateChallengeRequest.java b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/request/UpdateChallengeRequest.java index cfed1f92e..b73f6ff19 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/request/UpdateChallengeRequest.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/request/UpdateChallengeRequest.java @@ -5,6 +5,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import javax.validation.constraints.FutureOrPresent; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.time.LocalDate; @@ -22,9 +23,11 @@ public class UpdateChallengeRequest { private String imageUrl; @NotNull(message = "start_at은 Null일 수 없습니다.") + @FutureOrPresent(message = "start_at은 과거일 수 없습니다.") private LocalDate startAt; @NotNull(message = "end_at은 Null일 수 없습니다.") + @FutureOrPresent(message = "end_at은 과거일 수 없습니다.") private LocalDate endAt; @NotBlank(message = "award는 Null, 공백, 띄어쓰기를 허용하지 않습니다.") diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeDetailsResponse.java b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeDetailsResponse.java index 1f1f5766d..af4285a89 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeDetailsResponse.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeDetailsResponse.java @@ -2,6 +2,7 @@ import com.walkhub.walkhub.domain.challenge.domain.type.GoalScope; import com.walkhub.walkhub.domain.exercise.domain.type.GoalType; +import com.walkhub.walkhub.global.enums.Authority; import com.walkhub.walkhub.global.enums.UserScope; import lombok.Builder; import lombok.Getter; @@ -25,6 +26,7 @@ public class QueryChallengeDetailsResponse { private final Long participantCount; private final Boolean isMine; private final Boolean isParticipated; + private final Integer successStandard; private final Writer writer; @Getter @@ -33,5 +35,9 @@ public static class Writer{ private final Long userId; private final String name; private final String profileImageUrl; + private final Authority authority; + private final String schoolName; + private final Integer grade; + private final Integer classNum; } } diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeListResponse.java b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeListResponse.java index 40fae2873..45f2dc500 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeListResponse.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeListResponse.java @@ -19,7 +19,7 @@ public class QueryChallengeListResponse { @Getter @Builder public static class ChallengeResponse { - private final Long id; + private final Long challengeId; private final String name; private final LocalDate startAt; private final LocalDate endAt; diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeParticipantsForStudentResponse.java b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeParticipantsForStudentResponse.java new file mode 100644 index 000000000..658e8afc5 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeParticipantsForStudentResponse.java @@ -0,0 +1,21 @@ +package com.walkhub.walkhub.domain.challenge.presenstation.dto.response; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class QueryChallengeParticipantsForStudentResponse { + private final Integer participantCount; + private final List relatedChallengeParticipantList; + + @Getter + @Builder + public static class RelatedChallengeParticipants { + private final Long userId; + private final String name; + private final String profileImageUrl; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeParticipantsForTeacherResponse.java b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeParticipantsForTeacherResponse.java deleted file mode 100644 index 60d0576df..000000000 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeParticipantsForTeacherResponse.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.walkhub.walkhub.domain.challenge.presenstation.dto.response; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; - -import java.time.LocalDate; -import java.util.List; - -@Getter -@AllArgsConstructor -public class QueryChallengeParticipantsForTeacherResponse { - - private final List challengeParticipantsList; - - @Getter - @Builder - public static class ChallengeParticipants { - private final Long userId; - private final Integer grade; - private final Integer classNum; - private final Integer number; - private final String name; - private final String profileImageUrl; - private final String schoolName; - private final List successDate; - private final Boolean isSuccess; - } - -} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeProgressResponse.java b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeProgressResponse.java new file mode 100644 index 000000000..b92cf66d5 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/presenstation/dto/response/QueryChallengeProgressResponse.java @@ -0,0 +1,51 @@ +package com.walkhub.walkhub.domain.challenge.presenstation.dto.response; + +import com.walkhub.walkhub.domain.challenge.domain.type.GoalScope; +import com.walkhub.walkhub.domain.exercise.domain.type.GoalType; +import com.walkhub.walkhub.global.enums.UserScope; +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDate; +import java.util.List; + +@Getter +@Builder +public class QueryChallengeProgressResponse { + + private final String name; + private final Long userId; + private final String content; + private final String imageUrl; + private final String writerName; + private final String writerProfileImageUrl; + private final String award; + private final LocalDate startAt; + private final LocalDate endAt; + private final Integer goal; + private final GoalScope goalScope; + private final UserScope userScope; + private final GoalType goalType; + private final Integer classNum; + private final Integer grade; + private final Integer successStandard; + private final Long count; + private final List userResponse; + + @Getter + @Builder + public static class UserChallengeProgressResponse { + private final Long userId; + private final String userName; + private final Integer grade; + private final Integer classNum; + private final Integer number; + private final String schoolName; + private final String profileImageUrl; + private final Integer totalWalkCount; + private final Long progress; + private final Boolean isSuccess; + private final LocalDate successDate; + } + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/service/CreateChallengeService.java b/src/main/java/com/walkhub/walkhub/domain/challenge/service/CreateChallengeService.java index 98216cb79..48f7ef5d6 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/service/CreateChallengeService.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/service/CreateChallengeService.java @@ -24,6 +24,7 @@ public void execute(CreateChallengeRequest request) { UserScope userScope = user.getAuthority() == Authority.SU ? UserScope.ALL : request.getUserScope(); challengeRepository.save(Challenge.builder() + .user(user) .name(request.getName()) .content(request.getContent()) .imageUrl(request.getImageUrl()) diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/service/ParticipateChallengeService.java b/src/main/java/com/walkhub/walkhub/domain/challenge/service/ParticipateChallengeService.java index 779d56aa6..b184178b0 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/service/ParticipateChallengeService.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/service/ParticipateChallengeService.java @@ -36,7 +36,6 @@ public void execute(Long challengeId) { ChallengeStatus challengeStatus = ChallengeStatus.builder() .challenge(challenge) .user(user) - .successCount(0L) .build(); challengeStatusRepository.save(challengeStatus); diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeDetailsService.java b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeDetailsService.java index b839149db..a4cf1bcda 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeDetailsService.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeDetailsService.java @@ -4,6 +4,8 @@ import com.walkhub.walkhub.domain.challenge.facade.ChallengeFacade; import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeDetailsResponse; import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeDetailsResponse.Writer; +import com.walkhub.walkhub.domain.school.domain.School; +import com.walkhub.walkhub.domain.user.domain.Section; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.facade.UserFacade; import com.walkhub.walkhub.global.enums.UserScope; @@ -30,6 +32,9 @@ public QueryChallengeDetailsResponse execute(Long challengeId) { throw InvalidRoleException.EXCEPTION; } + School writerSchool = writer.hasSchool() ? writer.getSchool() : School.builder().build(); + Section wrtierSection = writer.hasSection() ? writer.getSection() : Section.builder().build(); + Boolean isMine = challenge.getChallengeStatuses() .stream() .anyMatch(challengeStatus -> challengeStatus.getUser().equals(user)); @@ -48,10 +53,15 @@ public QueryChallengeDetailsResponse execute(Long challengeId) { .participantCount((long) challenge.getChallengeStatuses().size()) .isMine(isMine) .isParticipated(user.equals(writer)) + .successStandard(challenge.getSuccessStandard()) .writer(Writer.builder() .userId(writer.getId()) .name(writer.getName()) .profileImageUrl(writer.getProfileImageUrl()) + .authority(writer.getAuthority()) + .schoolName(writerSchool.getName()) + .classNum(wrtierSection.getClassNum()) + .grade(wrtierSection.getGrade()) .build()) .build(); } diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeListService.java b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeListService.java index 55f7d4496..388807fa9 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeListService.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeListService.java @@ -4,29 +4,33 @@ import com.walkhub.walkhub.domain.challenge.facade.ChallengeFacade; import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeListResponse; import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeListResponse.ChallengeResponse; +import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.facade.UserFacade; -import java.util.List; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.stream.Collectors; + @RequiredArgsConstructor @Service public class QueryChallengeListService { - private final ChallengeRepository challengeRepository; - private final UserFacade userFacade; - private final ChallengeFacade challengeFacade; + private final UserFacade userFacade; + private final ChallengeRepository challengeRepository; + private final ChallengeFacade challengeFacade; + + @Transactional(readOnly = true) + public QueryChallengeListResponse execute() { + User user = userFacade.getCurrentUser(); - @Transactional(readOnly = true) - public QueryChallengeListResponse execute() { + List challengeResponseList = challengeRepository.queryChallenge(user) + .stream() + .map(challengeFacade::challengeResponseBuilder) + .collect(Collectors.toList()); - List challengeResponseList = challengeRepository.findAllBySchool(userFacade.getCurrentUser().getSchool()) - .stream() - .map(challengeFacade::challengeResponseBuilder) - .collect(Collectors.toList()); + return new QueryChallengeListResponse(challengeResponseList); + } - return new QueryChallengeListResponse(challengeResponseList); - } } diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeParticipantsForStudentService.java b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeParticipantsForStudentService.java new file mode 100644 index 000000000..d31862d3f --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeParticipantsForStudentService.java @@ -0,0 +1,36 @@ +package com.walkhub.walkhub.domain.challenge.service; + +import com.walkhub.walkhub.domain.challenge.domain.repository.ChallengeStatusRepository; +import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeParticipantsForStudentResponse; +import com.walkhub.walkhub.domain.school.domain.School; +import com.walkhub.walkhub.domain.user.domain.Section; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class QueryChallengeParticipantsForStudentService { + private final ChallengeStatusRepository challengeStatusRepository; + private final UserFacade userFacade; + + public QueryChallengeParticipantsForStudentResponse execute(Long challengeId) { + User user = userFacade.getCurrentUser(); + School school = user.getSchool(); + Section section = user.getSection(); + + return QueryChallengeParticipantsForStudentResponse.builder() + .participantCount(challengeStatusRepository.getParticipantsCountByChallengeId(challengeId)) + .relatedChallengeParticipantList(challengeStatusRepository.getRelatedChallengeParticipantsList(challengeId, school, section.getGrade(), section.getClassNum()) + .stream().map(participants -> QueryChallengeParticipantsForStudentResponse.RelatedChallengeParticipants.builder() + .userId(participants.getUserId()) + .name(participants.getName()) + .profileImageUrl(participants.getProfileImageUrl()) + .build() + ).collect(Collectors.toList()) + ).build(); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeParticipantsForTeacherService.java b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeParticipantsForTeacherService.java deleted file mode 100644 index 7470e56a0..000000000 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeParticipantsForTeacherService.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.walkhub.walkhub.domain.challenge.service; - -import com.walkhub.walkhub.domain.challenge.domain.Challenge; -import com.walkhub.walkhub.domain.challenge.domain.repository.ChallengeStatusRepository; -import com.walkhub.walkhub.domain.challenge.domain.type.SuccessScope; -import com.walkhub.walkhub.domain.challenge.facade.ChallengeFacade; -import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeParticipantsForTeacherResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.stream.Collectors; - -@RequiredArgsConstructor -@Service -public class QueryChallengeParticipantsForTeacherService { - private final ChallengeFacade challengeFacade; - private final ChallengeStatusRepository challengeStatusRepository; - - @Transactional(readOnly = true) - public QueryChallengeParticipantsForTeacherResponse execute(Long challengeId, SuccessScope successScope) { - Challenge challenge = challengeFacade.getChallengeById(challengeId); - - return new QueryChallengeParticipantsForTeacherResponse( - queryChallengeParticipantsForTeacherResponseBuilder(challenge, successScope) - ); - } - - private List queryChallengeParticipantsForTeacherResponseBuilder( - Challenge challenge, SuccessScope successScope - ) { - return challengeStatusRepository.queryChallengeParticipantsList(challenge, successScope) - .stream() - .map(vo -> QueryChallengeParticipantsForTeacherResponse.ChallengeParticipants.builder() - .userId(vo.getUserId()) - .grade(vo.getGrade()) - .classNum(vo.getClassNum()) - .number(vo.getNumber()) - .name(vo.getName()) - .profileImageUrl(vo.getProfileImageUrl()) - .schoolName(vo.getSchoolName()) - .successDate(vo.getExerciseAnalysesDates()) - .isSuccess(vo.getIsSuccess()) - .build()) - .collect(Collectors.toList()); - } - -} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeProgressService.java b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeProgressService.java new file mode 100644 index 000000000..1d7b04473 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryChallengeProgressService.java @@ -0,0 +1,81 @@ +package com.walkhub.walkhub.domain.challenge.service; + +import com.walkhub.walkhub.domain.challenge.domain.Challenge; +import com.walkhub.walkhub.domain.challenge.domain.repository.ChallengeStatusRepository; +import com.walkhub.walkhub.domain.challenge.domain.repository.vo.ChallengeProgressVO; +import com.walkhub.walkhub.domain.challenge.facade.ChallengeFacade; +import com.walkhub.walkhub.domain.challenge.presenstation.dto.request.QueryChallengeProgressRequest; +import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeProgressResponse; +import com.walkhub.walkhub.domain.user.domain.User; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class QueryChallengeProgressService { + + private final ChallengeStatusRepository challengeStatusRepository; + private final ChallengeFacade challengeFacade; + + public QueryChallengeProgressResponse execute(Long challengeId, QueryChallengeProgressRequest request) { + Challenge challenge = challengeFacade.getChallengeById(challengeId); + + List challengeProgressVOS = challengeStatusRepository.queryChallengeProgress( + challenge, + request.getParticipantsScope(), + request.getParticipantsOrder(), + request.getSuccessScope(), + request.getPage() + ); + + List challengeProgressResponses = challengeProgressVOS.stream() + .map(this::buildUserChallengeProgressResponse) + .collect(Collectors.toList()); + + User challengeCreator = challenge.getUser(); + + return QueryChallengeProgressResponse.builder() + .userResponse(challengeProgressResponses) + .award(challenge.getAward()) + .classNum(challengeCreator.hasSection() ? challengeCreator.getSection().getClassNum() : null) + .content(challenge.getContent()) + .count((long) challenge.getChallengeStatuses().size()) + .endAt(challenge.getEndAt()) + .goal(challenge.getGoal()) + .goalScope(challenge.getGoalScope()) + .goalType(challenge.getGoalType()) + .grade(challengeCreator.hasSection() ? challengeCreator.getSection().getGrade() : null) + .imageUrl(challenge.getImageUrl()) + .name(challenge.getName()) + .startAt(challenge.getStartAt()) + .successStandard(challenge.getSuccessStandard()) + .userId(challengeCreator.getId()) + .userScope(challenge.getUserScope()) + .writerName(challengeCreator.getName()) + .writerProfileImageUrl(challengeCreator.getProfileImageUrl()) + .name(challenge.getName()) + .build(); + } + + private QueryChallengeProgressResponse.UserChallengeProgressResponse buildUserChallengeProgressResponse(ChallengeProgressVO vo) { + if (vo.getUserId() == null) { + return null; + } + return QueryChallengeProgressResponse.UserChallengeProgressResponse.builder() + .classNum(vo.getClassNum()) + .progress(vo.getProgress() == null ? 0 : vo.getProgress()) + .grade(vo.getGrade()) + .isSuccess(vo.getIsSuccess()) + .number(vo.getNumber()) + .profileImageUrl(vo.getProfileImageUrl()) + .schoolName(vo.getSchoolName()) + .successDate(vo.getSuccessDate()) + .totalWalkCount(vo.getTotalWalkCount()) + .userId(vo.getUserId()) + .userName(vo.getUserName()) + .build(); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryClosesChallengeService.java b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryClosesChallengeService.java new file mode 100644 index 000000000..5648dda25 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryClosesChallengeService.java @@ -0,0 +1,32 @@ +package com.walkhub.walkhub.domain.challenge.service; + +import com.walkhub.walkhub.domain.challenge.domain.repository.ChallengeStatusRepository; +import com.walkhub.walkhub.domain.challenge.facade.ChallengeFacade; +import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeListResponse; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class QueryClosesChallengeService { + + private final ChallengeStatusRepository challengeStatusRepository; + private final UserFacade userFacade; + private final ChallengeFacade challengeFacade; + + @Transactional(readOnly = true) + public QueryChallengeListResponse execute() { + + List challengeResponseList = challengeStatusRepository.getAllChallengesByUser(userFacade.getCurrentUser()) + .stream() + .map(challengeFacade::challengeResponseBuilder) + .collect(Collectors.toList()); + + return new QueryChallengeListResponse(challengeResponseList); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryParticipatedChallengeListService.java b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryParticipatedChallengeListService.java index 14ee51670..8ae6812df 100644 --- a/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryParticipatedChallengeListService.java +++ b/src/main/java/com/walkhub/walkhub/domain/challenge/service/QueryParticipatedChallengeListService.java @@ -1,34 +1,34 @@ package com.walkhub.walkhub.domain.challenge.service; -import com.walkhub.walkhub.domain.challenge.domain.ChallengeStatus; import com.walkhub.walkhub.domain.challenge.domain.repository.ChallengeStatusRepository; import com.walkhub.walkhub.domain.challenge.facade.ChallengeFacade; import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeListResponse; import com.walkhub.walkhub.domain.challenge.presenstation.dto.response.QueryChallengeListResponse.ChallengeResponse; import com.walkhub.walkhub.domain.user.facade.UserFacade; -import java.util.List; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.stream.Collectors; + @RequiredArgsConstructor @Service public class QueryParticipatedChallengeListService { - private final ChallengeStatusRepository challengeStatusRepository; - private final UserFacade userFacade; - private final ChallengeFacade challengeFacade; + private final ChallengeStatusRepository challengeStatusRepository; + private final UserFacade userFacade; + private final ChallengeFacade challengeFacade; + + @Transactional(readOnly = true) + public QueryChallengeListResponse execute() { - @Transactional(readOnly = true) - public QueryChallengeListResponse execute() { + List challengeResponseList = challengeStatusRepository.getAllChallengesByUser(userFacade.getCurrentUser()) + .stream() + .map(challengeFacade::challengeResponseBuilder) + .collect(Collectors.toList()); - List challengeResponseList = challengeStatusRepository.findAllByUser(userFacade.getCurrentUser()) - .stream() - .map(ChallengeStatus::getChallenge) - .map(challengeFacade::challengeResponseBuilder) - .collect(Collectors.toList()); - return new QueryChallengeListResponse(challengeResponseList); - } + return new QueryChallengeListResponse(challengeResponseList); + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/excel/domain/type/UserType.java b/src/main/java/com/walkhub/walkhub/domain/excel/domain/type/UserType.java new file mode 100644 index 000000000..d1b9e4f90 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/excel/domain/type/UserType.java @@ -0,0 +1,7 @@ +package com.walkhub.walkhub.domain.excel.domain.type; + +public enum UserType { + ALL, + STUDENT, + TEACHER +} diff --git a/src/main/java/com/walkhub/walkhub/domain/excel/presentation/ExcelController.java b/src/main/java/com/walkhub/walkhub/domain/excel/presentation/ExcelController.java new file mode 100644 index 000000000..5edb104e6 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/excel/presentation/ExcelController.java @@ -0,0 +1,22 @@ +package com.walkhub.walkhub.domain.excel.presentation; + +import com.walkhub.walkhub.domain.excel.presentation.dto.request.PrintExcelRequest; +import com.walkhub.walkhub.domain.excel.presentation.dto.response.PrintExcelResponse; +import com.walkhub.walkhub.domain.excel.service.PrintExcelService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@RequestMapping("/excel") +@RestController +public class ExcelController { + + private final PrintExcelService printExcelService; + + @GetMapping + public PrintExcelResponse getExcel(PrintExcelRequest printExcelRequest) { + return printExcelService.execute(printExcelRequest); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/excel/presentation/dto/request/PrintExcelRequest.java b/src/main/java/com/walkhub/walkhub/domain/excel/presentation/dto/request/PrintExcelRequest.java new file mode 100644 index 000000000..0b94f3541 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/excel/presentation/dto/request/PrintExcelRequest.java @@ -0,0 +1,19 @@ +package com.walkhub.walkhub.domain.excel.presentation.dto.request; + +import com.walkhub.walkhub.domain.excel.domain.type.UserType; +import java.time.LocalDate; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class PrintExcelRequest { + + private LocalDate startAt; + private LocalDate endAt; + private UserType userType; + private Integer grade; + private Integer classNum; +} diff --git a/src/main/java/com/walkhub/walkhub/domain/excel/presentation/dto/response/PrintExcelResponse.java b/src/main/java/com/walkhub/walkhub/domain/excel/presentation/dto/response/PrintExcelResponse.java new file mode 100644 index 000000000..19038a721 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/excel/presentation/dto/response/PrintExcelResponse.java @@ -0,0 +1,44 @@ +package com.walkhub.walkhub.domain.excel.presentation.dto.response; + +import com.querydsl.core.annotations.QueryProjection; +import com.walkhub.walkhub.global.enums.Authority; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class PrintExcelResponse { + + private final List userList; + + @Getter + public static class PrintExcelVo { + + private final String name; + private final Integer grade; + private final Integer classNum; + private final Integer number; + private final Integer allWalkCount; + private final Integer averageWalkCount; + private final Integer allDistance; + private final Integer averageDistance; + private final Authority authority; + private final String schoolName; + + @QueryProjection + public PrintExcelVo(String name, Integer grade, Integer classNum, Integer number, + Integer allWalkCount, Integer averageWalkCount, Integer allDistance, Integer averageDistance, Authority authority, String schoolName) { + this.name = name; + this.grade = grade; + this.classNum = classNum; + this.number = number; + this.allWalkCount = allWalkCount; + this.averageWalkCount = averageWalkCount; + this.allDistance = allDistance; + this.averageDistance = averageDistance; + this.authority = authority; + this.schoolName = schoolName; + } + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/excel/service/PrintExcelService.java b/src/main/java/com/walkhub/walkhub/domain/excel/service/PrintExcelService.java new file mode 100644 index 000000000..d24768ab5 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/excel/service/PrintExcelService.java @@ -0,0 +1,27 @@ +package com.walkhub.walkhub.domain.excel.service; + +import com.walkhub.walkhub.domain.excel.presentation.dto.request.PrintExcelRequest; +import com.walkhub.walkhub.domain.excel.presentation.dto.response.PrintExcelResponse; +import com.walkhub.walkhub.domain.excel.presentation.dto.response.PrintExcelResponse.PrintExcelVo; +import com.walkhub.walkhub.domain.exercise.domain.repository.ExerciseAnalysisRepository; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Service +public class PrintExcelService { + + private final ExerciseAnalysisRepository exerciseAnalysisRepository; + private final UserFacade userFacade; + + @Transactional(readOnly = true) + public PrintExcelResponse execute(PrintExcelRequest printExcelRequest) { + Long schoolId = userFacade.getCurrentUser().getSchool().getId(); + List printExcelVoList = exerciseAnalysisRepository.getPrintExcelVoList(printExcelRequest, schoolId); + + return new PrintExcelResponse(printExcelVoList); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/cache/ExerciseAnalysisCacheRepository.java b/src/main/java/com/walkhub/walkhub/domain/exercise/cache/ExerciseAnalysisCacheRepository.java index 99ee4ac78..d6269dc64 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/cache/ExerciseAnalysisCacheRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/cache/ExerciseAnalysisCacheRepository.java @@ -3,9 +3,9 @@ import java.util.List; public interface ExerciseAnalysisCacheRepository { - void saveExerciseCache(Long userId, Double walkCount); + void saveExerciseCache(Long schoolId, Long userId, Double walkCount); - ExerciseAnalysisDto getUserTodayRank(Long userId); + ExerciseAnalysisDto getUserTodayRank(Long schoolId, Long userId); - List getUserIdsByRankTop100(); + List getUserIdsByRankTop100(Long schoolId); } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/cache/ExerciseAnalysisCacheRepositoryImpl.java b/src/main/java/com/walkhub/walkhub/domain/exercise/cache/ExerciseAnalysisCacheRepositoryImpl.java index 79e009460..e6092972a 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/cache/ExerciseAnalysisCacheRepositoryImpl.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/cache/ExerciseAnalysisCacheRepositoryImpl.java @@ -5,32 +5,33 @@ import org.springframework.data.redis.core.ZSetOperations; import org.springframework.stereotype.Repository; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; +import java.time.LocalDate; +import java.util.*; @RequiredArgsConstructor @Repository public class ExerciseAnalysisCacheRepositoryImpl implements ExerciseAnalysisCacheRepository { - public static final String EXERCISE_ANALYSIS_KEY = "exercise_analysis"; + public static final String EXERCISE_ANALYSIS_KEY = "exercise_analysis_"; private final ZSetOperations zSetOperations; @Override - public void saveExerciseCache(Long userId, Double walkCount) { - zSetOperations.add(EXERCISE_ANALYSIS_KEY, userId, walkCount); + public void saveExerciseCache(Long schoolId, Long userId, Double walkCount) { + zSetOperations.add(getExerciseAnalysisKey(schoolId), userId, walkCount); + Date today = java.sql.Date.valueOf(LocalDate.now()); + zSetOperations.getOperations().expireAt(getExerciseAnalysisKey(schoolId), today); } @Override - public ExerciseAnalysisDto getUserTodayRank(Long userId) { - Double doubleWalkCount = Optional.ofNullable(zSetOperations.score(EXERCISE_ANALYSIS_KEY, userId)) - .orElseThrow(() -> RedisTransactionException.EXCEPTION); + public ExerciseAnalysisDto getUserTodayRank(Long schoolId, Long userId) { + Double doubleWalkCount = zSetOperations.score(getExerciseAnalysisKey(schoolId), userId); + if (doubleWalkCount == null) { + return null; + } Integer walkCount = doubleWalkCount.intValue(); - Long ranking = zSetOperations.rank(EXERCISE_ANALYSIS_KEY, userId); + Long ranking = zSetOperations.rank(getExerciseAnalysisKey(schoolId), userId); try { return ExerciseAnalysisDto.builder() @@ -44,11 +45,12 @@ public ExerciseAnalysisDto getUserTodayRank(Long userId) { } @Override - public List getUserIdsByRankTop100() { - Set> rankUserIds = Optional.ofNullable( - zSetOperations.reverseRangeWithScores(EXERCISE_ANALYSIS_KEY, 0, 99)) - .orElseThrow(() -> RedisTransactionException.EXCEPTION); + public List getUserIdsByRankTop100(Long schoolId) { + Set> rankUserIds = zSetOperations.reverseRangeWithScores(getExerciseAnalysisKey(schoolId), 0, 99); int rank = 1; + if (rankUserIds == null) { + return Collections.emptyList(); + } List exerciseAnalysisDtos = new ArrayList<>(rankUserIds.size()); @@ -68,4 +70,8 @@ public List getUserIdsByRankTop100() { return exerciseAnalysisDtos; } + + private String getExerciseAnalysisKey(Long schoolId) { + return EXERCISE_ANALYSIS_KEY + schoolId; + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/Exercise.java b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/Exercise.java index e73843806..ba96d0d99 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/Exercise.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/Exercise.java @@ -4,12 +4,11 @@ import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.global.entity.BaseTimeEntity; import com.walkhub.walkhub.infrastructure.image.DefaultImage; -import javax.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.DynamicInsert; import javax.persistence.Column; import javax.persistence.Entity; @@ -21,11 +20,12 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; -import java.time.LocalDateTime; -import org.hibernate.validator.constraints.Length; +import javax.validation.constraints.NotNull; +import java.time.ZonedDateTime; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@DynamicInsert @Entity public class Exercise extends BaseTimeEntity { @@ -33,57 +33,56 @@ public class Exercise extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ColumnDefault("0") - @Column(nullable = false) + @Column(nullable = false, columnDefinition = "integer default 0") private Integer walkCount; - private LocalDateTime endAt; + private ZonedDateTime endAt; - @ColumnDefault("0") - @Column(nullable = false) + @Column(nullable = false, columnDefinition = "integer default 0") private Integer distance; - @ColumnDefault("0") - @Column(nullable = false) - private Integer calorie; + @Column(nullable = false, columnDefinition = "integer default 0") + private Double calorie; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private User user; - @ColumnDefault("6000") - @Column(nullable = false) + @Column(nullable = false, columnDefinition = "integer default 6000") private Integer goal; @NotNull - @Length(max = 8) @Enumerated(EnumType.STRING) private GoalType goalType; - @ColumnDefault("0") - @Column(nullable = false) + @Column(nullable = false, columnDefinition = "boolean default false") private Boolean isExercising; - @ColumnDefault("0") - @Column(nullable = false) + @Column(nullable = false, columnDefinition = "integer default 0") private Long cheeringCount; - @NotNull - @ColumnDefault(DefaultImage.EXERCISE_IMAGE) + @Column(nullable = false, columnDefinition = "varchar(255) default " + DefaultImage.EXERCISE_IMAGE) private String imageUrl; + @Column(nullable = false, columnDefinition = "integer default 0") + private Integer pausedTime; + @Builder public Exercise(User user, Integer goal, GoalType goalType) { this.user = user; this.goalType = goalType; - this.goal = goal; + if(goal != null) this.goal = goal; + this.isExercising = true; } - public void closeExercise(Integer walkCount, Integer distance, Integer calorie) { + public void closeExercise(Integer walkCount, Integer distance, Double calorie, String imageUrl, Integer pausedTime) { this.walkCount = walkCount; this.distance = distance; this.calorie = calorie; - this.endAt = LocalDateTime.now(); + this.endAt = ZonedDateTime.now(); + this.imageUrl = imageUrl; + this.isExercising = false; + this.pausedTime = pausedTime; } public void addCheeringCount() { diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/ExerciseAnalysis.java b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/ExerciseAnalysis.java index 1e7483270..2486517f1 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/ExerciseAnalysis.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/ExerciseAnalysis.java @@ -7,7 +7,15 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.validation.constraints.NotNull; import java.time.LocalDate; @Getter @@ -19,29 +27,34 @@ public class ExerciseAnalysis { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(nullable = false) - private Integer calorie; + @NotNull + private Double calorie; - @Column(nullable = false) + @NotNull private Integer walkCount; - @Column(nullable = false) + @NotNull private Integer distance; - @Column(nullable = false) + @NotNull private LocalDate date; + @Column(nullable = false) + private Double exerciseTime; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private User user; + @Builder - public ExerciseAnalysis(Integer calorie, Integer walkCount, + public ExerciseAnalysis(Double calorie, Integer walkCount, Double exerciseTime, Integer distance, LocalDate date, User user) { this.calorie = calorie; this.walkCount = walkCount; this.distance = distance; this.date = date; + this.exerciseTime = exerciseTime; this.user = user; } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/LocationId.java b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/LocationId.java index 06b711e43..f4ae791d4 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/LocationId.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/LocationId.java @@ -5,10 +5,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.Column; -import javax.persistence.FetchType; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; import java.io.Serializable; @Getter @@ -16,11 +12,8 @@ @EqualsAndHashCode public class LocationId implements Serializable { - @Column(nullable = false) private Integer sequence; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "exercise_id", nullable = false) - private Exercise exercise; + private Long exercise; } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseAnalysisRepository.java b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseAnalysisRepository.java index b657ba8c4..28f26aa97 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseAnalysisRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseAnalysisRepository.java @@ -2,13 +2,13 @@ import com.walkhub.walkhub.domain.exercise.domain.ExerciseAnalysis; import com.walkhub.walkhub.domain.user.domain.User; -import java.util.List; import org.springframework.data.repository.CrudRepository; import java.time.LocalDate; +import java.util.List; import java.util.Optional; -public interface ExerciseAnalysisRepository extends CrudRepository { +public interface ExerciseAnalysisRepository extends CrudRepository, ExerciseAnalysisRepositoryCustom { Optional findByUserAndDate(User user, LocalDate date); - List findAllByDateBetweenAndUser(LocalDate startAt, LocalDate endAt, User user); + List findAllByUserAndDateBetween(User user, LocalDate startAt, LocalDate endAt); } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseAnalysisRepositoryCustom.java b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseAnalysisRepositoryCustom.java new file mode 100644 index 000000000..523b6d6ea --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseAnalysisRepositoryCustom.java @@ -0,0 +1,10 @@ +package com.walkhub.walkhub.domain.exercise.domain.repository; + +import com.walkhub.walkhub.domain.excel.presentation.dto.request.PrintExcelRequest; +import com.walkhub.walkhub.domain.excel.presentation.dto.response.PrintExcelResponse.PrintExcelVo; +import java.util.List; + + +public interface ExerciseAnalysisRepositoryCustom { + List getPrintExcelVoList(PrintExcelRequest printExcelRequest, Long schoolId); +} diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseAnalysisRepositoryCustomImpl.java b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseAnalysisRepositoryCustomImpl.java new file mode 100644 index 000000000..e58467367 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseAnalysisRepositoryCustomImpl.java @@ -0,0 +1,95 @@ +package com.walkhub.walkhub.domain.exercise.domain.repository; + +import static com.walkhub.walkhub.domain.exercise.domain.QExerciseAnalysis.exerciseAnalysis; +import static com.walkhub.walkhub.domain.user.domain.QUser.user; +import static com.walkhub.walkhub.domain.school.domain.QSchool.school; +import static com.walkhub.walkhub.domain.user.domain.QSection.section; + +import com.querydsl.core.BooleanBuilder; +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.walkhub.walkhub.domain.excel.domain.type.UserType; +import com.walkhub.walkhub.domain.excel.presentation.dto.request.PrintExcelRequest; +import com.walkhub.walkhub.domain.excel.presentation.dto.response.PrintExcelResponse.PrintExcelVo; +import com.walkhub.walkhub.domain.excel.presentation.dto.response.QPrintExcelResponse_PrintExcelVo; +import com.walkhub.walkhub.global.enums.Authority; +import java.time.LocalDate; +import java.util.List; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class ExerciseAnalysisRepositoryCustomImpl implements ExerciseAnalysisRepositoryCustom { + + private final JPAQueryFactory queryFactory; + + @Override + public List getPrintExcelVoList(PrintExcelRequest excelRequest, Long schoolId) { + + LocalDate startAt = excelRequest.getStartAt(); + LocalDate endAt = excelRequest.getEndAt(); + UserType userType = excelRequest.getUserType(); + Integer grade = excelRequest.getGrade(); + Integer classNum = excelRequest.getClassNum(); + + return queryFactory + .select(new QPrintExcelResponse_PrintExcelVo( + user.name, + section.grade, + section.classNum, + user.number, + exerciseAnalysis.walkCount.sum(), + exerciseAnalysis.walkCount.avg().intValue(), + exerciseAnalysis.distance.sum(), + exerciseAnalysis.distance.avg().intValue(), + user.authority, + school.name + )) + .from(exerciseAnalysis) + .join(exerciseAnalysis.user, user) + .join(user.school, school) + .leftJoin(user.section, section) + .where( + school.id.eq(schoolId), + exerciseAnalysis.date.between(startAt, endAt), + userTypeFilter(userType, grade, classNum) + ) + .groupBy(user) + .fetch(); + } + + private BooleanBuilder userTypeFilter(UserType userType, Integer grade, Integer classNum) { + BooleanBuilder builder = new BooleanBuilder(); + + switch (userType) { + case STUDENT: { + builder.and(user.authority.eq(Authority.USER)); + builder.and(nullFilter(grade, classNum)); + break; + } + case TEACHER: { + builder.and(user.authority.eq(Authority.TEACHER)); + break; + } + case ALL: { + builder.and(user.authority.eq(Authority.USER).or( + user.authority.eq(Authority.TEACHER) + )); + } + } + + return builder; + } + + private BooleanBuilder nullFilter(Integer grade, Integer classNum) { + BooleanBuilder builder = new BooleanBuilder(); + + if (grade != null) { + builder.and(section.grade.eq(grade)); + + if (classNum != null) { + builder.and(section.classNum.eq(classNum)); + } + } + + return builder; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseRepository.java b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseRepository.java index dc2463285..93ea46336 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/ExerciseRepository.java @@ -2,12 +2,13 @@ import com.walkhub.walkhub.domain.exercise.domain.Exercise; import com.walkhub.walkhub.domain.user.domain.User; -import java.util.List; import org.springframework.data.repository.CrudRepository; +import java.util.List; import java.util.Optional; public interface ExerciseRepository extends CrudRepository { Optional findByIsExercisingTrueAndUser(User user); + List findAllByUser(User user); } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/LocationRepository.java b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/LocationRepository.java index 7dd886238..f745ba1fa 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/LocationRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/domain/repository/LocationRepository.java @@ -5,6 +5,9 @@ import com.walkhub.walkhub.domain.exercise.domain.LocationId; import org.springframework.data.repository.CrudRepository; +import java.util.List; + public interface LocationRepository extends CrudRepository { Location findTop1ByExerciseOrderBySequenceDesc(Exercise exercise); + List findAllByExercise(Exercise exercise); } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/exception/AlreadyExercisingException.java b/src/main/java/com/walkhub/walkhub/domain/exercise/exception/AlreadyExercisingException.java new file mode 100644 index 000000000..c8e0276aa --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/exception/AlreadyExercisingException.java @@ -0,0 +1,15 @@ +package com.walkhub.walkhub.domain.exercise.exception; + +import com.walkhub.walkhub.global.error.exception.ErrorCode; +import com.walkhub.walkhub.global.error.exception.WalkhubException; + +public class AlreadyExercisingException extends WalkhubException { + + public static final WalkhubException EXCEPTION = + new AlreadyExercisingException(); + + private AlreadyExercisingException() { + super(ErrorCode.ALREADY_EXERCISING); + } + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/ExerciseController.java b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/ExerciseController.java index b9671b68d..5df4242c5 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/ExerciseController.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/ExerciseController.java @@ -18,41 +18,58 @@ @RestController public class ExerciseController { - private final CreateExerciseService createExerciseService; - private final FinishExerciseService finishExerciseService; - private final SaveLocationService saveLocationService; - private final QueryExerciseListService queryExerciseListService; - private final SaveOrUpdateExerciseAnalysisService saveOrUpdateExerciseAnalysisService; - - @ResponseStatus(HttpStatus.CREATED) - @PostMapping - public CreateExerciseResponse createExercise(@RequestBody CreateExerciseRequest request) { - return createExerciseService.execute(request); - } - - @ResponseStatus(HttpStatus.NO_CONTENT) - @PatchMapping("/{exercise-id}") - public void finishExercise(@PathVariable(name = "exercise-id") Long exerciseId, - @RequestBody @Valid FinishExerciseRequest request) { - finishExerciseService.execute(exerciseId, request); - } - - @ResponseStatus(HttpStatus.CREATED) - @PostMapping("/locations/{exercise-id}") - public void saveLocation(@PathVariable(name = "exercise-id") Long exerciseId, - @RequestBody @Valid SaveLocationRequest request) { - saveLocationService.execute(exerciseId, request); - } - - @ResponseStatus(HttpStatus.NO_CONTENT) - @PutMapping - public void saveOrUpdateTodayExercise(@RequestBody @Valid SaveExerciseAnalysisRequest request) { - saveOrUpdateExerciseAnalysisService.execute(request); - } - - @GetMapping("/lists") - public ExerciseListResponse queryExerciseList() { - return queryExerciseListService.execute(); - } + private final CreateExerciseService createExerciseService; + private final FinishExerciseService finishExerciseService; + private final SaveLocationService saveLocationService; + private final QueryExerciseListService queryExerciseListService; + private final SaveOrUpdateExerciseAnalysisService saveOrUpdateExerciseAnalysisService; + private final QueryExerciseAnalysisService queryExerciseAnalysisService; + private final QueryExerciseDetailsService queryExerciseDetailsService; + private final QueryExercisingUserListService queryExercisingUserListService; + @ResponseStatus(HttpStatus.CREATED) + @PostMapping + public CreateExerciseResponse createExercise(@RequestBody CreateExerciseRequest request) { + return createExerciseService.execute(request); + } + + @ResponseStatus(HttpStatus.NO_CONTENT) + @PatchMapping("/{exercise-id}") + public void finishExercise(@PathVariable(name = "exercise-id") Long exerciseId, + @RequestBody @Valid FinishExerciseRequest request) { + finishExerciseService.execute(exerciseId, request); + } + + @ResponseStatus(HttpStatus.CREATED) + @PostMapping("/locations/{exercise-id}") + public void saveLocation(@PathVariable(name = "exercise-id") Long exerciseId, + @RequestBody @Valid SaveLocationRequest request) { + saveLocationService.execute(exerciseId, request); + } + + @ResponseStatus(HttpStatus.NO_CONTENT) + @PutMapping + public void saveOrUpdateTodayExercise(@RequestBody @Valid SaveExerciseAnalysisRequest request) { + saveOrUpdateExerciseAnalysisService.execute(request); + } + + @GetMapping("/analysis") + public QueryExerciseAnalysisResponse queryExerciseAnalysis() { + return queryExerciseAnalysisService.execute(); + } + + @GetMapping("/lists") + public ExerciseListResponse queryExerciseList() { + return queryExerciseListService.execute(); + } + + @GetMapping("{exercise-id}") + public QueryExerciseDetailsResponse queryExerciseDetails(@PathVariable("exercise-id") Long exerciseId) { + return queryExerciseDetailsService.execute(exerciseId); + } + + @GetMapping("/users/lists") + public QueryExercisingUserListResponse queryExercisingUserList() { + return queryExercisingUserListService.execute(); + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/request/CreateExerciseRequest.java b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/request/CreateExerciseRequest.java index 5d06a6209..3120b00e4 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/request/CreateExerciseRequest.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/request/CreateExerciseRequest.java @@ -1,6 +1,7 @@ package com.walkhub.walkhub.domain.exercise.presentation.dto.request; import com.walkhub.walkhub.domain.exercise.domain.type.GoalType; +import javax.validation.constraints.NotNull; import lombok.Getter; import lombok.NoArgsConstructor; @@ -10,6 +11,7 @@ public class CreateExerciseRequest { private Integer goal; + @NotNull(message = "goal_type은 null, 공백을 허용x") private GoalType goalType; } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/request/FinishExerciseRequest.java b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/request/FinishExerciseRequest.java index 26ae35312..180a2a6f5 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/request/FinishExerciseRequest.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/request/FinishExerciseRequest.java @@ -3,9 +3,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -import java.util.List; +import javax.validation.constraints.PositiveOrZero; @Getter @NoArgsConstructor @@ -17,10 +16,13 @@ public class FinishExerciseRequest { @NotNull(message = "distance는 null, 공백을 허용하지 않습니다.") private Integer distance; + @PositiveOrZero(message = "calorie는 음수일 수 없습니다.") @NotNull(message = "calorie는 null, 공백을 허용하지 않습니다.") - private Integer calorie; + private Double calorie; - @NotEmpty(message = "image_url은 공백을 허용하지 않습니다.") - private List imageUrl; + private String imageUrl; -} + @NotNull(message = "paused_time는 null, 공백을 허용하지 않습니다.") + private Integer pausedTime; + +} \ No newline at end of file diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/request/SaveExerciseAnalysisRequest.java b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/request/SaveExerciseAnalysisRequest.java index b9d2677a5..aaec6ec46 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/request/SaveExerciseAnalysisRequest.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/request/SaveExerciseAnalysisRequest.java @@ -24,6 +24,10 @@ public class SaveExerciseAnalysisRequest { @PositiveOrZero(message = "calorie는 음수를 허용하지 않습니다.") @NotNull(message = "calorie는 Null을 허용하지 않습니다.") - private Integer calorie; + private Double calorie; + + @PositiveOrZero(message = "exercise_time은 음수를 허용하지 않습니다.") + @NotNull(message = "exercise_time은 Null을 허용하지 않습니다") + private Double exerciseTime; } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/ExerciseListResponse.java b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/ExerciseListResponse.java index e5f3d327d..c82eb5c9f 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/ExerciseListResponse.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/ExerciseListResponse.java @@ -1,25 +1,28 @@ package com.walkhub.walkhub.domain.exercise.presentation.dto.response; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.List; +import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; +import java.math.BigDecimal; +import java.time.ZonedDateTime; +import java.util.List; + @Getter @AllArgsConstructor public class ExerciseListResponse { - private final List exerciseList; + private final List exerciseList; - @Getter - @Builder - public static class ExerciseResponse { - private final Long exerciseId; - private final String imageUrl; - private final LocalDateTime startAt; - private final BigDecimal latitude; - private final BigDecimal longitude; - } + @Getter + @Builder + public static class ExerciseResponse { + private final Long exerciseId; + private final String imageUrl; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private final ZonedDateTime startAt; + private final BigDecimal latitude; + private final BigDecimal longitude; + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/QueryExerciseAnalysisResponse.java b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/QueryExerciseAnalysisResponse.java new file mode 100644 index 000000000..469a2c3be --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/QueryExerciseAnalysisResponse.java @@ -0,0 +1,16 @@ +package com.walkhub.walkhub.domain.exercise.presentation.dto.response; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class QueryExerciseAnalysisResponse { + private final List walkCountList; + private final Integer dailyWalkCountGoal; + private final Integer walkCount; + private final Double calorie; + private final Integer distance; +} diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/QueryExerciseDetailsResponse.java b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/QueryExerciseDetailsResponse.java new file mode 100644 index 000000000..d4b5860af --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/QueryExerciseDetailsResponse.java @@ -0,0 +1,24 @@ +package com.walkhub.walkhub.domain.exercise.presentation.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.math.BigDecimal; +import java.util.List; + +@Getter +@AllArgsConstructor +public class QueryExerciseDetailsResponse { + + private final String certifyingShot; + private final List exerciseList; + + @Getter + @Builder + public static class ExerciseResponse { + private final Integer sequence; + private final BigDecimal latitude; + private final BigDecimal longitude; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/QueryExercisingUserListResponse.java b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/QueryExercisingUserListResponse.java new file mode 100644 index 000000000..924b5f9d8 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/presentation/dto/response/QueryExercisingUserListResponse.java @@ -0,0 +1,22 @@ +package com.walkhub.walkhub.domain.exercise.presentation.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class QueryExercisingUserListResponse { + + private final List userList; + + @Getter + @Builder + public static class ExercisingUserListResponse { + private final Long userId; + private final String name; + private final String profileImageUrl; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/service/CreateExerciseService.java b/src/main/java/com/walkhub/walkhub/domain/exercise/service/CreateExerciseService.java index 772614974..008a7f525 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/service/CreateExerciseService.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/service/CreateExerciseService.java @@ -2,28 +2,41 @@ import com.walkhub.walkhub.domain.exercise.domain.Exercise; import com.walkhub.walkhub.domain.exercise.domain.repository.ExerciseRepository; +import com.walkhub.walkhub.domain.exercise.exception.AlreadyExercisingException; import com.walkhub.walkhub.domain.exercise.presentation.dto.request.CreateExerciseRequest; import com.walkhub.walkhub.domain.exercise.presentation.dto.response.CreateExerciseResponse; +import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.facade.UserFacade; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor @Service public class CreateExerciseService { - private final UserFacade userFacade; - private final ExerciseRepository exerciseRepository; + private final UserFacade userFacade; + private final ExerciseRepository exerciseRepository; - public CreateExerciseResponse execute(CreateExerciseRequest request) { - Exercise exercise = exerciseRepository.save( - Exercise.builder() - .user(userFacade.getCurrentUser()) - .goalType(request.getGoalType()) - .goal(request.getGoal()) - .build() - ); + @Transactional + public CreateExerciseResponse execute(CreateExerciseRequest request) { - return new CreateExerciseResponse(exercise.getId()); - } + User user = userFacade.getCurrentUser(); + + if (exerciseRepository.findByIsExercisingTrueAndUser(user).isPresent()) { + throw AlreadyExercisingException.EXCEPTION; + } + + Exercise exercise = exerciseRepository.save( + Exercise.builder() + .user(user) + .goalType(request.getGoalType()) + .goal(request.getGoal()) + .build() + ); + + user.updateIsMeasuring(true); + + return new CreateExerciseResponse(exercise.getId()); + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/service/FinishExerciseService.java b/src/main/java/com/walkhub/walkhub/domain/exercise/service/FinishExerciseService.java index 35ec15d78..16e261381 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/service/FinishExerciseService.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/service/FinishExerciseService.java @@ -1,43 +1,29 @@ package com.walkhub.walkhub.domain.exercise.service; -import com.walkhub.walkhub.domain.exercise.domain.CertifyingShot; import com.walkhub.walkhub.domain.exercise.domain.Exercise; -import com.walkhub.walkhub.domain.exercise.domain.repository.CertifyingShotRepository; import com.walkhub.walkhub.domain.exercise.facade.ExerciseFacade; import com.walkhub.walkhub.domain.exercise.presentation.dto.request.FinishExerciseRequest; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; - @RequiredArgsConstructor @Service public class FinishExerciseService { private final ExerciseFacade exerciseFacade; - private final CertifyingShotRepository certifyingShotRepository; @Transactional public void execute(Long exerciseId, FinishExerciseRequest request) { Exercise exercise = exerciseFacade.getById(exerciseId); - exercise.closeExercise(request.getWalkCount(), request.getDistance(), request.getCalorie()); - - List certifyingShotList = request.getImageUrl() - .stream() - .map(image -> buildCertifyingShot(exercise, image)) - .collect(Collectors.toList()); - - certifyingShotRepository.saveAll(certifyingShotList); - } - - private CertifyingShot buildCertifyingShot(Exercise exercise, String image) { - return CertifyingShot.builder() - .exercise(exercise) - .photo(image) - .build(); + exercise.closeExercise( + request.getWalkCount(), + request.getDistance(), + request.getCalorie(), + request.getImageUrl(), + request.getPausedTime() + ); } } diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/service/QueryExerciseAnalysisService.java b/src/main/java/com/walkhub/walkhub/domain/exercise/service/QueryExerciseAnalysisService.java new file mode 100644 index 000000000..01181be2d --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/service/QueryExerciseAnalysisService.java @@ -0,0 +1,45 @@ +package com.walkhub.walkhub.domain.exercise.service; + +import com.walkhub.walkhub.domain.exercise.domain.ExerciseAnalysis; +import com.walkhub.walkhub.domain.exercise.domain.repository.ExerciseAnalysisRepository; +import com.walkhub.walkhub.domain.exercise.presentation.dto.response.QueryExerciseAnalysisResponse; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class QueryExerciseAnalysisService { + + private final ExerciseAnalysisRepository exerciseAnalysisRepository; + private final UserFacade userFacade; + + @Transactional(readOnly = true) + public QueryExerciseAnalysisResponse execute() { + + User user = userFacade.getCurrentUser(); + LocalDate startAt = LocalDate.now().minusDays(28); + + ExerciseAnalysis exerciseAnalysis = exerciseAnalysisRepository.findByUserAndDate(user, LocalDate.now()) + .orElse(ExerciseAnalysis.builder().build()); + + List walkCountList = exerciseAnalysisRepository.findAllByUserAndDateBetween(user, startAt, LocalDate.now()) + .stream() + .map(ExerciseAnalysis::getWalkCount) + .collect(Collectors.toList()); + + return QueryExerciseAnalysisResponse.builder() + .walkCount(exerciseAnalysis.getWalkCount()) + .walkCountList(walkCountList) + .calorie(exerciseAnalysis.getCalorie()) + .dailyWalkCountGoal(user.getDailyWalkCountGoal()) + .distance(exerciseAnalysis.getDistance()) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/service/QueryExerciseDetailsService.java b/src/main/java/com/walkhub/walkhub/domain/exercise/service/QueryExerciseDetailsService.java new file mode 100644 index 000000000..4aef27940 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/service/QueryExerciseDetailsService.java @@ -0,0 +1,37 @@ +package com.walkhub.walkhub.domain.exercise.service; + +import com.walkhub.walkhub.domain.exercise.domain.Exercise; +import com.walkhub.walkhub.domain.exercise.domain.repository.LocationRepository; +import com.walkhub.walkhub.domain.exercise.facade.ExerciseFacade; +import com.walkhub.walkhub.domain.exercise.presentation.dto.response.QueryExerciseDetailsResponse; +import com.walkhub.walkhub.domain.exercise.presentation.dto.response.QueryExerciseDetailsResponse.ExerciseResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class QueryExerciseDetailsService { + + private final ExerciseFacade exerciseFacade; + private final LocationRepository locationRepository; + + @Transactional(readOnly = true) + public QueryExerciseDetailsResponse execute(Long exerciseId) { + Exercise exercise = exerciseFacade.getById(exerciseId); + + List locations = locationRepository.findAllByExercise(exercise) + .stream() + .map(location -> ExerciseResponse.builder() + .sequence(location.getSequence()) + .latitude(location.getLatitude()) + .longitude(location.getLongitude()) + .build()) + .collect(Collectors.toList()); + + return new QueryExerciseDetailsResponse(exercise.getImageUrl(), locations); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/service/QueryExercisingUserListService.java b/src/main/java/com/walkhub/walkhub/domain/exercise/service/QueryExercisingUserListService.java new file mode 100644 index 000000000..f6dcb21c7 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/service/QueryExercisingUserListService.java @@ -0,0 +1,43 @@ +package com.walkhub.walkhub.domain.exercise.service; + +import com.walkhub.walkhub.domain.exercise.presentation.dto.response.QueryExercisingUserListResponse; +import com.walkhub.walkhub.domain.exercise.presentation.dto.response.QueryExercisingUserListResponse.ExercisingUserListResponse; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.domain.user.domain.repository.UserRepository; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class QueryExercisingUserListService { + + private final UserFacade userFacade; + private final UserRepository userRepository; + + @Transactional(readOnly = true) + public QueryExercisingUserListResponse execute() { + User user = userFacade.getCurrentUser(); + + List exercisingList = + userRepository.findTop3BySchoolAndIsMeasuringTrue(user.getSchool()) + .stream() + .map(this::buildExercisingUserList) + .collect(Collectors.toList()); + + return new QueryExercisingUserListResponse(exercisingList); + + } + + private ExercisingUserListResponse buildExercisingUserList(User user) { + return ExercisingUserListResponse.builder() + .userId(user.getId()) + .name(user.getName()) + .profileImageUrl(user.getProfileImageUrl()) + .build(); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/exercise/service/SaveOrUpdateExerciseAnalysisService.java b/src/main/java/com/walkhub/walkhub/domain/exercise/service/SaveOrUpdateExerciseAnalysisService.java index cde119a2d..e90bbadb2 100644 --- a/src/main/java/com/walkhub/walkhub/domain/exercise/service/SaveOrUpdateExerciseAnalysisService.java +++ b/src/main/java/com/walkhub/walkhub/domain/exercise/service/SaveOrUpdateExerciseAnalysisService.java @@ -1,5 +1,6 @@ package com.walkhub.walkhub.domain.exercise.service; +import com.walkhub.walkhub.domain.exercise.cache.ExerciseAnalysisCacheRepository; import com.walkhub.walkhub.domain.exercise.domain.ExerciseAnalysis; import com.walkhub.walkhub.domain.exercise.domain.repository.ExerciseAnalysisRepository; import com.walkhub.walkhub.domain.exercise.presentation.dto.request.SaveExerciseAnalysisRequest; @@ -16,11 +17,13 @@ public class SaveOrUpdateExerciseAnalysisService { private final ExerciseAnalysisRepository exerciseAnalysisRepository; + private final ExerciseAnalysisCacheRepository exerciseAnalysisCacheRepository; private final UserFacade userFacade; @Transactional public void execute(SaveExerciseAnalysisRequest request) { User user = userFacade.getCurrentUser(); + exerciseAnalysisCacheRepository.saveExerciseCache(user.getSchool().getId(), user.getId(), request.getWalkCount().doubleValue()); ExerciseAnalysis exerciseAnalysisToSave = buildOrUpdateExerciseAnalysis(request, user); exerciseAnalysisRepository.save(exerciseAnalysisToSave); } @@ -40,6 +43,7 @@ private ExerciseAnalysis buildExerciseAnalysis(SaveExerciseAnalysisRequest reque .distance(request.getDistance()) .walkCount(request.getWalkCount()) .calorie(request.getCalorie()) + .exerciseTime(request.getExerciseTime()) .user(user) .build(); } diff --git a/src/main/java/com/walkhub/walkhub/domain/notice/domain/Notice.java b/src/main/java/com/walkhub/walkhub/domain/notice/domain/Notice.java index df17d10ef..9228a9ad2 100644 --- a/src/main/java/com/walkhub/walkhub/domain/notice/domain/Notice.java +++ b/src/main/java/com/walkhub/walkhub/domain/notice/domain/Notice.java @@ -3,7 +3,6 @@ import com.walkhub.walkhub.domain.notice.domain.type.Scope; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.global.entity.BaseTimeEntity; -import javax.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -19,7 +18,7 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; -import org.hibernate.validator.constraints.Length; +import javax.validation.constraints.NotNull; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -37,8 +36,8 @@ public class Notice extends BaseTimeEntity { private String content; @NotNull - @Length(max = 7) @Enumerated(EnumType.STRING) + @Column(length = 6) private Scope scope; @ManyToOne(fetch = FetchType.LAZY) diff --git a/src/main/java/com/walkhub/walkhub/domain/notice/domain/repository/NoticeRepository.java b/src/main/java/com/walkhub/walkhub/domain/notice/domain/repository/NoticeRepository.java index b39c9f4fe..cf2c810a9 100644 --- a/src/main/java/com/walkhub/walkhub/domain/notice/domain/repository/NoticeRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/notice/domain/repository/NoticeRepository.java @@ -1,18 +1,8 @@ package com.walkhub.walkhub.domain.notice.domain.repository; import com.walkhub.walkhub.domain.notice.domain.Notice; -import com.walkhub.walkhub.domain.notice.domain.type.Scope; -import com.walkhub.walkhub.domain.school.domain.School; -import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.Param; -import java.util.List; +public interface NoticeRepository extends CrudRepository, NoticeRepositoryCustom { -public interface NoticeRepository extends CrudRepository { - - @Query("select n from Notice n where n.user.school = :school and n.scope = :scope") - List findAllBySchoolAndScope(@Param("school") School school, @Param("scope") Scope scope); - - List findAllByScope(Scope scope); } diff --git a/src/main/java/com/walkhub/walkhub/domain/notice/domain/repository/NoticeRepositoryCustom.java b/src/main/java/com/walkhub/walkhub/domain/notice/domain/repository/NoticeRepositoryCustom.java new file mode 100644 index 000000000..5899684f2 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/notice/domain/repository/NoticeRepositoryCustom.java @@ -0,0 +1,10 @@ +package com.walkhub.walkhub.domain.notice.domain.repository; + +import com.walkhub.walkhub.domain.notice.domain.type.Scope; +import com.walkhub.walkhub.domain.notice.presentation.dto.response.QueryNoticeListResponse.NoticeResponse; +import com.walkhub.walkhub.domain.school.domain.School; +import java.util.List; + +public interface NoticeRepositoryCustom { + List queryNoticeByScopeAndPage(Scope scope, Integer page, School userSchool); +} diff --git a/src/main/java/com/walkhub/walkhub/domain/notice/domain/repository/NoticeRepositoryCustomImpl.java b/src/main/java/com/walkhub/walkhub/domain/notice/domain/repository/NoticeRepositoryCustomImpl.java new file mode 100644 index 000000000..3caaa9270 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/notice/domain/repository/NoticeRepositoryCustomImpl.java @@ -0,0 +1,53 @@ +package com.walkhub.walkhub.domain.notice.domain.repository; + +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.walkhub.walkhub.domain.notice.domain.type.Scope; +import com.walkhub.walkhub.domain.notice.presentation.dto.response.QQueryNoticeListResponse_NoticeResponse; +import com.walkhub.walkhub.domain.notice.presentation.dto.response.QQueryNoticeListResponse_Writer; +import com.walkhub.walkhub.domain.notice.presentation.dto.response.QueryNoticeListResponse.NoticeResponse; +import com.walkhub.walkhub.domain.school.domain.School; +import java.util.List; +import lombok.RequiredArgsConstructor; + +import static com.walkhub.walkhub.domain.notice.domain.QNotice.notice; +import static com.walkhub.walkhub.domain.user.domain.QUser.user; +import static com.walkhub.walkhub.domain.school.domain.QSchool.school; + +@RequiredArgsConstructor +public class NoticeRepositoryCustomImpl implements NoticeRepositoryCustom { + + private final JPAQueryFactory queryFactory; + + @Override + public List queryNoticeByScopeAndPage(Scope scope, Integer page, School userSchool) { + long size = 10; + return queryFactory + .select(new QQueryNoticeListResponse_NoticeResponse( + notice.id, + notice.title, + notice.content, + notice.createdAt, + new QQueryNoticeListResponse_Writer( + user.id, + user.name, + user.profileImageUrl + )) + ) + .from(notice) + .join(notice.user, user) + .join(user.school, school) + .where( + notice.scope.eq(scope), + scopeFilter(scope, userSchool) + ) + .offset((long) page * size) + .limit(size) + .orderBy(notice.createdAt.desc()) + .fetch(); + } + + private BooleanExpression scopeFilter(Scope scope, School userSchool) { + return scope.equals(Scope.SCHOOL) ? school.eq(userSchool) : null; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/notice/presentation/NoticeController.java b/src/main/java/com/walkhub/walkhub/domain/notice/presentation/NoticeController.java index ca5dd52a8..03b192d79 100644 --- a/src/main/java/com/walkhub/walkhub/domain/notice/presentation/NoticeController.java +++ b/src/main/java/com/walkhub/walkhub/domain/notice/presentation/NoticeController.java @@ -30,8 +30,8 @@ public class NoticeController { private final DeleteNoticeService deleteNoticeService; @GetMapping("/list") - public QueryNoticeListResponse queryNoticeList(@RequestParam Scope scope) { - return queryNoticeListService.execute(scope); + public QueryNoticeListResponse queryNoticeList(@RequestParam Scope scope, @RequestParam Integer page) { + return queryNoticeListService.execute(scope, page); } @ResponseStatus(HttpStatus.CREATED) diff --git a/src/main/java/com/walkhub/walkhub/domain/notice/presentation/dto/response/QueryNoticeListResponse.java b/src/main/java/com/walkhub/walkhub/domain/notice/presentation/dto/response/QueryNoticeListResponse.java index 59bc56c4e..130e7cee9 100644 --- a/src/main/java/com/walkhub/walkhub/domain/notice/presentation/dto/response/QueryNoticeListResponse.java +++ b/src/main/java/com/walkhub/walkhub/domain/notice/presentation/dto/response/QueryNoticeListResponse.java @@ -1,33 +1,55 @@ package com.walkhub.walkhub.domain.notice.presentation.dto.response; -import java.time.LocalDateTime; -import java.util.List; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.querydsl.core.annotations.QueryProjection; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; +import java.time.ZonedDateTime; +import java.util.List; + @Getter @AllArgsConstructor public class QueryNoticeListResponse { - public final List noticeList; - - @Getter - @Builder - public static class NoticeResponse { - private final Long id; - private final String title; - private final String content; - private final LocalDateTime createdAt; - private final Writer writer; - } - - @Getter - @Builder - public static class Writer { - private final Long id; - private final String name; - private final String profileImageUrl; - } + public final List noticeList; + + @Getter + @Builder + public static class NoticeResponse { + private final Long id; + private final String title; + private final String content; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private final ZonedDateTime createdAt; + private final Writer writer; + + @QueryProjection + public NoticeResponse(Long id, String title, String content, ZonedDateTime createdAt, + Writer writer) { + this.id = id; + this.title = title; + this.content = content; + this.createdAt = createdAt; + this.writer = writer; + } + } + + @Getter + @Builder + public static class Writer { + private final Long id; + private final String name; + private final String profileImageUrl; + + @QueryProjection + public Writer(Long id, String name, String profileImageUrl) { + this.id = id; + this.name = name; + this.profileImageUrl = profileImageUrl; + } + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/notice/service/CreateNoticeService.java b/src/main/java/com/walkhub/walkhub/domain/notice/service/CreateNoticeService.java index 28565a90a..fbc1f978a 100644 --- a/src/main/java/com/walkhub/walkhub/domain/notice/service/CreateNoticeService.java +++ b/src/main/java/com/walkhub/walkhub/domain/notice/service/CreateNoticeService.java @@ -5,7 +5,6 @@ import com.walkhub.walkhub.domain.notice.domain.type.Scope; import com.walkhub.walkhub.domain.notice.presentation.dto.request.CreateNoticeRequest; import com.walkhub.walkhub.domain.user.domain.User; -import com.walkhub.walkhub.domain.user.exception.SectionNotFoundException; import com.walkhub.walkhub.domain.user.facade.UserFacade; import com.walkhub.walkhub.global.enums.Authority; import com.walkhub.walkhub.global.exception.InvalidRoleException; @@ -24,9 +23,8 @@ public class CreateNoticeService { public void execute(CreateNoticeRequest request) { User user = userFacade.getCurrentUser(); - if (Scope.SCHOOL.equals(request.getScope()) && user.getSection() == null) { - throw SectionNotFoundException.EXCEPTION; - } else if (!Authority.ROOT.equals(user.getAuthority())) { + if (request.getScope().equals(Scope.SCHOOL) && !user.getAuthority().equals(Authority.ROOT) || + request.getScope().equals(Scope.ALL) && !user.getAuthority().equals(Authority.SU)) { throw InvalidRoleException.EXCEPTION; } diff --git a/src/main/java/com/walkhub/walkhub/domain/notice/service/DeleteNoticeService.java b/src/main/java/com/walkhub/walkhub/domain/notice/service/DeleteNoticeService.java index 4e6e91424..c2624fea8 100644 --- a/src/main/java/com/walkhub/walkhub/domain/notice/service/DeleteNoticeService.java +++ b/src/main/java/com/walkhub/walkhub/domain/notice/service/DeleteNoticeService.java @@ -3,6 +3,9 @@ import com.walkhub.walkhub.domain.notice.domain.Notice; import com.walkhub.walkhub.domain.notice.domain.repository.NoticeRepository; import com.walkhub.walkhub.domain.notice.exception.NoticeNotFoundException; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import com.walkhub.walkhub.global.exception.InvalidRoleException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -11,13 +14,20 @@ @Service public class DeleteNoticeService { + private final UserFacade userFacade; private final NoticeRepository noticeRepository; @Transactional public void execute(Long noticeId) { + User user = userFacade.getCurrentUser(); + Notice notice = noticeRepository.findById(noticeId) .orElseThrow(() -> NoticeNotFoundException.EXCEPTION); + if (!notice.getUser().equals(user)) { + throw InvalidRoleException.EXCEPTION; + } + noticeRepository.delete(notice); } } diff --git a/src/main/java/com/walkhub/walkhub/domain/notice/service/QueryNoticeListService.java b/src/main/java/com/walkhub/walkhub/domain/notice/service/QueryNoticeListService.java index 3d95a012c..1fd0a8932 100644 --- a/src/main/java/com/walkhub/walkhub/domain/notice/service/QueryNoticeListService.java +++ b/src/main/java/com/walkhub/walkhub/domain/notice/service/QueryNoticeListService.java @@ -1,11 +1,9 @@ package com.walkhub.walkhub.domain.notice.service; -import com.walkhub.walkhub.domain.notice.domain.Notice; import com.walkhub.walkhub.domain.notice.domain.repository.NoticeRepository; import com.walkhub.walkhub.domain.notice.domain.type.Scope; import com.walkhub.walkhub.domain.notice.presentation.dto.response.QueryNoticeListResponse; import com.walkhub.walkhub.domain.notice.presentation.dto.response.QueryNoticeListResponse.NoticeResponse; -import com.walkhub.walkhub.domain.notice.presentation.dto.response.QueryNoticeListResponse.Writer; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.facade.UserFacade; import lombok.RequiredArgsConstructor; @@ -13,8 +11,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.stream.Collectors; - @RequiredArgsConstructor @Service public class QueryNoticeListService { @@ -23,36 +19,12 @@ public class QueryNoticeListService { private final UserFacade userFacade; @Transactional(readOnly = true) - public QueryNoticeListResponse execute(Scope scope) { + public QueryNoticeListResponse execute(Scope scope, Integer page) { User user = userFacade.getCurrentUser(); - List noticeList; - - if (scope.equals(Scope.ALL)) { - noticeList = noticeRepository.findAllByScope(scope); - } else { - noticeList = noticeRepository - .findAllBySchoolAndScope(user.getSection().getSchool(), scope); - } - return new QueryNoticeListResponse( - noticeList.stream() - .map(this::noticeResponseBuilder) - .collect(Collectors.toList()) - ); - } + List noticeResponseList = noticeRepository + .queryNoticeByScopeAndPage(scope, page, user.getSchool()); - private NoticeResponse noticeResponseBuilder(Notice notice) { - return NoticeResponse.builder() - .id(notice.getId()) - .title(notice.getTitle()) - .content(notice.getContent()) - .createdAt(notice.getCreatedAt()) - .writer(Writer.builder() - .id(notice.getUser().getId()) - .name(notice.getUser().getName()) - .profileImageUrl(notice.getUser().getProfileImageUrl()) - .build()) - .build(); + return new QueryNoticeListResponse(noticeResponseList); } - } diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/domain/SchoolRankId.java b/src/main/java/com/walkhub/walkhub/domain/rank/domain/SchoolRankId.java index 08f4b5747..d42677c31 100644 --- a/src/main/java/com/walkhub/walkhub/domain/rank/domain/SchoolRankId.java +++ b/src/main/java/com/walkhub/walkhub/domain/rank/domain/SchoolRankId.java @@ -1,13 +1,9 @@ package com.walkhub.walkhub.domain.rank.domain; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; +import java.time.LocalDate; +import lombok.*; import java.io.Serializable; -import java.time.LocalDateTime; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -16,7 +12,7 @@ public class SchoolRankId implements Serializable { private Long schoolId; - private LocalDateTime createdAt; + private LocalDate createdAt; private String dateType; } diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/domain/UserRank.java b/src/main/java/com/walkhub/walkhub/domain/rank/domain/UserRank.java index cdc705c9b..73bdb7808 100644 --- a/src/main/java/com/walkhub/walkhub/domain/rank/domain/UserRank.java +++ b/src/main/java/com/walkhub/walkhub/domain/rank/domain/UserRank.java @@ -1,15 +1,14 @@ package com.walkhub.walkhub.domain.rank.domain; +import com.walkhub.walkhub.domain.rank.domain.type.UserRankScope; +import com.walkhub.walkhub.global.enums.DateType; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.IdClass; +import javax.persistence.*; import java.time.LocalDate; @Getter @@ -26,10 +25,12 @@ public class UserRank { private LocalDate createdAt; @Id - private String dateType; + @Enumerated(EnumType.STRING) + private DateType dateType; @Id - private String scopeType; + @Enumerated(EnumType.STRING) + private UserRankScope scopeType; @Column(length = 20, nullable = false) private String name; diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/SchoolRankRepository.java b/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/SchoolRankRepository.java index e7e00f334..c304871c2 100644 --- a/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/SchoolRankRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/SchoolRankRepository.java @@ -4,5 +4,22 @@ import com.walkhub.walkhub.domain.rank.domain.SchoolRankId; import org.springframework.data.repository.CrudRepository; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + public interface SchoolRankRepository extends CrudRepository { + Optional findBySchoolIdAndDateTypeAndCreatedAtBetween( + Long schoolId, + String dateType, LocalDate startAt, LocalDate endAt + ); + + List findAllByDateTypeAndCreatedAtBetweenOrderByRankingAsc( + String dateType, LocalDate createdAt, LocalDate createdAt2 + ); + + List findAllByDateTypeAndNameContainingAndCreatedAtBetweenOrderByRankingAsc( + String dateType, String name, LocalDate startAt, + LocalDate endAt + ); } diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/UserRankRepository.java b/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/UserRankRepository.java index ba96d8f2f..ce9162d7c 100644 --- a/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/UserRankRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/UserRankRepository.java @@ -5,6 +5,6 @@ import java.util.List; -public interface UserRankRepository extends JpaRepository { +public interface UserRankRepository extends JpaRepository, UserRankRepositoryCustom { List findAllBySchoolIdAndNameContainingAndDateType(Long schoolId, String name, String dateType); } diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/UserRankRepositoryCustom.java b/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/UserRankRepositoryCustom.java new file mode 100644 index 000000000..e918900a4 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/UserRankRepositoryCustom.java @@ -0,0 +1,12 @@ +package com.walkhub.walkhub.domain.rank.domain.repository; + +import com.walkhub.walkhub.domain.rank.domain.repository.vo.UserRankVO; +import com.walkhub.walkhub.global.enums.DateType; + +import java.time.LocalDate; +import java.util.List; + +public interface UserRankRepositoryCustom { + UserRankVO getMyRankByUserId(Long userId, Integer grade, Integer classNum, DateType dateType, LocalDate date); + List getUserRankListBySchoolId(Long schoolId, Integer grade, Integer classNum, DateType dateType, LocalDate date); +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/UserRankRepositoryCustomImpl.java b/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/UserRankRepositoryCustomImpl.java new file mode 100644 index 000000000..1b65f25ec --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/UserRankRepositoryCustomImpl.java @@ -0,0 +1,95 @@ +package com.walkhub.walkhub.domain.rank.domain.repository; + +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.walkhub.walkhub.domain.rank.domain.repository.vo.QUserRankVO; +import com.walkhub.walkhub.domain.rank.domain.repository.vo.UserRankVO; +import com.walkhub.walkhub.domain.rank.domain.type.UserRankScope; +import com.walkhub.walkhub.global.enums.DateType; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDate; +import java.util.List; + +import static com.walkhub.walkhub.domain.rank.domain.QUserRank.userRank; + +@RequiredArgsConstructor +public class UserRankRepositoryCustomImpl implements UserRankRepositoryCustom { + + private final JPAQueryFactory queryFactory; + private static final Long LIMIT = 100L; + + @Override + public UserRankVO getMyRankByUserId(Long userId, Integer grade, Integer classNum, DateType dateType, LocalDate date) { + return queryFactory + .select(new QUserRankVO( + userRank.userId, + userRank.name, + userRank.ranking, + userRank.profileImageUrl, + userRank.walkCount + )) + .from(userRank) + .where( + userIdEq(userId), + classNumEq(classNum), + dateTypeEq(dateType), + createdAtEq(date) + ) + .fetchOne(); + } + + @Override + public List getUserRankListBySchoolId(Long schoolId, Integer grade, Integer classNum, DateType dateType, LocalDate date) { + return queryFactory + .select(new QUserRankVO( + userRank.userId, + userRank.name, + userRank.ranking, + userRank.profileImageUrl, + userRank.walkCount + )) + .from(userRank) + .where( + schoolIdEq(schoolId), + gradeEq(grade), + classNumEq(classNum), + dateTypeEq(dateType), + createdAtEq(date) + ) + .limit(LIMIT) + .orderBy(userRank.ranking.asc()) + .fetch(); + } + + private BooleanExpression userIdEq(Long userId) { + return userId != null ? userRank.userId.eq(userId) : null; + } + + private BooleanExpression schoolIdEq(Long schoolId) { + return schoolId != null ? userRank.schoolId.eq(schoolId) : null; + } + + private BooleanExpression gradeEq(Integer grade) { + return grade != null ? userRank.grade.eq(grade) : null; + } + + private BooleanExpression classNumEq(Integer classNum) { + return classNum != null ? userRank.scopeType.eq(UserRankScope.CLASS).and(userRank.classNum.eq(classNum)) : userRank.scopeType.eq(UserRankScope.SCHOOL); + } + + private BooleanExpression dateTypeEq(DateType dateType) { + switch (dateType) { + case WEEK: + return userRank.dateType.eq(DateType.WEEK); + case MONTH: + return userRank.dateType.eq(DateType.MONTH); + default: + return null; + } + } + + private BooleanExpression createdAtEq(LocalDate date) { + return date != null ? userRank.createdAt.eq(date) : null; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/vo/UserRankVO.java b/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/vo/UserRankVO.java new file mode 100644 index 000000000..d76d7dba7 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/vo/UserRankVO.java @@ -0,0 +1,22 @@ +package com.walkhub.walkhub.domain.rank.domain.repository.vo; + +import com.querydsl.core.annotations.QueryProjection; +import lombok.Getter; + +@Getter +public class UserRankVO { + private final Long userId; + private final String name; + private final Integer ranking; + private final String profileImageUrl; + private final Integer walkCount; + + @QueryProjection + public UserRankVO(Long userId, String name, Integer ranking, String profileImageUrl, Integer walkCount) { + this.userId = userId; + this.name = name; + this.ranking = ranking; + this.profileImageUrl = profileImageUrl; + this.walkCount = walkCount; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/vo/UserRankWriterVO.java b/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/vo/UserRankWriterVO.java new file mode 100644 index 000000000..00f48e924 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/domain/repository/vo/UserRankWriterVO.java @@ -0,0 +1,22 @@ +package com.walkhub.walkhub.domain.rank.domain.repository.vo; + +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDate; + +@Getter +@Builder +public class UserRankWriterVO { + private Long userId; + private LocalDate createdAt; + private String dateType; + private String scopeType; + private String name; + private Integer grade; + private Integer classNum; + private String profileImageUrl; + private Integer walkCount; + private Integer ranking; + private Long schoolId; +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/domain/type/SchoolDateType.java b/src/main/java/com/walkhub/walkhub/domain/rank/domain/type/SchoolDateType.java new file mode 100644 index 000000000..031b95a2b --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/domain/type/SchoolDateType.java @@ -0,0 +1,6 @@ +package com.walkhub.walkhub.domain.rank.domain.type; + +public enum SchoolDateType { + WEEK, + MONTH +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/domain/type/UserRankScope.java b/src/main/java/com/walkhub/walkhub/domain/rank/domain/type/UserRankScope.java new file mode 100644 index 000000000..57a05ed1c --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/domain/type/UserRankScope.java @@ -0,0 +1,6 @@ +package com.walkhub.walkhub.domain.rank.domain.type; + +public enum UserRankScope { + SCHOOL, + CLASS +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/facade/UserRankFacade.java b/src/main/java/com/walkhub/walkhub/domain/rank/facade/UserRankFacade.java new file mode 100644 index 000000000..a2c0042bc --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/facade/UserRankFacade.java @@ -0,0 +1,27 @@ +package com.walkhub.walkhub.domain.rank.facade; + +import com.walkhub.walkhub.domain.rank.domain.repository.vo.UserRankVO; +import com.walkhub.walkhub.domain.rank.presentation.dto.response.UserRankListResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class UserRankFacade { + + public List buildWeekOrMonthUsersRankResponse(List userRanks) { + return userRanks.stream() + .map(userRank -> UserRankListResponse.UserRankResponse.builder() + .userId(userRank.getUserId()) + .name(userRank.getName()) + .ranking(userRank.getRanking()) + .profileImageUrl(userRank.getProfileImageUrl()) + .walkCount(userRank.getWalkCount()) + .build() + ).collect(Collectors.toList()); + } + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/job/UserRankJob.java b/src/main/java/com/walkhub/walkhub/domain/rank/job/UserRankJob.java index c32ab58ac..73f2938bc 100644 --- a/src/main/java/com/walkhub/walkhub/domain/rank/job/UserRankJob.java +++ b/src/main/java/com/walkhub/walkhub/domain/rank/job/UserRankJob.java @@ -1,7 +1,9 @@ package com.walkhub.walkhub.domain.rank.job; -import com.walkhub.walkhub.domain.rank.domain.*; +import com.walkhub.walkhub.domain.rank.domain.repository.vo.UserRankWriterVO; +import com.walkhub.walkhub.domain.rank.domain.type.UserRankScope; import com.walkhub.walkhub.domain.rank.presentation.dto.response.UserRankInfo; +import com.walkhub.walkhub.global.enums.DateType; import lombok.RequiredArgsConstructor; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; @@ -19,108 +21,106 @@ import org.springframework.jdbc.core.ArgumentPreparedStatementSetter; import org.springframework.jdbc.core.SqlParameter; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; import java.sql.Types; import java.time.LocalDate; +import static com.walkhub.walkhub.domain.rank.job.constant.RankJobConstant.*; + @Configuration @RequiredArgsConstructor public class UserRankJob { - private static final Integer CHUNK_SIZE = 100; - private final JobBuilderFactory jobBuilderFactory; private final StepBuilderFactory stepBuilderFactory; - private final EntityManagerFactory em; private final DataSource dataSource; @Bean public Job userJob() { - return jobBuilderFactory.get("userRankJob") - .start(weeklyUserSchoolRankStep(null)) - .next(monthlyUserSchoolRankStep(null)) - .next(weeklyUserClassRankStep(null)) - .next(monthlyUserClassRankStep(null)) + return jobBuilderFactory.get(USER_RANK_JOB) + .start(weekUserSchoolRankStep(null)) + .next(monthUserSchoolRankStep(null)) + .next(weekUserClassRankStep(null)) + .next(monthUserClassRankStep(null)) .build(); } @Bean @JobScope - public Step weeklyUserSchoolRankStep(@Value("#{jobParameters[jobKey]}") String jobKey) { - return stepBuilderFactory.get("weeklyUserSchoolRankStep") - .chunk(CHUNK_SIZE) - .reader(weeklyUserSchoolRankReader(null)) - .processor(weeklyUserSchoolRankProcessor(null)) + public Step weekUserSchoolRankStep(@Value("#{jobParameters[jobKey]}") String jobKey) { + return stepBuilderFactory.get(WEEK_USER_SCHOOL_RANK_STEP) + .chunk(CHUNK_SIZE) + .reader(weekUserSchoolRankReader(null)) + .processor(weekUserSchoolRankProcessor(null)) .writer(userRankWriter(null)) .build(); } @Bean @JobScope - public Step monthlyUserSchoolRankStep(@Value("#{jobParameters[jobKey]}") String jobKey) { - return stepBuilderFactory.get("monthlyUserSchoolRankStep") - .chunk(CHUNK_SIZE) - .reader(monthlyUserSchoolRankReader(null)) - .processor(monthlyUserSchoolRankProcessor(null)) + public Step monthUserSchoolRankStep(@Value("#{jobParameters[jobKey]}") String jobKey) { + return stepBuilderFactory.get(MONTH_USER_SCHOOL_RANK_STEP) + .chunk(CHUNK_SIZE) + .reader(monthUserSchoolRankReader(null)) + .processor(monthUserSchoolRankProcessor(null)) .writer(userRankWriter(null)) .build(); } @Bean @JobScope - public Step weeklyUserClassRankStep(@Value("#{jobParameters[jobKey]}") String jobKey) { - return stepBuilderFactory.get("weeklyUserClassRankStep") - .chunk(CHUNK_SIZE) - .reader(weeklyUserClassRankReader(null)) - .processor(weeklyUserClassRankProcessor(null)) + public Step weekUserClassRankStep(@Value("#{jobParameters[jobKey]}") String jobKey) { + return stepBuilderFactory.get(WEEK_USER_CLASS_RANK_STEP) + .chunk(CHUNK_SIZE) + .reader(weekUserClassRankReader(null)) + .processor(weekUserClassRankProcessor(null)) .writer(userRankWriter(null)) .build(); } @Bean @JobScope - public Step monthlyUserClassRankStep(@Value("#{jobParameters[jobKey]}") String jobKey) { - return stepBuilderFactory.get("monthlyUserClassRankStep") - .chunk(CHUNK_SIZE) - .reader(monthlyUserClassRankReader(null)) - .processor(monthlyUserClassRankProcessor(null)) + public Step monthUserClassRankStep(@Value("#{jobParameters[jobKey]}") String jobKey) { + return stepBuilderFactory.get(MONTH_USER_CLASS_RANK_STEP) + .chunk(CHUNK_SIZE) + .reader(monthUserClassRankReader(null)) + .processor(monthUserClassRankProcessor(null)) .writer(userRankWriter(null)) .build(); } @Bean @StepScope - public StoredProcedureItemReader weeklyUserSchoolRankReader(@Value("#{jobParameters[stepKey]}") Integer type) { - return callProcedure("weeklyUserSchoolRankReader", "SELECT_USER_RANK_BY_SCHOOL", 7); + public StoredProcedureItemReader weekUserSchoolRankReader(@Value("#{jobParameters[stepKey]}") Integer type) { + return callProcedure(WEEK_USER_SCHOOL_RANK_READER, SELECT_BY_SCHOOL_PROCEDURE, 7); } @Bean @StepScope - public StoredProcedureItemReader monthlyUserSchoolRankReader(@Value("#{jobParameters[stepKey]}") Integer type) { - return callProcedure("monthlyUserSchoolRankReader", "SELECT_USER_RANK_BY_SCHOOL", 28); + public StoredProcedureItemReader monthUserSchoolRankReader(@Value("#{jobParameters[stepKey]}") Integer type) { + return callProcedure(MONTH_USER_SCHOOL_RANK_READER, SELECT_BY_SCHOOL_PROCEDURE, 28); } @Bean @StepScope - public StoredProcedureItemReader weeklyUserClassRankReader(@Value("#{jobParameters[stepKey]}") Integer type) { - return callProcedure("weeklyUserClassRankReader", "SELECT_USER_RANK_BY_CLASS", 7); + public StoredProcedureItemReader weekUserClassRankReader(@Value("#{jobParameters[stepKey]}") Integer type) { + return callProcedure(WEEK_USER_CLASS_RANK_READER, SELECT_BY_CLASS_PROCEDURE, 7); } @Bean @StepScope - public StoredProcedureItemReader monthlyUserClassRankReader(@Value("#{jobParameters[stepKey]}") Integer type) { - return callProcedure("monthlyUserClassRankReader", "SELECT_USER_RANK_BY_CLASS", 28); + public StoredProcedureItemReader monthUserClassRankReader(@Value("#{jobParameters[stepKey]}") Integer type) { + return callProcedure(MONTH_USER_CLASS_RANK_READER, SELECT_BY_CLASS_PROCEDURE, 28); } @Bean @StepScope - public ItemProcessor weeklyUserSchoolRankProcessor(@Value("#{jobParameters[jobKey]}") String jobKey) { - return rankInfo -> UserRank.builder() + public ItemProcessor weekUserSchoolRankProcessor(@Value("#{jobParameters[jobKey]}") String jobKey) { + return rankInfo -> UserRankWriterVO.builder() .userId(rankInfo.getUserId()) .createdAt(LocalDate.now()) - .dateType("WEEK") - .scopeType("SCHOOL") + .dateType(DateType.WEEK.name()) + .scopeType(UserRankScope.SCHOOL.name()) .schoolId(rankInfo.getSchoolId()) .name(rankInfo.getName()) .grade(rankInfo.getGrade()) @@ -133,12 +133,12 @@ public ItemProcessor weeklyUserSchoolRankProcessor(@Valu @Bean @StepScope - public ItemProcessor monthlyUserSchoolRankProcessor(@Value("#{jobParameters[jobKey]}") String jobKey) { - return rankInfo -> UserRank.builder() + public ItemProcessor monthUserSchoolRankProcessor(@Value("#{jobParameters[jobKey]}") String jobKey) { + return rankInfo -> UserRankWriterVO.builder() .userId(rankInfo.getUserId()) .createdAt(LocalDate.now()) - .dateType("MONTH") - .scopeType("SCHOOL") + .dateType(DateType.MONTH.name()) + .scopeType(UserRankScope.SCHOOL.name()) .schoolId(rankInfo.getSchoolId()) .name(rankInfo.getName()) .grade(rankInfo.getGrade()) @@ -151,12 +151,12 @@ public ItemProcessor monthlyUserSchoolRankProcessor(@Val @Bean @StepScope - public ItemProcessor weeklyUserClassRankProcessor(@Value("#{jobParameters[jobKey]}") String jobKey) { - return rankInfo -> UserRank.builder() + public ItemProcessor weekUserClassRankProcessor(@Value("#{jobParameters[jobKey]}") String jobKey) { + return rankInfo -> UserRankWriterVO.builder() .userId(rankInfo.getUserId()) .createdAt(LocalDate.now()) - .dateType("WEEK") - .scopeType("CLASS") + .dateType(DateType.WEEK.name()) + .scopeType(UserRankScope.CLASS.name()) .schoolId(rankInfo.getSchoolId()) .name(rankInfo.getName()) .grade(rankInfo.getGrade()) @@ -169,12 +169,12 @@ public ItemProcessor weeklyUserClassRankProcessor(@Value @Bean @StepScope - public ItemProcessor monthlyUserClassRankProcessor(@Value("#{jobParameters[jobKey]}") String jobKey) { - return rankInfo -> UserRank.builder() + public ItemProcessor monthUserClassRankProcessor(@Value("#{jobParameters[jobKey]}") String jobKey) { + return rankInfo -> UserRankWriterVO.builder() .userId(rankInfo.getUserId()) .createdAt(LocalDate.now()) - .dateType("MONTH") - .scopeType("CLASS") + .dateType(DateType.MONTH.name()) + .scopeType(UserRankScope.CLASS.name()) .schoolId(rankInfo.getSchoolId()) .name(rankInfo.getName()) .grade(rankInfo.getGrade()) @@ -187,10 +187,10 @@ public ItemProcessor monthlyUserClassRankProcessor(@Valu @Bean @StepScope - public JdbcBatchItemWriter userRankWriter(@Value("#{jobParameters[jobKey]}") String jobKey) { - JdbcBatchItemWriter writer = new JdbcBatchItemWriterBuilder() + public JdbcBatchItemWriter userRankWriter(@Value("#{jobParameters[jobKey]}") String jobKey) { + JdbcBatchItemWriter writer = new JdbcBatchItemWriterBuilder() .dataSource(dataSource) - .sql("CALL SAVE_USER_RANK(:user_id, :created_at, :date_type, :scope_type, :school_id, :name, :grade, :class_num, :profile_image_url, :ranking, :walk_count)") + .sql(CALL_SAVE_PROCEDURE) .beanMapped() .build(); diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/job/constant/RankJobConstant.java b/src/main/java/com/walkhub/walkhub/domain/rank/job/constant/RankJobConstant.java index 0d17bb332..e001cdc2d 100644 --- a/src/main/java/com/walkhub/walkhub/domain/rank/job/constant/RankJobConstant.java +++ b/src/main/java/com/walkhub/walkhub/domain/rank/job/constant/RankJobConstant.java @@ -21,4 +21,20 @@ public final class RankJobConstant { public static final String SELECT_PROCEDURE_NAME = "SELECT_SCHOOL_RANK_BY_DATETYPE"; public static final String SQL_SAVE_SCHOOL_RANK = "CALL SAVE_SCHOOL_RANK(:schoolId, :createdAt, :dateType, :name, :logoImageUrl, :userCount,:walkCount, :ranking)"; + // user rank + public static final String USER_RANK_JOB = "userRankJob"; + public static final String WEEK_USER_SCHOOL_RANK_STEP = "weekUserSchoolRankStep"; + public static final String MONTH_USER_SCHOOL_RANK_STEP = "monthUserSchoolRankStep"; + public static final String WEEK_USER_CLASS_RANK_STEP = "weekUserClassRankStep"; + public static final String MONTH_USER_CLASS_RANK_STEP = "monthUserClassRankStep"; + + public static final String WEEK_USER_SCHOOL_RANK_READER = "weekUserSchoolRankReader"; + public static final String MONTH_USER_SCHOOL_RANK_READER = "monthUserSchoolRankReader"; + public static final String WEEK_USER_CLASS_RANK_READER = "weekUserClassRankReader"; + public static final String MONTH_USER_CLASS_RANK_READER = "monthUserClassRankReader"; + + public static final String SELECT_BY_SCHOOL_PROCEDURE = "SELECT_USER_RANK_BY_SCHOOL"; + public static final String SELECT_BY_CLASS_PROCEDURE = "SELECT_USER_RANK_BY_CLASS"; + public static final String CALL_SAVE_PROCEDURE = "CALL SAVE_USER_RANK(:userId, :createdAt, :dateType, :scopeType, :schoolId, :name, :grade, :classNum, :profileImageUrl, :ranking, :walkCount)"; + } diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/presentation/RankController.java b/src/main/java/com/walkhub/walkhub/domain/rank/presentation/RankController.java index 3a970fdb0..538347ad4 100644 --- a/src/main/java/com/walkhub/walkhub/domain/rank/presentation/RankController.java +++ b/src/main/java/com/walkhub/walkhub/domain/rank/presentation/RankController.java @@ -1,8 +1,19 @@ package com.walkhub.walkhub.domain.rank.presentation; +import com.walkhub.walkhub.domain.rank.domain.type.SchoolDateType; +import com.walkhub.walkhub.domain.rank.presentation.dto.response.SchoolListResponse; +import com.walkhub.walkhub.domain.rank.domain.type.UserRankScope; +import com.walkhub.walkhub.domain.rank.presentation.dto.response.SchoolRankResponse; import com.walkhub.walkhub.domain.rank.presentation.dto.response.UserListResponse; +import com.walkhub.walkhub.domain.rank.presentation.dto.response.UserRankListResponse; +import com.walkhub.walkhub.domain.rank.service.QueryUserRankListByMySchoolService; +import com.walkhub.walkhub.domain.rank.service.QuerySchoolRankService; +import com.walkhub.walkhub.domain.rank.service.SchoolSearchService; +import com.walkhub.walkhub.domain.rank.service.QueryUserRankListService; import com.walkhub.walkhub.domain.rank.service.UserSearchService; import com.walkhub.walkhub.global.enums.DateType; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -16,6 +27,10 @@ public class RankController { private final UserSearchService userSearchService; + private final QueryUserRankListByMySchoolService queryUserRankListByMySchoolService; + private final QuerySchoolRankService querySchoolRankService; + private final SchoolSearchService schoolSearchService; + private final QueryUserRankListService queryUserRankListService; @GetMapping("/users/search/{school-id}") public UserListResponse userSearch(@PathVariable("school-id") Long schoolId, @@ -23,4 +38,26 @@ public UserListResponse userSearch(@PathVariable("school-id") Long schoolId, @RequestParam DateType dateType) { return userSearchService.execute(schoolId, name, dateType); } + + @GetMapping("/schools") + public SchoolRankResponse querySchoolRank(@RequestParam("schoolDateType") SchoolDateType dateType) { + return querySchoolRankService.execute(dateType); + } + + @GetMapping("/schools/search") + public SchoolListResponse schoolSearch(@RequestParam("name") @NotNull @Size(max = 20) String name, + @RequestParam("schoolDateType") SchoolDateType dateType) { + return schoolSearchService.execute(name, dateType); + } + + @GetMapping("/users/my-school") + public UserRankListResponse queryUserRankListByMySchool(@RequestParam UserRankScope scope, @RequestParam DateType dateType) { + return queryUserRankListByMySchoolService.execute(scope, dateType); + } + + @GetMapping("/users/{school-id}") + public UserRankListResponse queryUserRankList(@RequestParam DateType dateType, + @PathVariable("school-id") Long schoolId) { + return queryUserRankListService.execute(schoolId, dateType); + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/SchoolListResponse.java b/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/SchoolListResponse.java new file mode 100644 index 000000000..8f2503340 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/SchoolListResponse.java @@ -0,0 +1,23 @@ +package com.walkhub.walkhub.domain.rank.presentation.dto.response; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class SchoolListResponse { + + private final List schoolList; + + @Getter + @Builder + public static class SchoolResponse { + private final Long schoolId; + private final String schoolName; + private final Integer ranking; + private final String logoImageUrl; + private final Integer walkCount; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/SchoolRankResponse.java b/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/SchoolRankResponse.java new file mode 100644 index 000000000..e9e11def1 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/SchoolRankResponse.java @@ -0,0 +1,35 @@ +package com.walkhub.walkhub.domain.rank.presentation.dto.response; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class SchoolRankResponse { + + private final MySchoolResponse mySchoolRank; + private final List schoolList; + + @Getter + @Builder + public static class MySchoolResponse { + private final Long schoolId; + private final String name; + private final String logoImageUrl; + private final Integer grade; + private final Integer classNum; + } + + @Getter + @Builder + public static class SchoolResponse { + private final Long schoolId; + private final String name; + private final Integer ranking; + private final Long studentCount; + private final String logoImageUrl; + private final Integer walkCount; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/UserListResponse.java b/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/UserListResponse.java index d762c7569..50b86e185 100644 --- a/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/UserListResponse.java +++ b/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/UserListResponse.java @@ -17,7 +17,7 @@ public class UserListResponse { public static class UserSearchResponse { private final Long userId; private final String name; - private final Integer rank; + private final Integer ranking; private final Integer grade; private final Integer classNum; private final String profileImageUrl; diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/UserRankInfo.java b/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/UserRankInfo.java index 6cfecc7e1..8d4a21696 100644 --- a/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/UserRankInfo.java +++ b/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/UserRankInfo.java @@ -6,19 +6,19 @@ @Getter @Builder public class UserRankInfo { - private Long userId; + private final Long userId; - private String name; + private final String name; - private Long schoolId; + private final Long schoolId; - private Integer grade; + private final Integer grade; - private Integer classNum; + private final Integer classNum; - private String profileImageUrl; + private final String profileImageUrl; - private Integer walkCount; + private final Integer walkCount; - private Integer ranking; + private final Integer ranking; } diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/UserRankListResponse.java b/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/UserRankListResponse.java new file mode 100644 index 000000000..d743f1151 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/presentation/dto/response/UserRankListResponse.java @@ -0,0 +1,24 @@ +package com.walkhub.walkhub.domain.rank.presentation.dto.response; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class UserRankListResponse { + + private final UserRankResponse myRanking; + private final List rankList; + + @Getter + @Builder + public static class UserRankResponse { + private final Long userId; + private final String name; + private final Integer ranking; + private final String profileImageUrl; + private final Integer walkCount; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/service/QuerySchoolRankService.java b/src/main/java/com/walkhub/walkhub/domain/rank/service/QuerySchoolRankService.java new file mode 100644 index 000000000..dfe9cc61a --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/service/QuerySchoolRankService.java @@ -0,0 +1,63 @@ +package com.walkhub.walkhub.domain.rank.service; + +import com.walkhub.walkhub.domain.rank.domain.SchoolRank; +import com.walkhub.walkhub.domain.rank.domain.repository.SchoolRankRepository; +import com.walkhub.walkhub.domain.rank.domain.type.SchoolDateType; +import com.walkhub.walkhub.domain.rank.presentation.dto.response.SchoolRankResponse; +import com.walkhub.walkhub.domain.rank.presentation.dto.response.SchoolRankResponse.MySchoolResponse; +import com.walkhub.walkhub.domain.rank.presentation.dto.response.SchoolRankResponse.SchoolResponse; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Service +public class QuerySchoolRankService { + + private final SchoolRankRepository schoolRankRepository; + private final UserFacade userFacade; + + @Transactional(readOnly = true) + public SchoolRankResponse execute(SchoolDateType dateType) { + User user = userFacade.getCurrentUser(); + + MySchoolResponse mySchoolResponse = schoolRankRepository. + findBySchoolIdAndDateTypeAndCreatedAtBetween(user.getSchool().getId(), dateType.toString(), LocalDate.now().minusWeeks(1), LocalDate.now()) + .map(schoolRank -> mySchoolResponseBuilder(schoolRank, user)) + .orElse(null); + + List schoolResponseList = schoolRankRepository + .findAllByDateTypeAndCreatedAtBetweenOrderByRankingAsc(dateType.toString(), LocalDate.now().minusWeeks(1), LocalDate.now()) + .stream() + .map(this::schoolResponseBuilder) + .collect(Collectors.toList()); + + return new SchoolRankResponse(mySchoolResponse, schoolResponseList); + } + + private MySchoolResponse mySchoolResponseBuilder(SchoolRank schoolRank, User user) { + return MySchoolResponse.builder() + .schoolId(schoolRank.getSchoolId()) + .name(schoolRank.getName()) + .logoImageUrl(schoolRank.getLogoImageUrl()) + .grade(user.getSection().getGrade()) + .classNum(user.getSection().getClassNum()) + .build(); + } + + private SchoolResponse schoolResponseBuilder(SchoolRank schoolRank) { + return SchoolResponse.builder() + .schoolId(schoolRank.getSchoolId()) + .name(schoolRank.getName()) + .ranking(schoolRank.getRanking()) + .studentCount(schoolRank.getUserCount()) + .logoImageUrl(schoolRank.getLogoImageUrl()) + .walkCount(schoolRank.getWalkCount()) + .build(); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/service/QueryUserRankListByMySchoolService.java b/src/main/java/com/walkhub/walkhub/domain/rank/service/QueryUserRankListByMySchoolService.java new file mode 100644 index 000000000..6331b6c5c --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/service/QueryUserRankListByMySchoolService.java @@ -0,0 +1,117 @@ +package com.walkhub.walkhub.domain.rank.service; + +import com.walkhub.walkhub.domain.exercise.cache.ExerciseAnalysisCacheRepository; +import com.walkhub.walkhub.domain.exercise.cache.ExerciseAnalysisDto; +import com.walkhub.walkhub.domain.rank.domain.repository.UserRankRepository; +import com.walkhub.walkhub.domain.rank.domain.repository.vo.UserRankVO; +import com.walkhub.walkhub.domain.rank.domain.type.UserRankScope; +import com.walkhub.walkhub.domain.rank.facade.UserRankFacade; +import com.walkhub.walkhub.domain.rank.presentation.dto.response.UserRankListResponse; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import com.walkhub.walkhub.global.enums.DateType; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +@RequiredArgsConstructor +@Service +public class QueryUserRankListByMySchoolService { + private final UserRankRepository userRankRepository; + private final ExerciseAnalysisCacheRepository exerciseAnalysisCacheRepository; + private final UserFacade userFacade; + private final UserRankFacade userRankFacade; + + public UserRankListResponse execute(UserRankScope scope, DateType dateType) { + User user = userFacade.getCurrentUser(); + LocalDate date = LocalDate.now(); + UserRankListResponse userRankListResponse = null; + + if (dateType.equals(DateType.DAY)) { + userRankListResponse = buildDayRankResponse(user); + } else if (scope.equals(UserRankScope.SCHOOL)) { + userRankListResponse = buildWeekOrMonthRankResponse(user, null, null, dateType, date); + } else if (scope.equals(UserRankScope.CLASS)) { + userRankListResponse = buildWeekOrMonthRankResponse(user, user.getSection().getGrade(), user.getSection().getClassNum(), dateType, date); + } + + return userRankListResponse; + } + + private UserRankListResponse buildDayRankResponse(User user) { + UserRankListResponse.UserRankResponse myRank; + List userRankList = new ArrayList<>(); + + myRank = buildDayMyRank(user); + + List usersDayRank = exerciseAnalysisCacheRepository.getUserIdsByRankTop100(user.getSchool().getId()); + for (ExerciseAnalysisDto users : usersDayRank) { + userRankList.add(buildDayUsersRank(users)); + } + + return UserRankListResponse.builder() + .myRanking(myRank) + .rankList(userRankList) + .build(); + } + + private UserRankListResponse buildWeekOrMonthRankResponse(User user, Integer grade, Integer classNum, DateType dateType, LocalDate date) { + UserRankListResponse.UserRankResponse myRank; + List userRankList; + + myRank = buildWeekOrMonthMyRank(user.getId(), grade, classNum, dateType, date); + + List usersWeekOrMonthRank = userRankRepository.getUserRankListBySchoolId(user.getSchool().getId(), user.getSection().getGrade(), classNum, dateType, date); + userRankList = userRankFacade.buildWeekOrMonthUsersRankResponse(usersWeekOrMonthRank); + + return UserRankListResponse.builder() + .myRanking(myRank) + .rankList(userRankList) + .build(); + } + + private UserRankListResponse.UserRankResponse buildDayMyRank(User user) { + ExerciseAnalysisDto exerciseAnalysisDto = exerciseAnalysisCacheRepository.getUserTodayRank(user.getSchool().getId(), user.getId()); + if (exerciseAnalysisDto == null) { + return null; + } + + return UserRankListResponse.UserRankResponse.builder() + .userId(user.getId()) + .name(user.getName()) + .ranking(exerciseAnalysisDto.getRanking()) + .profileImageUrl(user.getProfileImageUrl()) + .walkCount(exerciseAnalysisDto.getWalkCount()) + .build(); + } + + private UserRankListResponse.UserRankResponse buildDayUsersRank(ExerciseAnalysisDto dayRank) { + User user = userFacade.getUserById(dayRank.getUserId()); + + return UserRankListResponse.UserRankResponse.builder() + .userId(user.getId()) + .name(user.getName()) + .ranking(dayRank.getRanking()) + .profileImageUrl(user.getProfileImageUrl()) + .walkCount(dayRank.getWalkCount()) + .build(); + } + + private UserRankListResponse.UserRankResponse buildWeekOrMonthMyRank(Long userId, Integer grade, Integer classNum, DateType dateType, LocalDate date) { + UserRankVO myRank = userRankRepository.getMyRankByUserId(userId, grade, classNum, dateType, date); + if (myRank == null) { + return null; + } + + return UserRankListResponse.UserRankResponse.builder() + .userId(myRank.getUserId()) + .name(myRank.getName()) + .ranking(myRank.getRanking()) + .profileImageUrl(myRank.getProfileImageUrl()) + .walkCount(myRank.getWalkCount()) + .build(); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/service/QueryUserRankListService.java b/src/main/java/com/walkhub/walkhub/domain/rank/service/QueryUserRankListService.java new file mode 100644 index 000000000..be72a1d79 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/service/QueryUserRankListService.java @@ -0,0 +1,33 @@ +package com.walkhub.walkhub.domain.rank.service; + +import com.walkhub.walkhub.domain.rank.domain.repository.UserRankRepository; +import com.walkhub.walkhub.domain.rank.domain.repository.vo.UserRankVO; +import com.walkhub.walkhub.domain.rank.facade.UserRankFacade; +import com.walkhub.walkhub.domain.rank.presentation.dto.response.UserRankListResponse; +import com.walkhub.walkhub.global.enums.DateType; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.List; + +@RequiredArgsConstructor +@Service +public class QueryUserRankListService { + + private final UserRankRepository userRankRepository; + private final UserRankFacade userRankFacade; + + @Transactional(readOnly = true) + public UserRankListResponse execute(Long schoolId, DateType dateType) { + LocalDate now = LocalDate.now(); + List usersWeekOrMonthRank = userRankRepository.getUserRankListBySchoolId(schoolId, null, null, dateType, now); + List rankList = userRankFacade.buildWeekOrMonthUsersRankResponse(usersWeekOrMonthRank); + + return UserRankListResponse.builder() + .rankList(rankList) + .build(); + } + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/service/SchoolSearchService.java b/src/main/java/com/walkhub/walkhub/domain/rank/service/SchoolSearchService.java new file mode 100644 index 000000000..1877778ef --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/rank/service/SchoolSearchService.java @@ -0,0 +1,36 @@ +package com.walkhub.walkhub.domain.rank.service; + +import com.walkhub.walkhub.domain.rank.domain.repository.SchoolRankRepository; +import com.walkhub.walkhub.domain.rank.domain.type.SchoolDateType; +import com.walkhub.walkhub.domain.rank.presentation.dto.response.SchoolListResponse; +import com.walkhub.walkhub.domain.rank.presentation.dto.response.SchoolListResponse.SchoolResponse; +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class SchoolSearchService { + + private final SchoolRankRepository schoolRankRepository; + + public SchoolListResponse execute(String name, SchoolDateType dateType) { + List schoolResponseList = schoolRankRepository + .findAllByDateTypeAndNameContainingAndCreatedAtBetweenOrderByRankingAsc(dateType.toString(), name, + LocalDate + .now().minusWeeks(1), LocalDate.now()) + .stream() + .map(schoolRank -> SchoolResponse.builder() + .schoolId(schoolRank.getSchoolId()) + .logoImageUrl(schoolRank.getLogoImageUrl()) + .schoolName(schoolRank.getName()) + .ranking(schoolRank.getRanking()) + .walkCount(schoolRank.getWalkCount()) + .build()) + .collect(Collectors.toList()); + + return new SchoolListResponse(schoolResponseList); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/rank/service/UserSearchService.java b/src/main/java/com/walkhub/walkhub/domain/rank/service/UserSearchService.java index 4cde0729b..4e03a47b9 100644 --- a/src/main/java/com/walkhub/walkhub/domain/rank/service/UserSearchService.java +++ b/src/main/java/com/walkhub/walkhub/domain/rank/service/UserSearchService.java @@ -5,6 +5,7 @@ import com.walkhub.walkhub.domain.rank.domain.UserRank; import com.walkhub.walkhub.domain.rank.domain.repository.UserRankRepository; import com.walkhub.walkhub.domain.rank.presentation.dto.response.UserListResponse; +import com.walkhub.walkhub.domain.rank.presentation.dto.response.UserListResponse.UserSearchResponse; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.domain.repository.UserRepository; import com.walkhub.walkhub.global.enums.DateType; @@ -25,7 +26,7 @@ public class UserSearchService { @Transactional(readOnly = true) public UserListResponse execute(Long schoolId, String name, DateType dateType) { - List result; + List result; if (DateType.DAY.equals(dateType)) { result = userRepository.findAllBySchoolIdAndNameContaining(schoolId, name) @@ -42,25 +43,28 @@ public UserListResponse execute(Long schoolId, String name, DateType dateType) { return new UserListResponse(result); } - private UserListResponse.UserSearchResponse buildDayUserSearchResponse(User user) { - ExerciseAnalysisDto exerciseAnalysisDto = exerciseAnalysisCacheRepository.getUserTodayRank(user.getId()); + private UserSearchResponse buildDayUserSearchResponse(User user) { + ExerciseAnalysisDto exerciseAnalysisDto = + exerciseAnalysisCacheRepository.getUserTodayRank(user.getSchool().getId(), user.getId()); - return UserListResponse.UserSearchResponse.builder() + ExerciseAnalysisDto exerciseAnalysis = exerciseAnalysisDto == null ? ExerciseAnalysisDto.builder().build() : exerciseAnalysisDto; + + return UserSearchResponse.builder() .userId(user.getId()) .name(user.getName()) - .rank(exerciseAnalysisDto.getRanking()) + .ranking(exerciseAnalysis.getRanking()) .grade(user.getSection().getGrade()) .classNum(user.getSection().getClassNum()) .profileImageUrl(user.getProfileImageUrl()) - .walkCount(exerciseAnalysisDto.getWalkCount()) + .walkCount(exerciseAnalysis.getWalkCount()) .build(); } - private UserListResponse.UserSearchResponse buildWeekOrMonthUserSearchResponse(UserRank userRank) { - return UserListResponse.UserSearchResponse.builder() + private UserSearchResponse buildWeekOrMonthUserSearchResponse(UserRank userRank) { + return UserSearchResponse.builder() .userId(userRank.getUserId()) .name(userRank.getName()) - .rank(userRank.getRanking()) + .ranking(userRank.getRanking()) .grade(userRank.getGrade()) .classNum(userRank.getClassNum()) .profileImageUrl(userRank.getProfileImageUrl()) diff --git a/src/main/java/com/walkhub/walkhub/domain/school/domain/School.java b/src/main/java/com/walkhub/walkhub/domain/school/domain/School.java index e2b23ff0e..127371e83 100644 --- a/src/main/java/com/walkhub/walkhub/domain/school/domain/School.java +++ b/src/main/java/com/walkhub/walkhub/domain/school/domain/School.java @@ -1,5 +1,6 @@ package com.walkhub.walkhub.domain.school.domain; +import com.walkhub.walkhub.domain.user.domain.Section; import com.walkhub.walkhub.global.entity.BaseTimeEntity; import com.walkhub.walkhub.infrastructure.image.DefaultImage; import lombok.AccessLevel; @@ -13,6 +14,8 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.OneToMany; +import java.util.List; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -36,12 +39,23 @@ public class School extends BaseTimeEntity { @Column(columnDefinition = "char(7)") private String authCode; + @OneToMany(mappedBy = "school") + private List
sections; + @Builder public School(String name, String logoImageUrl) { this.name = name; this.logoImageUrl = logoImageUrl; } + public void addUserCount() { + this.userCount++; + } + + public void minusUserCount() { + this.userCount--; + } + public void setLogoImage(String logoImageUrl) { this.logoImageUrl = logoImageUrl; } diff --git a/src/main/java/com/walkhub/walkhub/domain/school/domain/repository/SchoolRepository.java b/src/main/java/com/walkhub/walkhub/domain/school/domain/repository/SchoolRepository.java index 8a55b5f7c..a6f64780d 100644 --- a/src/main/java/com/walkhub/walkhub/domain/school/domain/repository/SchoolRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/school/domain/repository/SchoolRepository.java @@ -6,8 +6,10 @@ import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; +import java.util.Optional; public interface SchoolRepository extends JpaRepository { List findAllByNameContaining(String name); Page findAllBy(Pageable pageable); + Optional findByAuthCode(String authCode); } diff --git a/src/main/java/com/walkhub/walkhub/domain/school/facade/SchoolFacade.java b/src/main/java/com/walkhub/walkhub/domain/school/facade/SchoolFacade.java index 405cd6f78..4edeedb69 100644 --- a/src/main/java/com/walkhub/walkhub/domain/school/facade/SchoolFacade.java +++ b/src/main/java/com/walkhub/walkhub/domain/school/facade/SchoolFacade.java @@ -16,4 +16,9 @@ public School getSchoolById(Long schoolId) { return schoolRepository.findById(schoolId) .orElseThrow(() -> SchoolNotFoundException.EXCEPTION); } + + public School getSchoolByAuthCode(String authCode) { + return schoolRepository.findByAuthCode(authCode) + .orElseThrow(() -> SchoolNotFoundException.EXCEPTION); + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/school/presentation/SchoolController.java b/src/main/java/com/walkhub/walkhub/domain/school/presentation/SchoolController.java index f2cf4a3ec..2178a4a96 100644 --- a/src/main/java/com/walkhub/walkhub/domain/school/presentation/SchoolController.java +++ b/src/main/java/com/walkhub/walkhub/domain/school/presentation/SchoolController.java @@ -1,7 +1,9 @@ package com.walkhub.walkhub.domain.school.presentation; import com.walkhub.walkhub.domain.school.presentation.dto.request.SchoolLogoRequest; +import com.walkhub.walkhub.domain.school.presentation.dto.response.SchoolDetailsInfoResponse; import com.walkhub.walkhub.domain.school.presentation.dto.response.SearchSchoolListResponse; +import com.walkhub.walkhub.domain.school.service.SchoolDetailsInfoService; import com.walkhub.walkhub.domain.school.service.SchoolLogoSettingService; import com.walkhub.walkhub.domain.school.service.SearchSchoolService; import lombok.RequiredArgsConstructor; @@ -24,16 +26,21 @@ public class SchoolController { private final SchoolLogoSettingService schoolLogoSettingService; private final SearchSchoolService searchSchoolService; + private final SchoolDetailsInfoService schoolDetailsInfoService; @ResponseStatus(HttpStatus.NO_CONTENT) - @PatchMapping("/logos/{school-id}") - public void schoolLogoSetting(@PathVariable(name = "school-id") Long schoolId, - @RequestBody @Valid SchoolLogoRequest request) { - schoolLogoSettingService.execute(schoolId, request); + @PatchMapping("/logos") + public void schoolLogoSetting(@RequestBody @Valid SchoolLogoRequest request) { + schoolLogoSettingService.execute(request); } @GetMapping("/search") public SearchSchoolListResponse searchSchool(@RequestParam String name) { return searchSchoolService.execute(name); } + + @GetMapping("/details/{school-id}") + public SchoolDetailsInfoResponse schoolDetailsInfo(@PathVariable("school-id") Long schoolId) { + return schoolDetailsInfoService.execute(schoolId); + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/school/presentation/dto/response/SchoolDetailsInfoResponse.java b/src/main/java/com/walkhub/walkhub/domain/school/presentation/dto/response/SchoolDetailsInfoResponse.java new file mode 100644 index 000000000..7b4c37196 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/school/presentation/dto/response/SchoolDetailsInfoResponse.java @@ -0,0 +1,16 @@ +package com.walkhub.walkhub.domain.school.presentation.dto.response; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class SchoolDetailsInfoResponse { + + private final Long userCount; + private final Integer weekTotalWalkCount; + private final Integer monthTotalWalkCount; + private final Integer weekRanking; + private final Integer monthRanking; + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/school/service/SchoolDetailsInfoService.java b/src/main/java/com/walkhub/walkhub/domain/school/service/SchoolDetailsInfoService.java new file mode 100644 index 000000000..fbcc40032 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/school/service/SchoolDetailsInfoService.java @@ -0,0 +1,48 @@ +package com.walkhub.walkhub.domain.school.service; + +import com.walkhub.walkhub.domain.rank.domain.SchoolRank; +import com.walkhub.walkhub.domain.rank.domain.repository.SchoolRankRepository; +import com.walkhub.walkhub.domain.school.domain.School; +import com.walkhub.walkhub.domain.school.facade.SchoolFacade; +import com.walkhub.walkhub.domain.school.presentation.dto.response.SchoolDetailsInfoResponse; +import com.walkhub.walkhub.global.enums.DateType; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; + +@RequiredArgsConstructor +@Service +public class SchoolDetailsInfoService { + + private final SchoolRankRepository schoolRankRepository; + private final SchoolFacade schoolFacade; + + @Transactional(readOnly = true) + public SchoolDetailsInfoResponse execute(Long schoolId) { + School school = schoolFacade.getSchoolById(schoolId); + LocalDate now = LocalDate.now(); + LocalDate createAt = now.minusWeeks(1); + + return schoolRankRepository + .findBySchoolIdAndDateTypeAndCreatedAtBetween(schoolId, DateType.WEEK.toString(), createAt, now) + .flatMap( + weekSchoolRanks -> schoolRankRepository + .findBySchoolIdAndDateTypeAndCreatedAtBetween(schoolId, DateType.MONTH.toString(), createAt, now) + .map(monthSchoolRanks -> schoolDetailsInfoResponseBuilder(school, weekSchoolRanks, monthSchoolRanks)) + ) + .orElse(null); + } + + private SchoolDetailsInfoResponse schoolDetailsInfoResponseBuilder(School school, SchoolRank weekSchoolRanks, SchoolRank monthSchoolRanks) { + return SchoolDetailsInfoResponse.builder() + .userCount(school.getUserCount()) + .weekTotalWalkCount(weekSchoolRanks.getWalkCount()) + .monthTotalWalkCount(monthSchoolRanks.getWalkCount()) + .weekRanking(weekSchoolRanks.getRanking()) + .monthRanking(monthSchoolRanks.getRanking()) + .build(); + } + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/school/service/SchoolLogoSettingService.java b/src/main/java/com/walkhub/walkhub/domain/school/service/SchoolLogoSettingService.java index 81aa00a67..b6ba86d6d 100644 --- a/src/main/java/com/walkhub/walkhub/domain/school/service/SchoolLogoSettingService.java +++ b/src/main/java/com/walkhub/walkhub/domain/school/service/SchoolLogoSettingService.java @@ -1,11 +1,7 @@ package com.walkhub.walkhub.domain.school.service; -import com.walkhub.walkhub.domain.school.domain.School; -import com.walkhub.walkhub.domain.school.domain.repository.SchoolRepository; -import com.walkhub.walkhub.domain.school.exception.AgencyCodeNotMatchException; import com.walkhub.walkhub.domain.school.presentation.dto.request.SchoolLogoRequest; import com.walkhub.walkhub.domain.user.domain.User; -import com.walkhub.walkhub.domain.user.exception.SchoolNotFoundException; import com.walkhub.walkhub.domain.user.facade.UserFacade; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -16,19 +12,11 @@ public class SchoolLogoSettingService { private final UserFacade userFacade; - private final SchoolRepository schoolRepository; @Transactional - public void execute(Long schoolId, SchoolLogoRequest request) { + public void execute(SchoolLogoRequest request) { User user = userFacade.getCurrentUser(); - School school = schoolRepository.findById(schoolId) - .orElseThrow(() -> SchoolNotFoundException.EXCEPTION); - - if (!user.getSchool().equals(school)) { - throw AgencyCodeNotMatchException.EXCEPTION; - } - - school.setLogoImage(request.getImageUrl()); + user.getSchool().setLogoImage(request.getImageUrl()); } } diff --git a/src/main/java/com/walkhub/walkhub/domain/su/exception/SchoolRootUserNotFoundException.java b/src/main/java/com/walkhub/walkhub/domain/su/exception/SchoolRootUserNotFoundException.java new file mode 100644 index 000000000..0e979bdd8 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/su/exception/SchoolRootUserNotFoundException.java @@ -0,0 +1,14 @@ +package com.walkhub.walkhub.domain.su.exception; + +import com.walkhub.walkhub.global.error.exception.ErrorCode; +import com.walkhub.walkhub.global.error.exception.WalkhubException; + +public class SchoolRootUserNotFoundException extends WalkhubException { + + public static final WalkhubException EXCEPTION = + new SchoolRootUserNotFoundException(); + + private SchoolRootUserNotFoundException() { + super(ErrorCode.ROOT_USER_NOT_FOUND); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/su/presentation/SuController.java b/src/main/java/com/walkhub/walkhub/domain/su/presentation/SuController.java index 35c1931da..90ecec7cb 100644 --- a/src/main/java/com/walkhub/walkhub/domain/su/presentation/SuController.java +++ b/src/main/java/com/walkhub/walkhub/domain/su/presentation/SuController.java @@ -2,17 +2,14 @@ import com.walkhub.walkhub.domain.su.presentation.dto.response.CreateRootAccountResponse; import com.walkhub.walkhub.domain.su.presentation.dto.response.SchoolListResponse; +import com.walkhub.walkhub.domain.su.presentation.dto.response.UpdateSchoolPasswordResponse; import com.walkhub.walkhub.domain.su.service.CreateRootAccountService; import com.walkhub.walkhub.domain.su.service.ShowSchoolService; +import com.walkhub.walkhub.domain.su.service.UpdateSchoolPasswordService; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RequiredArgsConstructor @@ -22,6 +19,7 @@ public class SuController { private final ShowSchoolService showSchoolService; private final CreateRootAccountService createRootAccountService; + private final UpdateSchoolPasswordService updateSchoolPasswordService; @GetMapping public SchoolListResponse showSchool(Pageable page) { @@ -33,4 +31,9 @@ public SchoolListResponse showSchool(Pageable page) { public CreateRootAccountResponse rootAccount(@PathVariable("school-id") Long schoolId) { return createRootAccountService.execute(schoolId); } + + @PatchMapping("/accounts/{school-id}") + public UpdateSchoolPasswordResponse updateRootPassword(@PathVariable("school-id") Long schoolId) { + return updateSchoolPasswordService.execute(schoolId); + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/su/presentation/dto/response/UpdateSchoolPasswordResponse.java b/src/main/java/com/walkhub/walkhub/domain/su/presentation/dto/response/UpdateSchoolPasswordResponse.java new file mode 100644 index 000000000..3266e3e5c --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/su/presentation/dto/response/UpdateSchoolPasswordResponse.java @@ -0,0 +1,10 @@ +package com.walkhub.walkhub.domain.su.presentation.dto.response; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class UpdateSchoolPasswordResponse { + private final String password; +} diff --git a/src/main/java/com/walkhub/walkhub/domain/su/service/UpdateSchoolPasswordService.java b/src/main/java/com/walkhub/walkhub/domain/su/service/UpdateSchoolPasswordService.java new file mode 100644 index 000000000..6744c86e0 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/su/service/UpdateSchoolPasswordService.java @@ -0,0 +1,33 @@ +package com.walkhub.walkhub.domain.su.service; + +import com.walkhub.walkhub.domain.school.domain.School; +import com.walkhub.walkhub.domain.school.facade.SchoolFacade; +import com.walkhub.walkhub.domain.su.exception.SchoolRootUserNotFoundException; +import com.walkhub.walkhub.domain.su.presentation.dto.response.UpdateSchoolPasswordResponse; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.domain.user.domain.repository.UserRepository; +import com.walkhub.walkhub.global.enums.Authority; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Service +public class UpdateSchoolPasswordService { + private final UserRepository userRepository; + private final SchoolFacade schoolFacade; + + @Transactional + public UpdateSchoolPasswordResponse execute(Long schoolId) { + School school = schoolFacade.getSchoolById(schoolId); + + User user = userRepository.findBySchoolAndAuthority(school, Authority.ROOT) + .orElseThrow(() -> SchoolRootUserNotFoundException.EXCEPTION); + + String updatedPassword = user.updateRootUserPassword(); + + return UpdateSchoolPasswordResponse.builder() + .password(updatedPassword) + .build(); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/TeacherController.java b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/TeacherController.java index 3e4d89e8f..3ae199a27 100644 --- a/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/TeacherController.java +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/TeacherController.java @@ -1,21 +1,26 @@ package com.walkhub.walkhub.domain.teacher.presentation; -import com.walkhub.walkhub.domain.teacher.presentation.dto.request.CreateClassRequest; -import com.walkhub.walkhub.domain.teacher.presentation.dto.request.TeacherCodeRequest; +import com.walkhub.walkhub.domain.teacher.presentation.dto.request.*; +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.ClassListResponse; import com.walkhub.walkhub.domain.teacher.presentation.dto.response.CodeResponse; +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.QueryUserListResponse; +import com.walkhub.walkhub.domain.teacher.service.*; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; import com.walkhub.walkhub.domain.teacher.presentation.dto.response.DetailsClassResponse; +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.TokenResponse; import com.walkhub.walkhub.domain.teacher.presentation.dto.response.QueryUserDetailsResponse; +import com.walkhub.walkhub.domain.teacher.service.ClassListService; import com.walkhub.walkhub.domain.teacher.service.ConfirmTeacherCodeService; import com.walkhub.walkhub.domain.teacher.service.CreateClassService; import com.walkhub.walkhub.domain.teacher.service.DeleteClassService; -import com.walkhub.walkhub.domain.teacher.service.QueryStudentCodeService; +import com.walkhub.walkhub.domain.teacher.service.DetailsClassService; import com.walkhub.walkhub.domain.teacher.service.QueryUserDetailsService; -import com.walkhub.walkhub.domain.teacher.service.RefreshClassCodeService; import com.walkhub.walkhub.domain.teacher.service.VerificationCodeService; + import java.time.LocalDate; -import lombok.RequiredArgsConstructor; + import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -37,10 +42,13 @@ public class TeacherController { private final CreateClassService createClassService; private final VerificationCodeService verificationCodeService; private final DeleteClassService deleteClassService; - private final RefreshClassCodeService refreshClassCodeService; - private final QueryStudentCodeService queryStudentCodeService; + private final QueryUserListService queryUserListService; + private final DetailsClassService detailsClassService; private final QueryUserDetailsService queryUserDetailsService; private final ConfirmTeacherCodeService confirmTeacherCodeService; + private final ClassListService classListService; + private final UpdateTeacherSchoolService updateTeacherSchoolService; + private final UserSearchForTeacherService userSearchForTeacherService; @ResponseStatus(HttpStatus.CREATED) @PostMapping("/classes") @@ -60,27 +68,41 @@ public void deleteClass(@PathVariable(name = "section-id") Long sectionId) { deleteClassService.execute(sectionId); } - @PatchMapping("/classes/verification-codes") - public CodeResponse refreshClassCode() { - return refreshClassCodeService.execute(); + @GetMapping("/users") + public QueryUserListResponse queryUserList(QueryUserListRequest request) { + return queryUserListService.execute(request); } - @GetMapping("/classes") - public DetailsClassResponse queryStudentCode() { - return queryStudentCodeService.execute(); + @GetMapping("/classes/{section-id}") + public DetailsClassResponse detailsClass(@PathVariable("section-id") Long sectionId) { + return detailsClassService.execute(sectionId); } @GetMapping("/users/{user-id}") - public QueryUserDetailsResponse queryUserDetails(@PathVariable Long userId, - @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startAt, - @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endAt) { + public QueryUserDetailsResponse queryUserDetails(@PathVariable("user-id") Long userId, + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startAt, + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endAt) { return queryUserDetailsService.execute(userId, startAt, endAt); } - @ResponseStatus(HttpStatus.NO_CONTENT) @PatchMapping("/verification-codes") - public void confirmTeacherCode(@RequestBody TeacherCodeRequest teacherCodeRequest) { - confirmTeacherCodeService.execute(teacherCodeRequest); + public TokenResponse confirmTeacherCode(@RequestBody TeacherCodeRequest teacherCodeRequest) { + return confirmTeacherCodeService.execute(teacherCodeRequest); } + @GetMapping("/classes/lists") + public ClassListResponse classList() { + return classListService.execute(); + } + + @PatchMapping("/schools") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void updateTeacherSchool(@Valid @RequestBody UpdateTeacherSchoolRequest request) { + updateTeacherSchoolService.execute(request); + } + + @GetMapping("/users/search") + public QueryUserListResponse searchUser(UserSearchRequest request) { + return userSearchForTeacherService.execute(request); + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/request/QueryUserListRequest.java b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/request/QueryUserListRequest.java new file mode 100644 index 000000000..072f9cca5 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/request/QueryUserListRequest.java @@ -0,0 +1,14 @@ +package com.walkhub.walkhub.domain.teacher.presentation.dto.request; + +import com.walkhub.walkhub.domain.teacher.type.AuthorityScope; +import com.walkhub.walkhub.domain.teacher.type.SortStandard; +import lombok.Data; + +@Data +public class QueryUserListRequest { + private Integer page; + private AuthorityScope scope; + private SortStandard sort; + private Integer grade; + private Integer classNum; +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/request/UpdateTeacherSchoolRequest.java b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/request/UpdateTeacherSchoolRequest.java new file mode 100644 index 000000000..de64659b6 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/request/UpdateTeacherSchoolRequest.java @@ -0,0 +1,13 @@ +package com.walkhub.walkhub.domain.teacher.presentation.dto.request; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Getter +@NoArgsConstructor +public class UpdateTeacherSchoolRequest { + @NotBlank(message = "auth_code는 Null, 공백, 띄어쓰기를 허용하지 않습니다.") + private String authCode; +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/request/UserSearchRequest.java b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/request/UserSearchRequest.java new file mode 100644 index 000000000..62dc6269f --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/request/UserSearchRequest.java @@ -0,0 +1,16 @@ +package com.walkhub.walkhub.domain.teacher.presentation.dto.request; + +import com.walkhub.walkhub.domain.teacher.type.AuthorityScope; +import com.walkhub.walkhub.domain.teacher.type.SortStandard; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class UserSearchRequest { + private AuthorityScope scope; + private SortStandard sort; + private Integer grade; + private Integer classNum; + private String name; +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/ClassListResponse.java b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/ClassListResponse.java new file mode 100644 index 000000000..c9760433e --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/ClassListResponse.java @@ -0,0 +1,39 @@ +package com.walkhub.walkhub.domain.teacher.presentation.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class ClassListResponse { + + private final String authCode; + private final List classList; + + @Getter + @Builder + public static class ClassResponse { + private final SectionResponse section; + private final TeacherResponse teacher; + } + + @Getter + @Builder + public static class SectionResponse { + private final Long sectionId; + private final Integer grade; + private final Integer classNum; + } + + @Getter + @Builder + public static class TeacherResponse { + private final Long userId; + private final String name; + private final String profileImageUrl; + } + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/DetailsClassResponse.java b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/DetailsClassResponse.java index c0a6bd6e1..f43dc5106 100644 --- a/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/DetailsClassResponse.java +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/DetailsClassResponse.java @@ -1,6 +1,5 @@ package com.walkhub.walkhub.domain.teacher.presentation.dto.response; -import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -8,19 +7,31 @@ @Getter -@AllArgsConstructor +@Builder public class DetailsClassResponse { private final String classCode; + private final TeacherResponse teacher; private final List userList; + @Getter + @Builder + public static class TeacherResponse { + private final Long userId; + private final String name; + private final String profileImageUrl; + } @Getter @Builder public static class UserListResponse { - private Long userId; - private String name; - private String profileImageUrl; - private Integer walkCount; + private final Long userId; + private final String name; + private final Integer number; + private final String profileImageUrl; + private final Integer averageWalkCount; + private final Integer totalWalkCount; + private final Integer averageDistance; + private final Integer totalDistance; } } diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/QueryUserDetailsResponse.java b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/QueryUserDetailsResponse.java index 5cada11fc..8ccdd23ca 100644 --- a/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/QueryUserDetailsResponse.java +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/QueryUserDetailsResponse.java @@ -1,29 +1,32 @@ package com.walkhub.walkhub.domain.teacher.presentation.dto.response; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.List; +import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Builder; import lombok.Getter; +import java.math.BigDecimal; +import java.time.ZonedDateTime; +import java.util.List; + @Getter @Builder public class QueryUserDetailsResponse { - private final Long userId; - private final String name; - private final String profileImageUrl; - private final Integer grade; - private final Integer classNum; - private final Integer number; - private final List walkCountList; - private final List exerciseList; + private final Long userId; + private final String name; + private final String profileImageUrl; + private final Integer grade; + private final Integer classNum; + private final Integer number; + private final List walkCountList; + private final List exerciseList; - @Getter - @Builder - public static class ExerciseResponse { - private final String imageUrl; - private final LocalDateTime startAt; - private final BigDecimal latitude; - private final BigDecimal longitude; - } + @Getter + @Builder + public static class ExerciseResponse { + private final String imageUrl; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") + private final ZonedDateTime startAt; + private final BigDecimal latitude; + private final BigDecimal longitude; + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/QueryUserListResponse.java b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/QueryUserListResponse.java new file mode 100644 index 000000000..bb7077edb --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/QueryUserListResponse.java @@ -0,0 +1,29 @@ +package com.walkhub.walkhub.domain.teacher.presentation.dto.response; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class QueryUserListResponse { + + private final List userList; + + @Getter + @Builder + public static class UserListInfo { + private final Long userId; + private final String name; + private final String profileImageUrl; + private final Integer grade; + private final Integer classNum; + private final Integer number; + private final Double averageWalkCount; + private final Integer totalWalkCount; + private final Double averageDistance; + private final Integer totalDistance; + private final Boolean isTeacher; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/TokenResponse.java b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/TokenResponse.java new file mode 100644 index 000000000..255c7d884 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/presentation/dto/response/TokenResponse.java @@ -0,0 +1,13 @@ +package com.walkhub.walkhub.domain.teacher.presentation.dto.response; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class TokenResponse { + + private final String accessToken; + private final String refreshToken; + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/service/ClassListService.java b/src/main/java/com/walkhub/walkhub/domain/teacher/service/ClassListService.java new file mode 100644 index 000000000..e9309526d --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/service/ClassListService.java @@ -0,0 +1,64 @@ +package com.walkhub.walkhub.domain.teacher.service; + +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.ClassListResponse; +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.ClassListResponse.ClassResponse; +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.ClassListResponse.SectionResponse; +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.ClassListResponse.TeacherResponse; +import com.walkhub.walkhub.domain.user.domain.Section; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.domain.user.domain.repository.UserRepository; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import com.walkhub.walkhub.global.enums.Authority; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class ClassListService { + + private final UserFacade userFacade; + private final UserRepository userRepository; + + @Transactional(readOnly = true) + public ClassListResponse execute() { + User user = userFacade.getCurrentUser(); + List teachers = userRepository.findAllBySchoolAndAuthority(user.getSchool(), Authority.TEACHER); + + List classList = teachers.stream() + .map(this::buildClassResponse) + .collect(Collectors.toList()); + + return new ClassListResponse(user.getSchool().getAuthCode(), classList); + } + + private ClassResponse buildClassResponse(User teacher) { + Section section; + + if (teacher.hasSection()) { + section = teacher.getSection(); + } else { + section = Section.builder().build(); + } + + return ClassResponse.builder() + .section(SectionResponse.builder() + .sectionId(section.getId()) + .grade(section.getGrade()) + .classNum(section.getClassNum()) + .build()) + .teacher(TeacherResponse.builder() + .userId(teacher.getId()) + .name(teacher.getName()) + .profileImageUrl(teacher.getProfileImageUrl()) + .build()) + .build(); + + } +} + + + diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/service/ConfirmTeacherCodeService.java b/src/main/java/com/walkhub/walkhub/domain/teacher/service/ConfirmTeacherCodeService.java index 4548dc267..cc42ea00c 100644 --- a/src/main/java/com/walkhub/walkhub/domain/teacher/service/ConfirmTeacherCodeService.java +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/service/ConfirmTeacherCodeService.java @@ -1,9 +1,11 @@ package com.walkhub.walkhub.domain.teacher.service; import com.walkhub.walkhub.domain.teacher.presentation.dto.request.TeacherCodeRequest; +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.TokenResponse; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.facade.UserFacade; -import com.walkhub.walkhub.global.exception.InvalidVerificationCodeException; +import com.walkhub.walkhub.global.exception.VerificationCodeNotFoundException; +import com.walkhub.walkhub.global.security.jwt.JwtTokenProvider; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -12,15 +14,25 @@ @Service public class ConfirmTeacherCodeService { - private final UserFacade userFacade; + private final UserFacade userFacade; + private final JwtTokenProvider jwtTokenProvider; - @Transactional - public void execute(TeacherCodeRequest request) { - User user = userFacade.getCurrentUser(); + @Transactional + public TokenResponse execute(TeacherCodeRequest request) { + User user = userFacade.getCurrentUser(); - if (!user.getSchool().getAuthCode().equals(request.getCode())) { - throw InvalidVerificationCodeException.EXCEPTION; - } - user.setAuthorityTeacher(); - } + if (!user.getSchool().getAuthCode().equals(request.getCode())) { + throw VerificationCodeNotFoundException.EXCEPTION; + } + user.setAuthorityTeacher(); + user.setSectionNull(); + + String accessToken = jwtTokenProvider.generateAccessToken(user.getAccountId()); + String refreshToken = jwtTokenProvider.generateRefreshToken(user.getAccountId()); + + return TokenResponse.builder() + .accessToken(accessToken) + .refreshToken(refreshToken) + .build(); + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/service/CreateClassService.java b/src/main/java/com/walkhub/walkhub/domain/teacher/service/CreateClassService.java index 8c3d49b66..10ab872d8 100644 --- a/src/main/java/com/walkhub/walkhub/domain/teacher/service/CreateClassService.java +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/service/CreateClassService.java @@ -10,7 +10,6 @@ import com.walkhub.walkhub.domain.user.facade.UserFacade; import com.walkhub.walkhub.global.utils.code.RandomCodeUtil; import lombok.RequiredArgsConstructor; -import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,13 +34,13 @@ public CodeResponse execute(CreateClassRequest request) { .classCode(classCode) .build(); - try { - sectionRepository.save(section); - user.setSection(section); - } catch (DataIntegrityViolationException e) { + if (user.hasSection() || sectionRepository.findBySchoolAndGradeAndClassNum(userSchool, grade, classNum).isPresent()) { throw AlreadyCreatedException.EXCEPTION; } + Section savedSection = sectionRepository.save(section); + user.setSection(savedSection); + return new CodeResponse(classCode); } diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/service/DeleteClassService.java b/src/main/java/com/walkhub/walkhub/domain/teacher/service/DeleteClassService.java index 83b49831d..ff967cf23 100644 --- a/src/main/java/com/walkhub/walkhub/domain/teacher/service/DeleteClassService.java +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/service/DeleteClassService.java @@ -3,6 +3,7 @@ import com.walkhub.walkhub.domain.user.domain.Section; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.domain.repository.SectionRepository; +import com.walkhub.walkhub.domain.user.domain.repository.UserRepository; import com.walkhub.walkhub.domain.user.facade.SectionFacade; import com.walkhub.walkhub.domain.user.facade.UserFacade; import com.walkhub.walkhub.global.enums.Authority; @@ -18,6 +19,7 @@ public class DeleteClassService { private final UserFacade userFacade; private final SectionFacade sectionFacade; private final SectionRepository sectionRepository; + private final UserRepository userRepository; @Transactional public void execute(Long sectionId) { @@ -25,10 +27,11 @@ public void execute(Long sectionId) { User user = userFacade.getCurrentUser(); - if (user.getAuthority() == Authority.TEACHER && !user.getSection().equals(section)) { + if (user.getAuthority() != Authority.TEACHER || !user.getSection().equals(section)) { throw InvalidRoleException.EXCEPTION; } + userRepository.setUserSectionNull(section); sectionRepository.delete(section); } diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/service/DetailsClassService.java b/src/main/java/com/walkhub/walkhub/domain/teacher/service/DetailsClassService.java new file mode 100644 index 000000000..11d511620 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/service/DetailsClassService.java @@ -0,0 +1,72 @@ +package com.walkhub.walkhub.domain.teacher.service; + +import com.walkhub.walkhub.domain.exercise.domain.ExerciseAnalysis; +import com.walkhub.walkhub.domain.exercise.domain.repository.ExerciseAnalysisRepository; +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.DetailsClassResponse; +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.DetailsClassResponse.TeacherResponse; +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.DetailsClassResponse.UserListResponse; +import com.walkhub.walkhub.domain.user.domain.Section; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.domain.user.exception.UserNotFoundException; +import com.walkhub.walkhub.domain.user.facade.SectionFacade; +import com.walkhub.walkhub.global.enums.Authority; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class DetailsClassService { + + private final SectionFacade sectionFacade; + private final ExerciseAnalysisRepository exerciseAnalysisRepository; + + @Transactional(readOnly = true) + public DetailsClassResponse execute(Long sectionId) { + Section section = sectionFacade.getSectionById(sectionId); + + User teacher = section.getUsers().stream() + .filter(user -> user.getAuthority() == Authority.TEACHER) + .findFirst() + .orElseThrow(() -> UserNotFoundException.EXCEPTION); + + TeacherResponse teacherResponse = TeacherResponse.builder() + .userId(teacher.getId()) + .name(teacher.getName()) + .profileImageUrl(teacher.getProfileImageUrl()) + .build(); + + List userListResponses = section.getUsers() + .stream() + .filter(user -> user.getAuthority() == Authority.USER) + .map(this::buildUserListResponse) + .collect(Collectors.toList()); + + return DetailsClassResponse.builder() + .classCode(section.getClassCode()) + .teacher(teacherResponse) + .userList(userListResponses) + .build(); + } + + private UserListResponse buildUserListResponse(User user) { + LocalDate startAt = LocalDate.now().minusDays(7); + LocalDate endAt = LocalDate.now(); + List exerciseAnalyses = exerciseAnalysisRepository.findAllByUserAndDateBetween(user, startAt, endAt); + + return UserListResponse.builder() + .userId(user.getId()) + .name(user.getName()) + .profileImageUrl(user.getProfileImageUrl()) + .number(user.getNumber()) + .averageWalkCount((int) exerciseAnalyses.stream().mapToInt(ExerciseAnalysis::getWalkCount).average().orElse(0)) + .totalWalkCount(exerciseAnalyses.stream().mapToInt(ExerciseAnalysis::getWalkCount).sum()) + .averageDistance((int) exerciseAnalyses.stream().mapToInt(ExerciseAnalysis::getDistance).average().orElse(0)) + .totalDistance(exerciseAnalyses.stream().mapToInt(ExerciseAnalysis::getDistance).sum()) + .build(); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/service/QueryStudentCodeService.java b/src/main/java/com/walkhub/walkhub/domain/teacher/service/QueryStudentCodeService.java deleted file mode 100644 index 5b939ee82..000000000 --- a/src/main/java/com/walkhub/walkhub/domain/teacher/service/QueryStudentCodeService.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.walkhub.walkhub.domain.teacher.service; - -import com.walkhub.walkhub.domain.exercise.domain.ExerciseAnalysis; -import com.walkhub.walkhub.domain.exercise.domain.repository.ExerciseAnalysisRepository; -import com.walkhub.walkhub.domain.teacher.presentation.dto.response.DetailsClassResponse; -import com.walkhub.walkhub.domain.user.domain.Section; -import com.walkhub.walkhub.domain.user.domain.User; -import com.walkhub.walkhub.domain.user.domain.repository.UserRepository; -import com.walkhub.walkhub.domain.user.facade.SectionFacade; -import com.walkhub.walkhub.domain.user.facade.UserFacade; -import com.walkhub.walkhub.global.enums.Authority; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDate; -import java.util.List; -import java.util.stream.Collectors; - -@RequiredArgsConstructor -@Service -public class QueryStudentCodeService { - - private final UserFacade userFacade; - private final SectionFacade sectionFacade; - private final UserRepository userRepository; - private final ExerciseAnalysisRepository exerciseAnalysisRepository; - - @Transactional(readOnly = true) - public DetailsClassResponse execute() { - User teacher = userFacade.getCurrentUser(); - Section section = sectionFacade.getSectionById(teacher.getSection().getId()); - - List result = - userRepository.findAllBySectionAndAuthority(section, Authority.STUDENT) - .stream() - .map(this::buildUserListResponse) - .collect(Collectors.toList()); - - return new DetailsClassResponse(section.getClassCode(), result); - } - - private DetailsClassResponse.UserListResponse buildUserListResponse(User user) { - Integer walkCount = exerciseAnalysisRepository.findByUserAndDate(user, LocalDate.now()) - .map(ExerciseAnalysis::getWalkCount) - .orElse(0); - return DetailsClassResponse.UserListResponse.builder() - .userId(user.getId()) - .name(user.getName()) - .profileImageUrl(user.getProfileImageUrl()) - .walkCount(walkCount) - .build(); - } -} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/service/QueryUserDetailsService.java b/src/main/java/com/walkhub/walkhub/domain/teacher/service/QueryUserDetailsService.java index 73d5dbaaa..96cedcdff 100644 --- a/src/main/java/com/walkhub/walkhub/domain/teacher/service/QueryUserDetailsService.java +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/service/QueryUserDetailsService.java @@ -7,50 +7,51 @@ import com.walkhub.walkhub.domain.teacher.presentation.dto.response.QueryUserDetailsResponse.ExerciseResponse; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.facade.UserFacade; -import java.time.LocalDate; -import java.util.List; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; + @RequiredArgsConstructor @Service public class QueryUserDetailsService { - private final UserFacade userFacade; - private final LocationRepository locationRepository; - private final ExerciseAnalysisRepository exerciseAnalysisRepository; - - @Transactional(readOnly = true) - public QueryUserDetailsResponse execute(Long userId, LocalDate startAt, LocalDate endAt) { - User user = userFacade.getUserById(userId); - - List exerciseList = user.getExerciseList() - .stream() - .map(locationRepository::findTop1ByExerciseOrderBySequenceDesc) - .map(location -> ExerciseResponse.builder() - .imageUrl(location.getExercise().getImageUrl()) - .startAt(location.getExercise().getCreatedAt()) - .latitude(location.getLatitude()) - .longitude(location.getLongitude()) - .build()) - .collect(Collectors.toList()); - - List walkCountList = exerciseAnalysisRepository.findAllByDateBetweenAndUser(startAt, endAt, user) - .stream() - .map(ExerciseAnalysis::getWalkCount) - .collect(Collectors.toList()); - - return QueryUserDetailsResponse.builder() - .userId(user.getId()) - .name(user.getName()) - .profileImageUrl(user.getProfileImageUrl()) - .grade(user.getSection().getGrade()) - .classNum(user.getSection().getClassNum()) - .number(user.getNumber()) - .walkCountList(walkCountList) - .exerciseList(exerciseList) - .build(); - } + private final UserFacade userFacade; + private final LocationRepository locationRepository; + private final ExerciseAnalysisRepository exerciseAnalysisRepository; + + @Transactional(readOnly = true) + public QueryUserDetailsResponse execute(Long userId, LocalDate startAt, LocalDate endAt) { + User user = userFacade.getUserById(userId); + + List exerciseList = user.getExerciseList() + .stream() + .map(locationRepository::findTop1ByExerciseOrderBySequenceDesc) + .map(location -> ExerciseResponse.builder() + .imageUrl(location.getExercise().getImageUrl()) + .startAt(location.getExercise().getCreatedAt()) + .latitude(location.getLatitude()) + .longitude(location.getLongitude()) + .build()) + .collect(Collectors.toList()); + + List walkCountList = exerciseAnalysisRepository.findAllByUserAndDateBetween(user, startAt, endAt) + .stream() + .map(ExerciseAnalysis::getWalkCount) + .collect(Collectors.toList()); + + return QueryUserDetailsResponse.builder() + .userId(user.getId()) + .name(user.getName()) + .profileImageUrl(user.getProfileImageUrl()) + .grade(user.getSection().getGrade()) + .classNum(user.getSection().getClassNum()) + .number(user.getNumber()) + .walkCountList(walkCountList) + .exerciseList(exerciseList) + .build(); + } } diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/service/QueryUserListService.java b/src/main/java/com/walkhub/walkhub/domain/teacher/service/QueryUserListService.java new file mode 100644 index 000000000..911e6aaa5 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/service/QueryUserListService.java @@ -0,0 +1,38 @@ +package com.walkhub.walkhub.domain.teacher.service; + +import com.walkhub.walkhub.domain.teacher.presentation.dto.request.QueryUserListRequest; +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.QueryUserListResponse; +import com.walkhub.walkhub.domain.user.domain.repository.UserRepository; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class QueryUserListService { + private final UserRepository userRepository; + private final UserFacade userFacade; + + public QueryUserListResponse execute(QueryUserListRequest request) { + return QueryUserListResponse.builder() + .userList(userRepository.queryUserList(request.getPage(), request.getScope(), request.getSort(), request.getGrade(), request.getClassNum(), userFacade.getCurrentUser()) + .stream().map(users -> QueryUserListResponse.UserListInfo.builder() + .userId(users.getUserId()) + .name(users.getName()) + .profileImageUrl(users.getProfileImageUrl()) + .grade(users.getGrade()) + .classNum(users.getClassNum()) + .number(users.getNumber()) + .averageWalkCount(users.getAverageWalkCount()) + .totalWalkCount(users.getTotalWalkCount()) + .averageDistance(users.getAverageDistance()) + .totalDistance(users.getTotalDistance()) + .isTeacher(users.getIsTeacher()) + .build() + ).collect(Collectors.toList()) + ) + .build(); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/service/RefreshClassCodeService.java b/src/main/java/com/walkhub/walkhub/domain/teacher/service/RefreshClassCodeService.java deleted file mode 100644 index 692b53b74..000000000 --- a/src/main/java/com/walkhub/walkhub/domain/teacher/service/RefreshClassCodeService.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.walkhub.walkhub.domain.teacher.service; - -import com.walkhub.walkhub.domain.teacher.presentation.dto.response.CodeResponse; -import com.walkhub.walkhub.domain.user.facade.UserFacade; -import com.walkhub.walkhub.global.utils.code.RandomCodeUtil; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@RequiredArgsConstructor -@Service -public class RefreshClassCodeService { - - private final UserFacade userFacade; - - @Transactional - public CodeResponse execute() { - - String classCode = RandomCodeUtil.make(7); - - userFacade.getCurrentUser().getSection().setClassCode(classCode); - - return new CodeResponse(classCode); - } -} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/service/UpdateTeacherSchoolService.java b/src/main/java/com/walkhub/walkhub/domain/teacher/service/UpdateTeacherSchoolService.java new file mode 100644 index 000000000..32f26b034 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/service/UpdateTeacherSchoolService.java @@ -0,0 +1,48 @@ +package com.walkhub.walkhub.domain.teacher.service; + +import com.walkhub.walkhub.domain.challenge.domain.Challenge; +import com.walkhub.walkhub.domain.challenge.domain.repository.ChallengeRepository; +import com.walkhub.walkhub.domain.school.domain.School; +import com.walkhub.walkhub.domain.school.facade.SchoolFacade; +import com.walkhub.walkhub.domain.teacher.presentation.dto.request.UpdateTeacherSchoolRequest; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.domain.user.domain.repository.SectionRepository; +import com.walkhub.walkhub.domain.user.domain.repository.UserRepository; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.List; + +@RequiredArgsConstructor +@Service +public class UpdateTeacherSchoolService { + + private final UserFacade userFacade; + private final SchoolFacade schoolFacade; + private final ChallengeRepository challengeRepository; + private final SectionRepository sectionRepository; + private final UserRepository userRepository; + + @Transactional + public void execute(UpdateTeacherSchoolRequest request) { + User user = userFacade.getCurrentUser(); + School school = schoolFacade.getSchoolByAuthCode(request.getAuthCode()); + List challenges = challengeRepository.findAllByUser(user); + + user.getSchool().minusUserCount(); + school.addUserCount(); + + challenges.forEach(Challenge::setUserNull); + user.setSchool(school); + + if (user.hasSection()) { + userRepository.setUserSectionNull(user.getSection()); + sectionRepository.delete(user.getSection()); + } + + challengeRepository.deleteAllByUserAndEndAtAfter(user, LocalDate.now()); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/service/UserSearchForTeacherService.java b/src/main/java/com/walkhub/walkhub/domain/teacher/service/UserSearchForTeacherService.java new file mode 100644 index 000000000..ac785b0fa --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/service/UserSearchForTeacherService.java @@ -0,0 +1,43 @@ +package com.walkhub.walkhub.domain.teacher.service; + +import com.walkhub.walkhub.domain.teacher.presentation.dto.request.UserSearchRequest; +import com.walkhub.walkhub.domain.teacher.presentation.dto.response.QueryUserListResponse; +import com.walkhub.walkhub.domain.teacher.vo.UserListInfoVO; +import com.walkhub.walkhub.domain.user.domain.repository.UserRepository; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class UserSearchForTeacherService { + private final UserRepository userRepository; + private final UserFacade userFacade; + + public QueryUserListResponse execute(UserSearchRequest request) { + return QueryUserListResponse.builder() + .userList(userRepository.searchUser(request.getScope(), request.getSort(), request.getGrade(), request.getClassNum(), userFacade.getCurrentUser(), request.getName()) + .stream().map(this::buildUserListResponse) + .collect(Collectors.toList()) + ) + .build(); + } + + private QueryUserListResponse.UserListInfo buildUserListResponse(UserListInfoVO users) { + return QueryUserListResponse.UserListInfo.builder() + .userId(users.getUserId()) + .name(users.getName()) + .profileImageUrl(users.getProfileImageUrl()) + .grade(users.getGrade()) + .classNum(users.getClassNum()) + .number(users.getNumber()) + .averageWalkCount(users.getAverageWalkCount()) + .totalWalkCount(users.getTotalWalkCount()) + .averageDistance(users.getAverageDistance()) + .totalDistance(users.getTotalDistance()) + .isTeacher(users.getIsTeacher()) + .build(); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/type/AuthorityScope.java b/src/main/java/com/walkhub/walkhub/domain/teacher/type/AuthorityScope.java new file mode 100644 index 000000000..10101dee0 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/type/AuthorityScope.java @@ -0,0 +1,7 @@ +package com.walkhub.walkhub.domain.teacher.type; + +public enum AuthorityScope { + ALL, + STUDENT, + TEACHER +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/type/SortStandard.java b/src/main/java/com/walkhub/walkhub/domain/teacher/type/SortStandard.java new file mode 100644 index 000000000..7a344733b --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/type/SortStandard.java @@ -0,0 +1,8 @@ +package com.walkhub.walkhub.domain.teacher.type; + +public enum SortStandard { + NAME, + GCN, + WALK_COUNT, + DISTANCE +} diff --git a/src/main/java/com/walkhub/walkhub/domain/teacher/vo/UserListInfoVO.java b/src/main/java/com/walkhub/walkhub/domain/teacher/vo/UserListInfoVO.java new file mode 100644 index 000000000..90c3d9ef6 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/teacher/vo/UserListInfoVO.java @@ -0,0 +1,35 @@ +package com.walkhub.walkhub.domain.teacher.vo; + +import com.querydsl.core.annotations.QueryProjection; +import lombok.Getter; + +@Getter +public class UserListInfoVO { + private final Long userId; + private final String name; + private final String profileImageUrl; + private final Integer grade; + private final Integer classNum; + private final Integer number; + private final Double averageWalkCount; + private final Integer totalWalkCount; + private final Double averageDistance; + private final Integer totalDistance; + private final Boolean isTeacher; + + @QueryProjection + + public UserListInfoVO(Long userId, String name, String profileImageUrl, Integer grade, Integer classNum, Integer number, Double averageWalkCount, Integer totalWalkCount, Double averageDistance, Integer totalDistance, Boolean isTeacher) { + this.userId = userId; + this.name = name; + this.profileImageUrl = profileImageUrl; + this.grade = grade; + this.classNum = classNum; + this.number = number; + this.averageWalkCount = averageWalkCount; + this.totalWalkCount = totalWalkCount; + this.averageDistance = averageDistance; + this.totalDistance = totalDistance; + this.isTeacher = isTeacher; + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/user/domain/Section.java b/src/main/java/com/walkhub/walkhub/domain/user/domain/Section.java index 6d7448220..423abd419 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/domain/Section.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/domain/Section.java @@ -7,15 +7,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.Table; +import javax.persistence.*; +import java.util.List; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -40,6 +33,9 @@ public class Section extends BaseTimeEntity { @JoinColumn(name = "school_id") private School school; + @OneToMany(mappedBy = "section") + private List users; + @Builder public Section(Integer grade, Integer classNum, School school, String classCode) { this.grade = grade; diff --git a/src/main/java/com/walkhub/walkhub/domain/user/domain/User.java b/src/main/java/com/walkhub/walkhub/domain/user/domain/User.java index 092fbdd83..bbe0a74a6 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/domain/User.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/domain/User.java @@ -8,9 +8,12 @@ import com.walkhub.walkhub.domain.school.domain.School; import com.walkhub.walkhub.domain.user.domain.type.HealthInfo; import com.walkhub.walkhub.domain.user.domain.type.Sex; +import com.walkhub.walkhub.domain.user.exception.SectionNotFoundException; import com.walkhub.walkhub.domain.user.presentation.dto.request.UpdateUserInfoRequest; import com.walkhub.walkhub.global.entity.BaseTimeEntity; import com.walkhub.walkhub.global.enums.Authority; +import com.walkhub.walkhub.global.exception.InvalidRoleException; +import com.walkhub.walkhub.global.utils.code.RandomCodeUtil; import com.walkhub.walkhub.infrastructure.image.DefaultImage; import lombok.AccessLevel; import lombok.Builder; @@ -18,7 +21,6 @@ import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.ColumnDefault; -import org.hibernate.validator.constraints.Length; import javax.persistence.CascadeType; import javax.persistence.Column; @@ -63,7 +65,6 @@ public class User extends BaseTimeEntity { private String profileImageUrl; @NotNull - @Length(max = 6) @Enumerated(EnumType.STRING) private Authority authority; @@ -83,8 +84,9 @@ public class User extends BaseTimeEntity { private HealthInfo healthInfo; @NotNull - @Length(max = 6) + @ColumnDefault("X") @Enumerated(EnumType.STRING) + @Setter private Sex sex; @OneToOne(fetch = FetchType.LAZY) @@ -102,8 +104,8 @@ public class User extends BaseTimeEntity { @JoinColumn(name = "max_level_id") private CalorieLevel maxLevel; - @NotNull @ColumnDefault("10000") + @Column(nullable = false) private Integer dailyWalkCountGoal; @OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE) @@ -114,12 +116,11 @@ public class User extends BaseTimeEntity { @OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE) private List exerciseList; - + @Builder - public User(Long id, String accountId, String password, String phoneNumber, String name, + public User(String accountId, String password, String phoneNumber, String name, Authority authority, Section section, School school, boolean isMeasuring, - Integer weight, BigDecimal height, Sex sex, Badge badge, String deviceToken, Integer dailyWalkCountGoal) { - this.id = id; + Integer weight, BigDecimal height, Sex sex, Badge badge, CalorieLevel calorieLevel) { this.accountId = accountId; this.password = password; this.phoneNumber = phoneNumber; @@ -129,10 +130,10 @@ public User(Long id, String accountId, String password, String phoneNumber, Stri this.school = school; this.isMeasuring = isMeasuring; this.healthInfo = new HealthInfo(weight, height); - this.sex = sex; + this.dailyWalkCountGoal = 10000; + if(sex != null) this.sex = sex; this.badge = badge; - this.deviceToken = deviceToken; - this.dailyWalkCountGoal = dailyWalkCountGoal; + this.maxLevel = calorieLevel; } public void setDeviceToken(String deviceToken) { @@ -142,7 +143,6 @@ public void setDeviceToken(String deviceToken) { public void updateUser(UpdateUserInfoRequest request) { this.name = request.getName(); this.profileImageUrl = request.getProfileImageUrl(); - this.sex = request.getSex(); } public void setBadge(Badge badge) { @@ -169,12 +169,44 @@ public void setAuthorityTeacher() { this.authority = Authority.TEACHER; } - public void updatedailyWalkCountGoal(Integer dailyWalkCountGoal) { + public void updateDailyWalkCountGoal(Integer dailyWalkCountGoal) { this.dailyWalkCountGoal = dailyWalkCountGoal; } + public Section getSection() { + if (!hasSection()) { + throw SectionNotFoundException.EXCEPTION; + } + return this.section; + } + + public boolean hasSection() { + return this.section != null; + } + + public boolean hasSchool() { + return this.school != null; + } + public void setMaxLevel(CalorieLevel calorieLevel) { this.maxLevel = calorieLevel; } + public void setSectionNull() { + this.section = null; + } + + public String updateRootUserPassword() { + if (this.authority != Authority.ROOT) { + throw InvalidRoleException.EXCEPTION; + } + + this.password = RandomCodeUtil.make(8); + return this.password; + } + + public void updateIsMeasuring(Boolean isMeasuring) { + this.isMeasuring = isMeasuring; + } + } diff --git a/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/SectionRepository.java b/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/SectionRepository.java index 2e0cdef41..a753202bb 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/SectionRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/SectionRepository.java @@ -1,7 +1,12 @@ package com.walkhub.walkhub.domain.user.domain.repository; +import com.walkhub.walkhub.domain.school.domain.School; import com.walkhub.walkhub.domain.user.domain.Section; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface SectionRepository extends JpaRepository { + Optional
findByClassCodeAndSchool(String classCode, School school); + Optional
findBySchoolAndGradeAndClassNum(School school, Integer grade, Integer classNum); } diff --git a/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/UserRepository.java b/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/UserRepository.java index 08d01f703..9e9736562 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/UserRepository.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/UserRepository.java @@ -1,16 +1,34 @@ package com.walkhub.walkhub.domain.user.domain.repository; +import com.walkhub.walkhub.domain.school.domain.School; import com.walkhub.walkhub.domain.user.domain.Section; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.global.enums.Authority; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; -public interface UserRepository extends CrudRepository { +public interface UserRepository extends CrudRepository, UserRepositoryCustom { Optional findByAccountId(String accountId); + Optional findByPhoneNumber(String phoneNumber); - List findAllBySectionAndAuthority(Section section, Authority authority); + List findAllBySchoolIdAndNameContaining(Long id, String name); + + @Query("select u from User u left join fetch u.section where u.school = :school and u.authority = :authority") + List findAllBySchoolAndAuthority(@Param("school") School school, @Param("authority") Authority authority); + + Optional findBySchoolAndAuthority(School school, Authority authority); + + @Transactional + @Modifying + @Query("update User u set u.section = null where u.section = :section") + void setUserSectionNull(@Param("section") Section section); + + List findTop3BySchoolAndIsMeasuringTrue(School school); } diff --git a/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/UserRepositoryCustom.java b/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/UserRepositoryCustom.java new file mode 100644 index 000000000..a8d890cc5 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/UserRepositoryCustom.java @@ -0,0 +1,13 @@ +package com.walkhub.walkhub.domain.user.domain.repository; + +import com.walkhub.walkhub.domain.teacher.type.AuthorityScope; +import com.walkhub.walkhub.domain.teacher.type.SortStandard; +import com.walkhub.walkhub.domain.teacher.vo.UserListInfoVO; +import com.walkhub.walkhub.domain.user.domain.User; + +import java.util.List; + +public interface UserRepositoryCustom { + List queryUserList(Integer page, AuthorityScope scope, SortStandard sort, Integer grade, Integer classNum, User currentUser); + List searchUser(AuthorityScope scope, SortStandard sort, Integer grade, Integer classNum, User currentUser, String name); +} diff --git a/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/UserRepositoryCustomImpl.java b/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/UserRepositoryCustomImpl.java new file mode 100644 index 000000000..b01002b32 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/user/domain/repository/UserRepositoryCustomImpl.java @@ -0,0 +1,142 @@ +package com.walkhub.walkhub.domain.user.domain.repository; + +import com.querydsl.core.types.OrderSpecifier; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.types.dsl.MathExpressions; +import com.querydsl.jpa.impl.JPAQueryFactory; +import com.walkhub.walkhub.domain.teacher.type.AuthorityScope; +import com.walkhub.walkhub.domain.teacher.type.SortStandard; +import com.walkhub.walkhub.domain.teacher.vo.QUserListInfoVO; +import com.walkhub.walkhub.domain.teacher.vo.UserListInfoVO; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.global.enums.Authority; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDate; +import java.util.List; + +import static com.walkhub.walkhub.domain.exercise.domain.QExerciseAnalysis.exerciseAnalysis; +import static com.walkhub.walkhub.domain.user.domain.QUser.user; + +@RequiredArgsConstructor +public class UserRepositoryCustomImpl implements UserRepositoryCustom { + private final JPAQueryFactory queryFactory; + + @Override + public List queryUserList(Integer page, AuthorityScope scope, SortStandard sort, Integer grade, Integer classNum, User currentUser) { + long size = 4; + return queryFactory + .select(new QUserListInfoVO( + user.id.as("userId"), + user.name, + user.profileImageUrl, + user.section.grade, + user.section.classNum, + user.number, + MathExpressions.round(exerciseAnalysis.walkCount.avg(), 1).as("averageWalkCount"), + exerciseAnalysis.walkCount.sum().as("totalWalkCount"), + MathExpressions.round(exerciseAnalysis.distance.avg(), 1).as("averageDistance"), + exerciseAnalysis.distance.sum().as("totalDistance"), + user.authority.eq(Authority.TEACHER).as("isTeacher") + )) + .from(user) + .join(user.exerciseAnalyses, exerciseAnalysis) + .where( + user.school.eq(currentUser.getSchool()), + buildFilteringCondition(scope), + gradeEq(grade), + classNumEq(classNum), + exerciseAnalysis.date.after(LocalDate.now().minusDays(7)) + ) + .offset((long) page * size) + .limit(size) + .orderBy(buildSortCondition(sort)) + .groupBy(user.id) + .fetch(); + } + + @Override + public List searchUser(AuthorityScope scope, SortStandard sort, Integer grade, Integer classNum, User currentUser, String name) { + return queryFactory + .select(new QUserListInfoVO( + user.id.as("userId"), + user.name, + user.profileImageUrl, + user.section.grade, + user.section.classNum, + user.number, + MathExpressions.round(exerciseAnalysis.walkCount.avg(), 1).as("averageWalkCount"), + exerciseAnalysis.walkCount.sum().as("totalWalkCount"), + MathExpressions.round(exerciseAnalysis.distance.avg(), 1).as("averageDistance"), + exerciseAnalysis.distance.sum().as("totalDistance"), + user.authority.eq(Authority.TEACHER).as("isTeacher") + )) + .from(user) + .join(user.exerciseAnalyses, exerciseAnalysis) + .where( + user.school.eq(currentUser.getSchool()), + buildFilteringCondition(scope), + gradeEq(grade), + classNumEq(classNum), + nameEq(name), + exerciseAnalysis.date.after(LocalDate.now().minusDays(7)) + ) + .orderBy(buildSortCondition(sort)) + .groupBy(user.id) + .fetch(); + + } + + private BooleanExpression gradeEq(Integer grade) { + return grade != null ? user.section.grade.eq(grade) : null; + } + + private BooleanExpression classNumEq(Integer classNum) { + return classNum != null ? user.section.classNum.eq(classNum) : null; + } + + private BooleanExpression nameEq(String name) { + return name.isEmpty() ? null : user.name.contains(name); + } + + private BooleanExpression buildFilteringCondition(AuthorityScope scope) { + switch (scope) { + case ALL: + return user.authority.eq(Authority.TEACHER).or(user.authority.eq(Authority.USER)); + case STUDENT: + return user.authority.eq(Authority.USER); + case TEACHER: + return user.authority.eq(Authority.TEACHER); + default: + return null; + } + } + + private OrderSpecifier[] buildSortCondition(SortStandard sort) { + switch (sort) { + case NAME: + return new OrderSpecifier[]{ + user.authority.asc(), user.name.asc() + }; + case GCN: + return new OrderSpecifier[]{ + user.authority.asc(), + user.section.grade.asc(), + user.section.classNum.asc(), + user.number.asc() + }; + case WALK_COUNT: + return new OrderSpecifier[]{ + user.authority.asc(), + exerciseAnalysis.walkCount.desc() + }; + case DISTANCE: + return new OrderSpecifier[]{ + user.authority.asc(), + exerciseAnalysis.distance.desc() + }; + default: + return new OrderSpecifier[]{}; + } + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/user/exception/ClassCodeNotMatchException.java b/src/main/java/com/walkhub/walkhub/domain/user/exception/ClassCodeNotMatchException.java deleted file mode 100644 index c1cf52c56..000000000 --- a/src/main/java/com/walkhub/walkhub/domain/user/exception/ClassCodeNotMatchException.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.walkhub.walkhub.domain.user.exception; - -import com.walkhub.walkhub.global.error.exception.ErrorCode; -import com.walkhub.walkhub.global.error.exception.WalkhubException; - -public class ClassCodeNotMatchException extends WalkhubException { - - public static final WalkhubException EXCEPTION = - new ClassCodeNotMatchException(); - - private ClassCodeNotMatchException() { - super(ErrorCode.CLASS_CODE_NOT_MATCH); - } - -} diff --git a/src/main/java/com/walkhub/walkhub/domain/user/exception/DefaultTitleBadgeNotFound.java b/src/main/java/com/walkhub/walkhub/domain/user/exception/DefaultTitleBadgeNotFound.java new file mode 100644 index 000000000..cfa6d5a7d --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/user/exception/DefaultTitleBadgeNotFound.java @@ -0,0 +1,14 @@ +package com.walkhub.walkhub.domain.user.exception; + +import com.walkhub.walkhub.global.error.exception.ErrorCode; +import com.walkhub.walkhub.global.error.exception.WalkhubException; + +public class DefaultTitleBadgeNotFound extends WalkhubException { + + public static final WalkhubException EXCEPTION = + new DefaultTitleBadgeNotFound(); + + public DefaultTitleBadgeNotFound() { + super(ErrorCode.DEFAULT_TITLE_BADGE_NOT_FOUND); + } +} diff --git a/src/main/java/com/walkhub/walkhub/domain/user/presentation/UserController.java b/src/main/java/com/walkhub/walkhub/domain/user/presentation/UserController.java index 59af8bfc5..c9fe4dc73 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/presentation/UserController.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/presentation/UserController.java @@ -3,6 +3,7 @@ import com.walkhub.walkhub.domain.auth.presentation.dto.response.UserTokenResponse; import com.walkhub.walkhub.domain.user.presentation.dto.request.InputHealthInformationRequest; import com.walkhub.walkhub.domain.user.presentation.dto.request.JoinSectionRequest; +import com.walkhub.walkhub.domain.user.presentation.dto.request.UpdateGoalWalkCountRequest; import com.walkhub.walkhub.domain.user.presentation.dto.request.UpdatePasswordRequest; import com.walkhub.walkhub.domain.user.presentation.dto.request.UpdateSchoolInfoRequest; import com.walkhub.walkhub.domain.user.presentation.dto.request.UpdateUserInfoRequest; @@ -10,11 +11,13 @@ import com.walkhub.walkhub.domain.user.presentation.dto.request.UserSignUpRequest; import com.walkhub.walkhub.domain.user.presentation.dto.response.QueryUserProfileResponse; import com.walkhub.walkhub.domain.user.presentation.dto.response.UserAccountIdResponse; +import com.walkhub.walkhub.domain.user.service.ExitSectionService; import com.walkhub.walkhub.domain.user.service.InputHealthInformationService; import com.walkhub.walkhub.domain.user.service.JoinSectionService; import com.walkhub.walkhub.domain.user.service.QueryMyPageService; import com.walkhub.walkhub.domain.user.service.QueryUserProfileService; import com.walkhub.walkhub.domain.user.service.SearchAccountIdService; +import com.walkhub.walkhub.domain.user.service.UpdateGoalWalkCountService; import com.walkhub.walkhub.domain.user.service.UpdatePasswordService; import com.walkhub.walkhub.domain.user.service.UpdateSchoolInfoService; import com.walkhub.walkhub.domain.user.service.UpdateUserInfoService; @@ -22,6 +25,7 @@ import com.walkhub.walkhub.domain.user.service.UserSignUpService; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -48,6 +52,8 @@ public class UserController { private final UpdatePasswordService updatePasswordService; private final UpdateSchoolInfoService updateSchoolInfoService; private final SearchAccountIdService searchAccountIdService; + private final UpdateGoalWalkCountService updateGoalWalkCountService; + private final ExitSectionService exitSectionService; @ResponseStatus(HttpStatus.CREATED) @PostMapping("/verification-codes") @@ -84,10 +90,9 @@ public void updateUserInfo(@RequestBody @Valid UpdateUserInfoRequest request) { } @ResponseStatus(HttpStatus.CREATED) - @PostMapping("/classes/{section-id}") - public void joinSection(@PathVariable(name = "section-id") Long sectionId, - @RequestBody @Valid JoinSectionRequest request) { - joinSectionService.execute(sectionId, request); + @PostMapping("/classes") + public void joinSection(@RequestBody @Valid JoinSectionRequest request) { + joinSectionService.execute(request); } @ResponseStatus(HttpStatus.NO_CONTENT) @@ -97,7 +102,7 @@ public void updatePassword(@RequestBody @Valid UpdatePasswordRequest request) { } @ResponseStatus(HttpStatus.NO_CONTENT) - @PatchMapping("/school") + @PatchMapping("/schools") public void updateSchoolInfo(@RequestBody @Valid UpdateSchoolInfoRequest request) { updateSchoolInfoService.execute(request); } @@ -107,4 +112,16 @@ public UserAccountIdResponse searchAccountId(@PathVariable(name = "phone-number" return searchAccountIdService.execute(phoneNumber); } + @ResponseStatus(HttpStatus.NO_CONTENT) + @PatchMapping("/goal") + public void updateGoalWalkCount(@RequestBody UpdateGoalWalkCountRequest updateGoalWalkCountRequest) { + updateGoalWalkCountService.execute(updateGoalWalkCountRequest); + } + + @ResponseStatus(HttpStatus.NO_CONTENT) + @DeleteMapping("/classes") + public void exitSection() { + exitSectionService.execute(); + } + } diff --git a/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/request/InputHealthInformationRequest.java b/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/request/InputHealthInformationRequest.java index dc35a7e96..82c8958ba 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/request/InputHealthInformationRequest.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/request/InputHealthInformationRequest.java @@ -1,5 +1,6 @@ package com.walkhub.walkhub.domain.user.presentation.dto.request; +import com.walkhub.walkhub.domain.user.domain.type.Sex; import lombok.Getter; import lombok.NoArgsConstructor; @@ -12,11 +13,12 @@ @NoArgsConstructor public class InputHealthInformationRequest { - @NotNull(message = "height는 null일 수 없습니다.") @Digits(integer = 3, fraction = 1) private BigDecimal height; - @NotNull(message = "weight는 null일 수 없습니다.") @Positive(message = "weight는 양수여야 합니다.") private Integer weight; + + @NotNull + private Sex sex; } \ No newline at end of file diff --git a/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/request/UpdateUserInfoRequest.java b/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/request/UpdateUserInfoRequest.java index 7144c99f0..8952cf514 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/request/UpdateUserInfoRequest.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/request/UpdateUserInfoRequest.java @@ -1,6 +1,5 @@ package com.walkhub.walkhub.domain.user.presentation.dto.request; -import com.walkhub.walkhub.domain.user.domain.type.Sex; import lombok.Getter; import lombok.NoArgsConstructor; @@ -18,8 +17,4 @@ public class UpdateUserInfoRequest { @NotBlank(message = "profile_image_url은 null, 공백, 띄어쓰기를 허용하지 않습니다.") private String profileImageUrl; - @NotBlank(message = "sex는 null, 공백, 띄어쓰기를 허용하지 않습니다.") - @Size(min = 1, max = 1, message = "sex는 1글자 여야 합니다.") - private Sex sex; - } diff --git a/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/response/CalorieLevelListResponse.java b/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/response/CalorieLevelListResponse.java index b97865551..2fcd74bf0 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/response/CalorieLevelListResponse.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/response/CalorieLevelListResponse.java @@ -18,7 +18,7 @@ public static class CalorieLevelResponse { private final Long levelId; private final String foodImageUrl; private final String foodName; - private final Integer calorie; + private final Double calorie; private final Integer size; private final Integer level; private final String message; diff --git a/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/response/QueryUserProfileResponse.java b/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/response/QueryUserProfileResponse.java index 79206973d..9fa6f3ba6 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/response/QueryUserProfileResponse.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/presentation/dto/response/QueryUserProfileResponse.java @@ -11,6 +11,7 @@ public class QueryUserProfileResponse { private final String name; private final String profileImageUrl; private final String schoolName; + private final String schoolImageUrl; private final Integer grade; private final Integer classNum; private final TitleBadge titleBadge; diff --git a/src/main/java/com/walkhub/walkhub/domain/user/service/ExitSectionService.java b/src/main/java/com/walkhub/walkhub/domain/user/service/ExitSectionService.java new file mode 100644 index 000000000..00695d1c7 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/domain/user/service/ExitSectionService.java @@ -0,0 +1,25 @@ +package com.walkhub.walkhub.domain.user.service; + +import com.walkhub.walkhub.domain.challenge.domain.repository.ChallengeStatusRepository; +import com.walkhub.walkhub.domain.user.domain.User; +import com.walkhub.walkhub.domain.user.facade.UserFacade; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Service +public class ExitSectionService { + + private final UserFacade userFacade; + private final ChallengeStatusRepository challengeStatusRepository; + + @Transactional + public void execute() { + User user = userFacade.getCurrentUser(); + + challengeStatusRepository.deleteNotOverChallengeStatusByUserId(user.getId()); + user.setSectionNull(); + } + +} diff --git a/src/main/java/com/walkhub/walkhub/domain/user/service/InputHealthInformationService.java b/src/main/java/com/walkhub/walkhub/domain/user/service/InputHealthInformationService.java index 946d5645d..d13d12446 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/service/InputHealthInformationService.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/service/InputHealthInformationService.java @@ -6,6 +6,7 @@ import com.walkhub.walkhub.domain.user.presentation.dto.request.InputHealthInformationRequest; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor @Service @@ -13,8 +14,14 @@ public class InputHealthInformationService { private final UserFacade userFacade; + @Transactional public void execute(InputHealthInformationRequest request) { User user = userFacade.getCurrentUser(); - user.setHealthInfo(new HealthInfo(request.getWeight(), request.getHeight())); + if (request.getHeight() != null || request.getWeight() != null) { + user.setHealthInfo(new HealthInfo(request.getWeight(), request.getHeight())); + } + if (request.getSex() != null) { + user.setSex(request.getSex()); + } } } \ No newline at end of file diff --git a/src/main/java/com/walkhub/walkhub/domain/user/service/JoinSectionService.java b/src/main/java/com/walkhub/walkhub/domain/user/service/JoinSectionService.java index 0532a898d..0b57ab91d 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/service/JoinSectionService.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/service/JoinSectionService.java @@ -1,10 +1,10 @@ package com.walkhub.walkhub.domain.user.service; -import com.walkhub.walkhub.domain.school.exception.AgencyCodeNotMatchException; import com.walkhub.walkhub.domain.user.domain.Section; import com.walkhub.walkhub.domain.user.domain.User; -import com.walkhub.walkhub.domain.user.exception.ClassCodeNotMatchException; -import com.walkhub.walkhub.domain.user.facade.SectionFacade; +import com.walkhub.walkhub.domain.user.domain.repository.SectionRepository; +import com.walkhub.walkhub.domain.user.exception.AlreadyJoinedException; +import com.walkhub.walkhub.domain.user.exception.SectionNotFoundException; import com.walkhub.walkhub.domain.user.facade.UserFacade; import com.walkhub.walkhub.domain.user.presentation.dto.request.JoinSectionRequest; import lombok.RequiredArgsConstructor; @@ -16,21 +16,18 @@ public class JoinSectionService { private final UserFacade userFacade; - private final SectionFacade sectionFacade; + private final SectionRepository sectionRepository; @Transactional - public void execute(Long sectionId, JoinSectionRequest request) { + public void execute(JoinSectionRequest request) { User user = userFacade.getCurrentUser(); - Section section = sectionFacade.getSectionById(sectionId); - - if (!section.getClassCode().equals(request.getClassCode())) { - throw ClassCodeNotMatchException.EXCEPTION; + if (user.hasSection()) { + throw AlreadyJoinedException.EXCEPTION; } - if (!user.getSchool().equals(section.getSchool())) { - throw AgencyCodeNotMatchException.EXCEPTION; - } + Section section = sectionRepository.findByClassCodeAndSchool(request.getClassCode(), user.getSchool()) + .orElseThrow(() -> SectionNotFoundException.EXCEPTION); user.setSection(section); user.setNumber(request.getNumber()); diff --git a/src/main/java/com/walkhub/walkhub/domain/user/service/QueryMyPageService.java b/src/main/java/com/walkhub/walkhub/domain/user/service/QueryMyPageService.java index d9111dd4f..643a01feb 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/service/QueryMyPageService.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/service/QueryMyPageService.java @@ -2,6 +2,7 @@ import com.walkhub.walkhub.domain.badge.domain.Badge; import com.walkhub.walkhub.domain.calorielevel.domain.CalorieLevel; +import com.walkhub.walkhub.domain.user.domain.Section; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.facade.UserFacade; import com.walkhub.walkhub.domain.user.presentation.dto.response.QueryUserProfileResponse; @@ -17,27 +18,28 @@ public class QueryMyPageService { private final UserFacade userFacade; public QueryUserProfileResponse execute() { - User user = userFacade.getCurrentUser(); Badge titleBadge = user.getBadge(); CalorieLevel level = user.getMaxLevel(); + Section section = user.hasSection() ? user.getSection() : Section.builder().build(); return QueryUserProfileResponse.builder() - .userId(user.getId()) - .name(user.getName()) - .profileImageUrl(user.getProfileImageUrl()) - .schoolName(user.getSchool().getName()) - .grade(user.getSection().getGrade()) - .classNum(user.getSection().getClassNum()) - .titleBadge(TitleBadge.builder() - .id(titleBadge.getId()) - .name(titleBadge.getName()) - .imageUrl(titleBadge.getImageUrl()) - .build()) - .level(Level.builder() - .imageUrl(level.getFoodImageUrl()) - .name(level.getFoodName()) - .build()) - .build(); + .userId(user.getId()) + .name(user.getName()) + .profileImageUrl(user.getProfileImageUrl()) + .schoolName(user.getSchool().getName()) + .schoolImageUrl(user.getSchool().getLogoImageUrl()) + .classNum(section.getClassNum()) + .grade(section.getGrade()) + .titleBadge(TitleBadge.builder() + .id(titleBadge.getId()) + .name(titleBadge.getName()) + .imageUrl(titleBadge.getImageUrl()) + .build()) + .level(Level.builder() + .imageUrl(level.getFoodImageUrl()) + .name(level.getFoodName()) + .build()) + .build(); } -} +} \ No newline at end of file diff --git a/src/main/java/com/walkhub/walkhub/domain/user/service/QueryUserProfileService.java b/src/main/java/com/walkhub/walkhub/domain/user/service/QueryUserProfileService.java index 04c46a959..2c6c6f657 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/service/QueryUserProfileService.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/service/QueryUserProfileService.java @@ -2,6 +2,7 @@ import com.walkhub.walkhub.domain.badge.domain.Badge; import com.walkhub.walkhub.domain.calorielevel.domain.CalorieLevel; +import com.walkhub.walkhub.domain.user.domain.Section; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.facade.UserFacade; import com.walkhub.walkhub.domain.user.presentation.dto.response.QueryUserProfileResponse; @@ -21,14 +22,15 @@ public QueryUserProfileResponse execute(Long userId) { User user = userFacade.getUserById(userId); Badge titleBadge = user.getBadge(); CalorieLevel level = user.getMaxLevel(); + Section section = user.hasSection() ? user.getSection() : Section.builder().build(); return QueryUserProfileResponse.builder() .userId(user.getId()) .name(user.getName()) .profileImageUrl(user.getProfileImageUrl()) .schoolName(user.getSchool().getName()) - .grade(user.getSection().getGrade()) - .classNum(user.getSection().getClassNum()) + .classNum(section.getClassNum()) + .grade(section.getGrade()) .titleBadge(TitleBadge.builder() .id(titleBadge.getId()) .name(titleBadge.getName()) diff --git a/src/main/java/com/walkhub/walkhub/domain/user/service/UpdateGoalWalkCountService.java b/src/main/java/com/walkhub/walkhub/domain/user/service/UpdateGoalWalkCountService.java index f17e0abfe..be701453a 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/service/UpdateGoalWalkCountService.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/service/UpdateGoalWalkCountService.java @@ -17,6 +17,6 @@ public class UpdateGoalWalkCountService { public void execute(UpdateGoalWalkCountRequest request) { User user = userFacade.getCurrentUser(); - user.updatedailyWalkCountGoal(request.getDailyWalkCountGoal()); + user.updateDailyWalkCountGoal(request.getDailyWalkCountGoal()); } } diff --git a/src/main/java/com/walkhub/walkhub/domain/user/service/UpdateSchoolInfoService.java b/src/main/java/com/walkhub/walkhub/domain/user/service/UpdateSchoolInfoService.java index d761d2653..5e8b3f0f8 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/service/UpdateSchoolInfoService.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/service/UpdateSchoolInfoService.java @@ -1,9 +1,9 @@ package com.walkhub.walkhub.domain.user.service; +import com.walkhub.walkhub.domain.challenge.domain.repository.ChallengeStatusRepository; import com.walkhub.walkhub.domain.school.domain.School; -import com.walkhub.walkhub.domain.school.domain.repository.SchoolRepository; +import com.walkhub.walkhub.domain.school.facade.SchoolFacade; import com.walkhub.walkhub.domain.user.domain.User; -import com.walkhub.walkhub.domain.user.exception.SchoolNotFoundException; import com.walkhub.walkhub.domain.user.facade.UserFacade; import com.walkhub.walkhub.domain.user.presentation.dto.request.UpdateSchoolInfoRequest; import lombok.RequiredArgsConstructor; @@ -15,15 +15,20 @@ public class UpdateSchoolInfoService { private final UserFacade userFacade; - private final SchoolRepository schoolRepository; + private final ChallengeStatusRepository challengeStatusRepository; + private final SchoolFacade schoolFacade; @Transactional public void execute(UpdateSchoolInfoRequest request) { User user = userFacade.getCurrentUser(); + School school = schoolFacade.getSchoolById(request.getSchoolId()); - School school = schoolRepository.findById(request.getSchoolId()) - .orElseThrow(() -> SchoolNotFoundException.EXCEPTION); - + user.setSection(null); user.setSchool(school); + user.getSchool().minusUserCount(); + school.addUserCount(); + + + challengeStatusRepository.deleteNotOverChallengeStatusByUserId(user.getId()); } } diff --git a/src/main/java/com/walkhub/walkhub/domain/user/service/UserSignUpService.java b/src/main/java/com/walkhub/walkhub/domain/user/service/UserSignUpService.java index 257757265..e3ba1a590 100644 --- a/src/main/java/com/walkhub/walkhub/domain/user/service/UserSignUpService.java +++ b/src/main/java/com/walkhub/walkhub/domain/user/service/UserSignUpService.java @@ -1,12 +1,19 @@ package com.walkhub.walkhub.domain.user.service; import com.walkhub.walkhub.domain.auth.presentation.dto.response.UserTokenResponse; +import com.walkhub.walkhub.domain.badge.domain.Badge; +import com.walkhub.walkhub.domain.badge.domain.repository.BadgeRepository; +import com.walkhub.walkhub.domain.calorielevel.domain.CalorieLevel; +import com.walkhub.walkhub.domain.calorielevel.domain.repository.CalorieLevelRepository; +import com.walkhub.walkhub.domain.calorielevel.exception.CalorieLevelNotFoundException; import com.walkhub.walkhub.domain.school.domain.School; import com.walkhub.walkhub.domain.school.domain.repository.SchoolRepository; import com.walkhub.walkhub.domain.user.domain.User; import com.walkhub.walkhub.domain.user.domain.UserAuthCode; import com.walkhub.walkhub.domain.user.domain.repository.UserAuthCodeRepository; import com.walkhub.walkhub.domain.user.domain.repository.UserRepository; +import com.walkhub.walkhub.domain.user.domain.type.HealthInfo; +import com.walkhub.walkhub.domain.user.exception.DefaultTitleBadgeNotFound; import com.walkhub.walkhub.domain.user.exception.SchoolNotFoundException; import com.walkhub.walkhub.domain.user.exception.UnauthorizedUserAuthCodeException; import com.walkhub.walkhub.domain.user.exception.UserAuthCodeNotFoundException; @@ -18,8 +25,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; +import java.time.ZonedDateTime; @RequiredArgsConstructor @Service @@ -32,7 +40,10 @@ public class UserSignUpService { private final PasswordEncoder passwordEncoder; private final JwtTokenProvider jwtTokenProvider; private final JwtProperties jwtProperties; + private final BadgeRepository badgeRepository; + private final CalorieLevelRepository calorieLevelRepository; + @Transactional public UserTokenResponse execute(UserSignUpRequest request) { UserAuthCode code = userAuthCodeRepository.findById(request.getPhoneNumber()) .orElseThrow(() -> UserAuthCodeNotFoundException.EXCEPTION); @@ -42,10 +53,16 @@ public UserTokenResponse execute(UserSignUpRequest request) { userFacade.checkUserExists(request.getAccountId()); + Badge defaultTitleBadge = badgeRepository.findById(1L) + .orElseThrow(() -> DefaultTitleBadgeNotFound.EXCEPTION); + School school = schoolRepository.findById(request.getSchoolId()) .orElseThrow(() -> SchoolNotFoundException.EXCEPTION); - userRepository.save(User.builder() + CalorieLevel calorieLevel = calorieLevelRepository.findByLevel(1) + .orElseThrow(() -> CalorieLevelNotFoundException.EXCEPTION); + + User user = User.builder() .accountId(request.getAccountId()) .password(passwordEncoder.encode(request.getPassword())) .phoneNumber(request.getPhoneNumber()) @@ -56,15 +73,25 @@ public UserTokenResponse execute(UserSignUpRequest request) { .weight(request.getWeight()) .sex(request.getSex()) .isMeasuring(false) - .build()); + .badge(defaultTitleBadge) + .calorieLevel(calorieLevel) + .build(); + userRepository.save(user); + + school.addUserCount(); + HealthInfo healthInfo = user.getHealthInfo(); String accessToken = jwtTokenProvider.generateAccessToken(request.getAccountId()); String refreshToken = jwtTokenProvider.generateRefreshToken(request.getAccountId()); return UserTokenResponse.builder() .accessToken(accessToken) - .expiredAt(LocalDateTime.now().plusSeconds(jwtProperties.getAccessExp())) + .expiredAt(ZonedDateTime.now().plusSeconds(jwtProperties.getAccessExp())) .refreshToken(refreshToken) + .authority(user.getAuthority()) + .height(healthInfo.getHeight()) + .weight(healthInfo.getWeight()) + .sex(user.getSex()) .build(); } } diff --git a/src/main/java/com/walkhub/walkhub/global/async/AsyncConfig.java b/src/main/java/com/walkhub/walkhub/global/async/AsyncConfig.java index 53634241b..012d3c25d 100644 --- a/src/main/java/com/walkhub/walkhub/global/async/AsyncConfig.java +++ b/src/main/java/com/walkhub/walkhub/global/async/AsyncConfig.java @@ -1,8 +1,27 @@ package com.walkhub.walkhub.global.async; import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; @EnableAsync @Configuration -public class AsyncConfig { } \ No newline at end of file +public class AsyncConfig implements AsyncConfigurer { + + @Override + public Executor getAsyncExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setThreadNamePrefix("async-thread-"); + executor.setCorePoolSize(10); + executor.setMaxPoolSize(50); + executor.setQueueCapacity(100); + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); + executor.initialize(); + + return executor; + } +} \ No newline at end of file diff --git a/src/main/java/com/walkhub/walkhub/global/date/DateTimeZoneConfiguration.java b/src/main/java/com/walkhub/walkhub/global/date/DateTimeZoneConfiguration.java new file mode 100644 index 000000000..ddda9981b --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/global/date/DateTimeZoneConfiguration.java @@ -0,0 +1,14 @@ +package com.walkhub.walkhub.global.date; + +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.TimeZone; + +@Configuration +public class DateTimeZoneConfiguration { + @PostConstruct + public void setup() { + TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); + } +} diff --git a/src/main/java/com/walkhub/walkhub/global/entity/BaseTimeEntity.java b/src/main/java/com/walkhub/walkhub/global/entity/BaseTimeEntity.java index c694b0afe..fbb5f82cb 100644 --- a/src/main/java/com/walkhub/walkhub/global/entity/BaseTimeEntity.java +++ b/src/main/java/com/walkhub/walkhub/global/entity/BaseTimeEntity.java @@ -1,21 +1,16 @@ package com.walkhub.walkhub.global.entity; import lombok.Getter; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.Column; -import javax.persistence.EntityListeners; import javax.persistence.MappedSuperclass; -import java.time.LocalDateTime; +import java.time.ZonedDateTime; @Getter @MappedSuperclass -@EntityListeners(AuditingEntityListener.class) public abstract class BaseTimeEntity { @Column(nullable = false) - @CreatedDate - private LocalDateTime createdAt; + private ZonedDateTime createdAt = ZonedDateTime.now(); } diff --git a/src/main/java/com/walkhub/walkhub/global/enums/Authority.java b/src/main/java/com/walkhub/walkhub/global/enums/Authority.java index 712313986..bd82e9595 100644 --- a/src/main/java/com/walkhub/walkhub/global/enums/Authority.java +++ b/src/main/java/com/walkhub/walkhub/global/enums/Authority.java @@ -2,7 +2,6 @@ public enum Authority { USER, - STUDENT, TEACHER, ROOT, SU diff --git a/src/main/java/com/walkhub/walkhub/global/enums/UserScope.java b/src/main/java/com/walkhub/walkhub/global/enums/UserScope.java index 26a12fbbd..62a9fa5b3 100644 --- a/src/main/java/com/walkhub/walkhub/global/enums/UserScope.java +++ b/src/main/java/com/walkhub/walkhub/global/enums/UserScope.java @@ -1,5 +1,8 @@ package com.walkhub.walkhub.global.enums; public enum UserScope { - CLASS, GRADE, SCHOOL, ALL + CLASS, + GRADE, + SCHOOL, + ALL } diff --git a/src/main/java/com/walkhub/walkhub/global/error/CustomAuthenticationEntryPoint.java b/src/main/java/com/walkhub/walkhub/global/error/CustomAuthenticationEntryPoint.java new file mode 100644 index 000000000..cb8b0346f --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/global/error/CustomAuthenticationEntryPoint.java @@ -0,0 +1,36 @@ +package com.walkhub.walkhub.global.error; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.walkhub.walkhub.global.error.exception.ErrorCode; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@RequiredArgsConstructor +@Component +public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { + + private final ObjectMapper objectMapper; + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { + ErrorCode errorCode = ErrorCode.FORBIDDEN; + String errorResponseJson = objectMapper.writeValueAsString( + ErrorResponse.builder() + .status(errorCode.getStatus()) + .code(errorCode.getCode()) + .message(errorCode.getMessage()) + .build()); + + response.setStatus(errorCode.getStatus()); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.getWriter().write(errorResponseJson); + } + +} diff --git a/src/main/java/com/walkhub/walkhub/global/error/ExceptionFilter.java b/src/main/java/com/walkhub/walkhub/global/error/ExceptionFilter.java index 939ca6510..a89661f34 100644 --- a/src/main/java/com/walkhub/walkhub/global/error/ExceptionFilter.java +++ b/src/main/java/com/walkhub/walkhub/global/error/ExceptionFilter.java @@ -4,6 +4,7 @@ import com.walkhub.walkhub.global.error.exception.ErrorCode; import com.walkhub.walkhub.global.error.exception.WalkhubException; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; @@ -11,6 +12,7 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; +@Slf4j @RequiredArgsConstructor public class ExceptionFilter extends OncePerRequestFilter { @@ -23,6 +25,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } catch (WalkhubException e) { sendErrorMessage(response, e.getErrorCode()); } catch (Exception e) { + logger.error(e); sendErrorMessage(response, ErrorCode.INTERNAL_SERVER_ERROR); } } diff --git a/src/main/java/com/walkhub/walkhub/global/error/exception/ErrorCode.java b/src/main/java/com/walkhub/walkhub/global/error/exception/ErrorCode.java index a1f1a9471..6645f6060 100644 --- a/src/main/java/com/walkhub/walkhub/global/error/exception/ErrorCode.java +++ b/src/main/java/com/walkhub/walkhub/global/error/exception/ErrorCode.java @@ -18,11 +18,11 @@ public enum ErrorCode { EXPIRED_JWT(401, "COMMON-401-1", "Expired Jwt"), INVALID_JWT(401, "COMMON-401-2", "Invalid Jwt"), UNAUTHORIZED_USER_AUTH_CODE(401, "USER-401-1", "Unauthorized User AuthCode"), - CLASS_CODE_NOT_MATCH(401, "USER-401-2", "Class Code Not Match"), INVALID_SCOPE(401, "CHALLENGE-401-1", "Invalid Scope"), PASSWORD_MISMATCH(401, "AUTH-401-1", "Password Mismatch"), INVALID_ROLE(401, "GLOBAL-401-1", "Invalid Role"), - INVALID_VERIFICATION_CODE(401, "GLOBAL-401-2", "Invalid Verification Code"), + + FORBIDDEN(403, "COMMON-403-1", "Forbidden"), USER_NOT_FOUND(404, "USER-404-1", "User Not Found"), USER_AUTH_CODE_NOT_FOUND(404, "USER-404-2", "User AuthCode Not Found"), @@ -30,8 +30,10 @@ public enum ErrorCode { SCHOOL_NOT_FOUND(404, "USER-404-4", "School Not Found"), SECTION_NOT_FOUND(404, "SECTION-404-1", "Section Not Found"), REFRESH_TOKEN_NOT_FOUND(404, "AUTH-404-1", "Refresh Token Not Found"), - + PHONE_NUMBER_NOT_FOUND(404, "USER-404-5", "PhoneNumber Not Found"), + VERIFICATION_CODE_NOT_FOUND(404, "TEACHER-404-1", "Verification Code Not Found"), CHALLENGE_NOT_FOUND(404, "CHALLENGE-404-1", "Challenge Not Found"), + ROOT_USER_NOT_FOUND(404, "USER-404-6", "Root User Not Found."), EXERCISE_NOT_FOUND(404, "EXERCISE-404-1", "Exercise Not Found"), EXERCISE_ANALYSIS_NOT_FOUND(404, "EXERCISE-404-2", "Exercise Analysis Not Found"), @@ -49,9 +51,12 @@ public enum ErrorCode { ALREADY_CREATED(409, "SECTION-409-1", "Already Created"), ALREADY_JOINED(409, "USER-409-2", "Already Joined"), ALREADY_PARTICIPATED(409, "CHALLENGE-409-1", "Already Participated"), + ALREADY_EXERCISING(409, "EXERCISE-409-1", "Already Exercising"), REDIS_TRANSACTION_EXCEPTION(500, "REDIS-500-1", "Cannot Read Cache From Redis"), - INTERNAL_SERVER_ERROR(500, "SERVER-500-1", "Internal Server Error"); + INTERNAL_SERVER_ERROR(500, "SERVER-500-1", "Internal Server Error"), + + DEFAULT_TITLE_BADGE_NOT_FOUND(503, "SERVER-503-1", "Contact The Server Developer"); private final int status; private final String code; diff --git a/src/main/java/com/walkhub/walkhub/global/exception/InvalidVerificationCodeException.java b/src/main/java/com/walkhub/walkhub/global/exception/InvalidVerificationCodeException.java deleted file mode 100644 index bf77f357f..000000000 --- a/src/main/java/com/walkhub/walkhub/global/exception/InvalidVerificationCodeException.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.walkhub.walkhub.global.exception; - -import com.walkhub.walkhub.global.error.exception.ErrorCode; -import com.walkhub.walkhub.global.error.exception.WalkhubException; - -public class InvalidVerificationCodeException extends WalkhubException { - - public static final WalkhubException EXCEPTION = - new InvalidVerificationCodeException(); - - private InvalidVerificationCodeException() { - super(ErrorCode.INVALID_VERIFICATION_CODE); - } -} diff --git a/src/main/java/com/walkhub/walkhub/global/exception/VerificationCodeNotFoundException.java b/src/main/java/com/walkhub/walkhub/global/exception/VerificationCodeNotFoundException.java new file mode 100644 index 000000000..35739b166 --- /dev/null +++ b/src/main/java/com/walkhub/walkhub/global/exception/VerificationCodeNotFoundException.java @@ -0,0 +1,14 @@ +package com.walkhub.walkhub.global.exception; + +import com.walkhub.walkhub.global.error.exception.ErrorCode; +import com.walkhub.walkhub.global.error.exception.WalkhubException; + +public class VerificationCodeNotFoundException extends WalkhubException { + + public static final WalkhubException EXCEPTION = + new VerificationCodeNotFoundException(); + + private VerificationCodeNotFoundException() { + super(ErrorCode.VERIFICATION_CODE_NOT_FOUND); + } +} diff --git a/src/main/java/com/walkhub/walkhub/global/security/SecurityConfig.java b/src/main/java/com/walkhub/walkhub/global/security/SecurityConfig.java index 5dea90cd6..dc709ae38 100644 --- a/src/main/java/com/walkhub/walkhub/global/security/SecurityConfig.java +++ b/src/main/java/com/walkhub/walkhub/global/security/SecurityConfig.java @@ -1,6 +1,7 @@ package com.walkhub.walkhub.global.security; import com.fasterxml.jackson.databind.ObjectMapper; +import com.walkhub.walkhub.global.error.CustomAuthenticationEntryPoint; import com.walkhub.walkhub.global.security.jwt.JwtTokenProvider; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; @@ -51,11 +52,16 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers(HttpMethod.GET, "/users/{user-id}").authenticated() .antMatchers(HttpMethod.GET, "/users").authenticated() .antMatchers(HttpMethod.PATCH, "/users").authenticated() - .antMatchers(HttpMethod.POST,"/users/classes/{section-id}").authenticated() + .antMatchers(HttpMethod.POST, "/users/classes").hasAuthority("USER") + .antMatchers(HttpMethod.DELETE, "/users/classes").hasAuthority("USER") .antMatchers(HttpMethod.GET, "/users/accounts/{phone-number}").permitAll() - .antMatchers(HttpMethod.PATCH, "/users/healths").authenticated() + .antMatchers(HttpMethod.PATCH, "/users/health").authenticated() .antMatchers(HttpMethod.PATCH, "/users/goal").authenticated() - .antMatchers(HttpMethod.PATCH, "/users/school").authenticated() + .antMatchers(HttpMethod.PATCH, "/users/schools").authenticated() + + //levels + .antMatchers(HttpMethod.GET, "/levels/lists").authenticated() + .antMatchers(HttpMethod.PATCH, "/levels/{level-id}").authenticated() // badges .antMatchers(HttpMethod.GET, "/badges/{user-id}").authenticated() @@ -70,11 +76,13 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers(HttpMethod.POST, "/exercises/locations/{exercise-id}").authenticated() .antMatchers(HttpMethod.GET, "/exercises/analysis").authenticated() .antMatchers(HttpMethod.GET, "/exercises/lists").authenticated() + .antMatchers(HttpMethod.GET, "/exercises/{exercise-id}").hasAnyAuthority("TEACHER", "ROOT") + .antMatchers(HttpMethod.GET, "/exercises/users/lists").hasAnyAuthority("USER", "TEACHER") // notices .antMatchers(HttpMethod.GET, "/notices/list").authenticated() - .antMatchers(HttpMethod.POST, "/notices").hasAnyAuthority("TEACHER", "ROOT", "SU") - .antMatchers(HttpMethod.DELETE, "/notices/{notice-id}").hasAnyAuthority("TEACHER", "ROOT", "SU") + .antMatchers(HttpMethod.POST, "/notices").hasAnyAuthority("ROOT", "SU") + .antMatchers(HttpMethod.DELETE, "/notices/{notice-id}").hasAnyAuthority("ROOT", "SU") // notifications .antMatchers(HttpMethod.GET, "/notifications").authenticated() @@ -87,49 +95,59 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers(HttpMethod.GET, "/ranks/schools/search").authenticated() .antMatchers(HttpMethod.GET, "/ranks/users/{school-id}").authenticated() .antMatchers(HttpMethod.GET, "/ranks/users/my-school").authenticated() - .antMatchers(HttpMethod.GET, "/ranks/users/search").authenticated() + .antMatchers(HttpMethod.GET, "/ranks/users/search/{school-id}").authenticated() // challenges .antMatchers(HttpMethod.POST, "/challenges").hasAnyAuthority("TEACHER", "ROOT", "SU") .antMatchers(HttpMethod.PATCH, "/challenges/{challenge-id}").hasAnyAuthority("TEACHER", "ROOT", "SU") .antMatchers(HttpMethod.DELETE, "/challenges/{challenge-id}").hasAnyAuthority("TEACHER", "ROOT", "SU") - .antMatchers(HttpMethod.GET, "/challenges/list").authenticated() + .antMatchers(HttpMethod.GET, "/challenges/{challenge-id}/progress").hasAnyAuthority("TEACHER", "ROOT") .antMatchers(HttpMethod.GET, "/challenges/{challenge-id}").authenticated() .antMatchers(HttpMethod.POST, "/challenges/{challenge-id}").authenticated() - .antMatchers(HttpMethod.GET, "/challenges/participated").authenticated() .antMatchers(HttpMethod.GET, "/challenges/{challenge-id}/participants/students").authenticated() .antMatchers(HttpMethod.GET, "/challenges/{challenge-id}/participants/teachers").hasAnyAuthority("TEACHER", "ROOT", "SU") + .antMatchers(HttpMethod.GET, "/challenges/list").authenticated() + .antMatchers(HttpMethod.GET, "/challenges/lists/closed").authenticated() + .antMatchers(HttpMethod.GET, "/challenges/participated").authenticated() // images .antMatchers(HttpMethod.POST, "/images").permitAll() // schools - .antMatchers(HttpMethod.PATCH, "/schools/logos/{school-id}").hasAuthority("ROOT") - .antMatchers(HttpMethod.GET, "/schools/search").authenticated() + .antMatchers(HttpMethod.PATCH, "/schools/logos").hasAuthority("ROOT") + .antMatchers(HttpMethod.GET, "/schools/search").permitAll() // teachers .antMatchers(HttpMethod.POST, "/teachers/verification-codes").hasAuthority("ROOT") .antMatchers(HttpMethod.PATCH, "/teachers/verification-codes").hasAuthority("USER") + .antMatchers(HttpMethod.GET, "/teachers/users/search").hasAnyAuthority("TEACHER", "ROOT") + .antMatchers(HttpMethod.GET, "/teachers/users").hasAnyAuthority("TEACHER", "ROOT") .antMatchers(HttpMethod.POST, "/teachers/classes").hasAnyAuthority("TEACHER", "ROOT") + .antMatchers(HttpMethod.GET, "/teachers/classes/lists").hasAnyAuthority("TEACHER", "ROOT") .antMatchers(HttpMethod.DELETE, "/teachers/classes/{section-id}").hasAnyAuthority("TEACHER", "ROOT") - .antMatchers(HttpMethod.GET, "/teachers/classes").hasAnyAuthority("TEACHER") - .antMatchers(HttpMethod.GET,"/teachers/{user-id}").hasAnyAuthority("TEACHER") - .antMatchers(HttpMethod.GET,"/teachers/users").hasAnyAuthority("TEACHER","ROOT") - .antMatchers(HttpMethod.GET,"/teachers/students/verification-codes").hasAnyAuthority("TEACHER") - .antMatchers(HttpMethod.PATCH, "/teachers/classes/verification-codes").hasAuthority("TEACHER") + .antMatchers(HttpMethod.GET, "/teachers/classes").hasAuthority("TEACHER") + .antMatchers(HttpMethod.GET, "/teachers/users/{user-id}").hasAnyAuthority("TEACHER") + .antMatchers(HttpMethod.GET, "/teachers/users").hasAnyAuthority("TEACHER", "ROOT") + .antMatchers(HttpMethod.PATCH, "/teachers/schools").hasAuthority("TEACHER") + .antMatchers(HttpMethod.GET, "/teachers/students/verification-codes").hasAnyAuthority("TEACHER") + .antMatchers(HttpMethod.PATCH, "/teachers/classes/verification-codes").hasAuthority("TEACHER") // su - .antMatchers(HttpMethod.GET,"/su").hasAuthority("SU") - .antMatchers(HttpMethod.POST,"/su/accounts/{school-id}").hasAuthority("SU") + .antMatchers(HttpMethod.GET, "/su").hasAuthority("SU") + .antMatchers(HttpMethod.POST, "/su/accounts/{school-id}").hasAuthority("SU") + .antMatchers(HttpMethod.PATCH, "/su/accounts/{school-id}").hasAuthority("SU") //excel - .antMatchers(HttpMethod.GET,"/excel").hasAnyAuthority("TEACHER", "ROOT") + .antMatchers(HttpMethod.GET, "/excel").hasAnyAuthority("TEACHER", "ROOT") // socket.io .antMatchers(HttpMethod.GET, "/socket.io").authenticated() .anyRequest().denyAll() + .and() + .exceptionHandling().authenticationEntryPoint(new CustomAuthenticationEntryPoint(objectMapper)) + .and() .apply(new FilterConfig(jwtTokenProvider, objectMapper)); } diff --git a/src/main/java/com/walkhub/walkhub/global/security/jwt/JwtTokenProvider.java b/src/main/java/com/walkhub/walkhub/global/security/jwt/JwtTokenProvider.java index 3662b066e..fbaa4bfb8 100644 --- a/src/main/java/com/walkhub/walkhub/global/security/jwt/JwtTokenProvider.java +++ b/src/main/java/com/walkhub/walkhub/global/security/jwt/JwtTokenProvider.java @@ -1,5 +1,7 @@ package com.walkhub.walkhub.global.security.jwt; +import com.walkhub.walkhub.domain.auth.domain.RefreshToken; +import com.walkhub.walkhub.domain.auth.domain.repository.RefreshTokenRepository; import com.walkhub.walkhub.global.exception.ExpiredJwtException; import com.walkhub.walkhub.global.exception.InvalidJwtException; import com.walkhub.walkhub.global.security.auth.AuthDetailsService; @@ -21,13 +23,21 @@ public class JwtTokenProvider { private final JwtProperties jwtProperties; private final AuthDetailsService authDetailsService; + private final RefreshTokenRepository refreshTokenRepository; public String generateAccessToken(String id) { return generateToken(id, "access", jwtProperties.getAccessExp()); } public String generateRefreshToken(String id) { - return generateToken(id, "refresh", jwtProperties.getRefreshExp()); + String refreshToken = generateToken(id, "refresh", jwtProperties.getRefreshExp()); + refreshTokenRepository.save(RefreshToken.builder() + .accountId(id) + .token(refreshToken) + .timeToLive(jwtProperties.getRefreshExp()) + .build()); + + return refreshToken; } private String generateToken(String id, String type, Long exp) { @@ -36,7 +46,7 @@ private String generateToken(String id, String type, Long exp) { .setSubject(id) .claim("type", type) .setIssuedAt(new Date()) - .setExpiration(new Date(System.currentTimeMillis() + exp)) + .setExpiration(new Date(System.currentTimeMillis() + exp * 1000)) .compact(); } diff --git a/src/main/java/com/walkhub/walkhub/global/web/WebMvcConfig.java b/src/main/java/com/walkhub/walkhub/global/web/WebMvcConfig.java index fecfc7d49..045cc4e8d 100644 --- a/src/main/java/com/walkhub/walkhub/global/web/WebMvcConfig.java +++ b/src/main/java/com/walkhub/walkhub/global/web/WebMvcConfig.java @@ -11,6 +11,7 @@ public class WebMvcConfig implements WebMvcConfigurer { public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") + .allowedMethods("*") .allowedHeaders("*"); } } diff --git a/src/main/java/com/walkhub/walkhub/global/websocket/connect/WebSocketJwtHandler.java b/src/main/java/com/walkhub/walkhub/global/websocket/connect/WebSocketJwtHandler.java index 7671d86e2..a4ecf7a7e 100644 --- a/src/main/java/com/walkhub/walkhub/global/websocket/connect/WebSocketJwtHandler.java +++ b/src/main/java/com/walkhub/walkhub/global/websocket/connect/WebSocketJwtHandler.java @@ -6,12 +6,14 @@ import com.walkhub.walkhub.global.security.jwt.JwtTokenProvider; import com.walkhub.walkhub.global.websocket.property.ClientProperty; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +@Slf4j @RequiredArgsConstructor @RestController public class WebSocketJwtHandler { @@ -26,11 +28,13 @@ public void onConnect(SocketIOClient client) { Authentication authentication = jwtTokenProvider.authentication(token); socketIOClientMap.put(authentication.getName(), client); client.set(ClientProperty.USER_KEY, authentication.getName()); + log.info("Connected : " + client.getSessionId() + ", " + authentication.getName()); } @OnDisconnect public void onDisconnect(SocketIOClient client) { socketIOClientMap.remove(client.get(ClientProperty.USER_KEY).toString()); + log.info("DisConnected : " + client.getSessionId()); } }