Skip to content

Commit

Permalink
Merge pull request #45 from 9oormDari/develop
Browse files Browse the repository at this point in the history
feat: ๋งˆ์ดํŽ˜์ด์ง€ ๋ชฉํ‘œ ์™„๋ฃŒ ๊ธฐ๋ก
  • Loading branch information
T-ferret authored Sep 28, 2024
2 parents 97168e2 + d114242 commit f120be6
Show file tree
Hide file tree
Showing 17 changed files with 502 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package com.goormdari.domain.history.application;

import com.goormdari.domain.history.domain.History;
import com.goormdari.domain.history.domain.dto.response.HistoryResponse;
import com.goormdari.domain.history.domain.repository.HistoryRepository;
import com.goormdari.domain.history.exception.ResourceNotFoundException;
import com.goormdari.domain.routine.domain.Routine;
import com.goormdari.domain.routine.domain.repository.RoutineRepository;
import com.goormdari.domain.team.domain.Team;
import com.goormdari.domain.team.domain.repository.TeamRepository;
import com.goormdari.domain.user.domain.User;
import com.goormdari.domain.user.domain.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Transactional
public class HistoryService {

private final HistoryRepository historyRepository;
private final TeamRepository teamRepository;
private final RoutineRepository routineRepository;
private final UserRepository userRepository;

// ์ตœ์ดˆ ๊ธฐ๋ก ์ƒ์„ฑ
public void createHistory(Long userId, Long teamId, boolean isSuccess) {
// ํŒ€ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
Team team = teamRepository.findById(teamId)
.orElseThrow(() -> new ResourceNotFoundException("Team " + teamId + " not found"));

// ํŒ€์˜ ์‹œ์ž‘์ผ๊ณผ ๋ฐ๋“œ๋ผ์ธ
LocalDate startDate = team.getCreatedAt().toLocalDate();
LocalDate endDate = team.getDeadLine();

// ์ค‘๋ณต ํ™•์ธ: ํ•ด๋‹น ์œ ์ €์™€ ๊ธฐ๊ฐ„(์‹œ์ž‘์ผ ~ ๋ฐ๋“œ๋ผ์ธ)์— ๋Œ€ํ•œ ๊ธฐ๋ก์ด ์ด๋ฏธ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ
boolean historyExists = historyRepository.existsByUserIdAndDateRange(userId, startDate, endDate);

if (historyExists) {
// ์ด๋ฏธ ๊ธฐ๋ก์ด ์กด์žฌํ•˜๋ฉด ๋” ์ด์ƒ ์ƒ์„ฑํ•˜์ง€ ์•Š์Œ
return;
}

// ์œ ์ € ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
User user = userRepository.findById(userId)
.orElseThrow(() -> new ResourceNotFoundException("User " + userId + " not found"));

// ํžˆ์Šคํ† ๋ฆฌ ์ƒ์„ฑ ๋ฐ ์ €์žฅ
History history = History.builder()
.goal(team.getGoal())
.routine1(team.getRoutine1())
.routine2(team.getRoutine2())
.routine3(team.getRoutine3())
.routine4(team.getRoutine4())
.user(user) // ์œ ์ € ์ •๋ณด ํ• ๋‹น
.isSuccess(isSuccess) // 70% ์ด์ƒ ์„ฑ๊ณต ์—ฌ๋ถ€ ๋ฐ˜์˜
.build();
historyRepository.save(history);
}


@Scheduled(cron = "0 0 0 * * *") // ๋งค์ผ ์ž์ • ์‹คํ–‰
public void checkTeamGoalsAndCreateHistory() {
LocalDate today = LocalDate.now();
List<Team> expiredTeams = teamRepository.findAllByDeadLineBefore(today);

for (Team team : expiredTeams) {
List<User> teamMembers = userRepository.findAllByTeamId(team.getId());

for (User user : teamMembers) {
// ๋ชฉํ‘œ ๋‹ฌ์„ฑ ์—ฌ๋ถ€ ๊ณ„์‚ฐ
boolean isGoalAchieved = calculateGoalAchievement(user.getId(), team.getId());

// ์ค‘๋ณต ๊ธฐ๋ก ์ƒ์„ฑ ๋ฐฉ์ง€ ์ฒ˜๋ฆฌ๋œ ํžˆ์Šคํ† ๋ฆฌ ์ƒ์„ฑ
createHistory(user.getId(), team.getId(), isGoalAchieved);
}
}
}

// D-Day ์ž์ •๋งˆ๋‹ค ์ดˆ๊ธฐํ™”
// ๋งค์ผ ์ž์ •์— ์‹คํ–‰ (cron ํ‘œํ˜„์‹: "0 0 0 * * *")
@Scheduled(cron = "0 0 0 * * *")
public void updateDDayPlus() {
// ๋ชจ๋“  ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์กฐํšŒ
List<History> histories = historyRepository.findAll();

for (History history : histories) {
// dDayPlus ๊ฐ’์„ 1์”ฉ ์ฆ๊ฐ€
history.incrementDDayPlus();
}
// ์—…๋ฐ์ดํŠธ๋œ ํžˆ์Šคํ† ๋ฆฌ ์ €์žฅ
historyRepository.saveAll(histories);
}

// 70% ์ด์ƒ ๋ฃจํ‹ด ์™„์ˆ˜ ์‹œ์— ์„ฑ๊ณต์œผ๋กœ ๊ณ„์‚ฐ
public boolean calculateGoalAchievement(Long userId, Long teamId) {
// ์œ ์ €์™€ ํŒ€ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
Team team = teamRepository.findById(teamId)
.orElseThrow(() -> new ResourceNotFoundException("Team " + teamId + " not found"));

// ํŒ€์˜ ์‹œ์ž‘์ผ๊ณผ ๋ฐ๋“œ๋ผ์ธ ๊ฐ€์ ธ์˜ค๊ธฐ
LocalDate startDate = team.getCreatedAt().toLocalDate();
LocalDate endDate = team.getDeadLine();

// ํ•ด๋‹น ๊ธฐ๊ฐ„ ๋‚ด์— ์œ ์ €๊ฐ€ ์ˆ˜ํ–‰ํ•œ ๋ฃจํ‹ด ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ
List<Routine> routines = routineRepository.findRoutinesByUserAndDateRange(userId, startDate, endDate);

// ์„ฑ๊ณตํ•œ ๋ฃจํ‹ด ๊ฐœ์ˆ˜ ๊ณ„์‚ฐ (์ด๋ฏธ์ง€๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์„ฑ๊ณตํ•œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ)
long successfulRoutines = routines.stream()
.filter(routine -> routine.getRoutineImg() != null && !routine.getRoutineImg().isEmpty())
.count();

// ์ „์ฒด ๋ฃจํ‹ด ๊ฐœ์ˆ˜ ๊ณ„์‚ฐ
long totalRoutines = routines.size();

// ์„ฑ๊ณต๋ฅ  ๊ณ„์‚ฐ (70% ์ด์ƒ ์„ฑ๊ณต ์‹œ ๋ชฉํ‘œ ๋‹ฌ์„ฑ)
if (totalRoutines > 0) {
double successRate = (double) successfulRoutines / totalRoutines;
return successRate >= 0.7;
}
return false; // ๋ฃจํ‹ด์ด ์—†๋Š” ๊ฒฝ์šฐ ์‹คํŒจ๋กœ ๊ฐ„์ฃผ
}

// ํŠน์ • ์œ ์ €์˜ ํžˆ์Šคํ† ๋ฆฌ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ
public List<HistoryResponse> getAllHistoriesByUser(Long userId) {
List<History> histories = historyRepository.findAllByUserId(userId);

return histories.stream().map(history -> {
String[] routines = {
history.getRoutine1(),
history.getRoutine2(),
history.getRoutine3(),
history.getRoutine4()
};

int dDay = history.getDDayPlus();
String result = history.getIsSuccess() ? "์„ฑ๊ณต" : "์‹คํŒจ";

return new HistoryResponse(dDay, history.getGoal(), routines, result);
}).collect(Collectors.toList());
}
}
35 changes: 28 additions & 7 deletions src/main/java/com/goormdari/domain/history/domain/History.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.goormdari.domain.history.domain;

import com.goormdari.domain.common.BaseEntity;
import com.goormdari.domain.team.domain.Team;
import com.goormdari.domain.user.domain.User;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.*;
import org.springframework.data.annotation.CreatedDate;

import java.time.LocalDateTime;

@Entity
@Getter
Expand Down Expand Up @@ -37,8 +37,29 @@ public class History extends BaseEntity {
@Column(name = "is_success")
private Boolean isSuccess;

@CreatedDate
private LocalDateTime createAt;

private int dDayPlus;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
@JoinColumn(name = "user_id") // user_id๋กœ User์™€ ์—ฐ๊ฒฐ
private User user;

@Builder
public History(String goal, String routine1, String routine2, String routine3, String routine4, User user, Boolean isSuccess, int dDayPlus) {
this.goal = goal;
this.routine1 = routine1;
this.routine2 = routine2;
this.routine3 = routine3;
this.routine4 = routine4;
this.user = user;
this.isSuccess = isSuccess;
this.dDayPlus = dDayPlus;
}

public void incrementDDayPlus() {
this.dDayPlus++; // dDayPlus ์ฆ๊ฐ€
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.goormdari.domain.history.domain.dto.request;

import lombok.Builder;

import java.util.List;

@Builder
public record CreateHistoryRequest(
String goal,
String routine1,
String routine2,
String routine3,
String routine4,
List<Long> routineIds
// Long teamId
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.goormdari.domain.history.domain.dto.response;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class HistoryResponse {
private int dDay;
private String goal;
private String[] routineList;
private String result;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.goormdari.domain.history.domain.repository;

import com.goormdari.domain.history.domain.History;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.time.LocalDate;
import java.util.List;

public interface HistoryRepository extends JpaRepository<History, Long> {

// ํŠน์ • ์œ ์ €์™€ ๊ธฐ๊ฐ„(์‹œ์ž‘์ผ~๋ฐ๋“œ๋ผ์ธ)์— ๋Œ€ํ•œ ๊ธฐ๋ก์ด ์žˆ๋Š”์ง€ ํ™•์ธ
@Query("SELECT COUNT(h) > 0 FROM History h WHERE h.user.id = :userId AND DATE(h.createdAt) BETWEEN :startDate AND :endDate")
boolean existsByUserIdAndDateRange(@Param("userId") Long userId,
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate);

List<History> findAllByUserId(Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.goormdari.domain.history.exception;

public class ResourceNotFoundException extends RuntimeException {

public ResourceNotFoundException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.goormdari.domain.history.presentation;

import com.goormdari.domain.calendar.exception.InvalidTokenException;
import com.goormdari.domain.history.application.HistoryService;
import com.goormdari.domain.history.domain.dto.response.HistoryResponse;
import com.goormdari.global.config.security.jwt.JWTUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/histories")
@RequiredArgsConstructor
public class HistoryController {

private final HistoryService historyService;
private final JWTUtil jwtUtil;

@Operation(summary = "๋ฃจํ‹ด ์™„์ˆ˜ ๊ธฐ๋ก ํ™”๋ฉด ์กฐํšŒ", description = "๋ฃจํ‹ด ์™„์ˆ˜ ๊ธฐ๋ก์„ list ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™ฅํ•ฉ๋‹ˆ๋‹ค.")
@GetMapping
public ResponseEntity<List<HistoryResponse>> getHistoriesByUser(@Parameter(description = "Accesstoken์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", required = true) @RequestHeader("Authorization") String token) {
if (token == null) {
throw new InvalidTokenException();
}

String jwt = token.startsWith("Bearer ") ? token.substring(7) : token;
if (!jwtUtil.validateToken(jwt)) {
throw new IllegalArgumentException("Invalid token");
}
Long userId = jwtUtil.extractId(jwt);

List<HistoryResponse> histories = historyService.getAllHistoriesByUser(userId);
return ResponseEntity.ok(histories);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import com.goormdari.domain.routine.domain.Routine;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

Expand All @@ -13,4 +15,10 @@ public interface RoutineRepository extends JpaRepository<Routine, Long> {

@Query("SELECT r FROM Routine r WHERE r.user.id = :userId")
List<Routine> findAllByUserId(Long userId);

// ์œ ์ €์™€ ํŒ€์˜ ์‹œ์ž‘์ผ~๋ฐ๋“œ๋ผ์ธ ์‚ฌ์ด์— ํ•ด๋‹นํ•˜๋Š” ๋ฃจํ‹ด ๊ฒ€์ƒ‰
@Query("SELECT r FROM Routine r WHERE r.user.id = :userId AND DATE(r.createdAt) BETWEEN :startDate AND :endDate")
List<Routine> findRoutinesByUserAndDateRange(@Param("userId") Long userId,
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import com.goormdari.domain.team.domain.Team;
import org.springframework.data.jpa.repository.JpaRepository;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

public interface TeamRepository extends JpaRepository<Team, Long> {
Optional<Team> findByJoinCode(String joinCode);
List<Team> findAllByDeadLineBefore(LocalDate today);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.goormdari.domain.user.domain.dto;
package com.goormdari.domain.user.domain.dto.request;

import jakarta.validation.constraints.NotBlank;
import lombok.*;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.goormdari.domain.user.domain.dto;
package com.goormdari.domain.user.domain.dto.request;

import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.goormdari.domain.user.domain.dto;
package com.goormdari.domain.user.domain.dto.response;

import lombok.AllArgsConstructor;
import lombok.Getter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
public interface UserRepository extends JpaRepository<User, Long> {

Optional<User> findByUsername(String username);
List<User> findAllByTeamId(Long teamId);

@Query("SELECT t.joinCode FROM User u JOIN u.team t WHERE u.id = :userId")
String findJoinCodeByUserId(Long userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import com.goormdari.domain.team.domain.repository.TeamRepository;
import com.goormdari.domain.user.domain.dto.response.findCurrentStepResponse;
import com.goormdari.domain.user.domain.User;
import com.goormdari.domain.user.domain.dto.AddUserRequest;
import com.goormdari.domain.user.domain.dto.JwtResponse;
import com.goormdari.domain.user.domain.dto.LoginRequest;
import com.goormdari.domain.user.domain.dto.request.AddUserRequest;
import com.goormdari.domain.user.domain.dto.response.JwtResponse;
import com.goormdari.domain.user.domain.dto.request.LoginRequest;
import com.goormdari.domain.user.domain.repository.UserRepository;
import com.goormdari.global.config.security.jwt.JWTUtil;
import jakarta.transaction.Transactional;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.goormdari.domain.user.presentation;

import com.goormdari.domain.user.domain.dto.AddUserRequest;
import com.goormdari.domain.user.domain.dto.JwtResponse;
import com.goormdari.domain.user.domain.dto.LoginRequest;
import com.goormdari.domain.user.domain.dto.request.AddUserRequest;
import com.goormdari.domain.user.domain.dto.response.JwtResponse;
import com.goormdari.domain.user.domain.dto.request.LoginRequest;
import com.goormdari.domain.user.domain.service.UserService;

import jakarta.validation.Valid;
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/goormdari/global/config/SchedulingConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.goormdari.global.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@EnableScheduling
public class SchedulingConfig {
// ์Šค์ผ€์ค„๋ง ํ™œ์„ฑํ™”
}
Loading

0 comments on commit f120be6

Please sign in to comment.