Skip to content

Commit 4159399

Browse files
authored
feat: 가족 걸음 수 저장 및 조회 기능 추가 (#9)
* feat: 가족 걸음 수 저장 및 조회 기능 추가 * feat: 가족 걸음 수 저장 및 조회 기능 추가 * test: 컨트롤러별 단위 테스트 추가 (Cursor) (#13) * 파일 끝 빈 줄 추가
1 parent 5fd0b53 commit 4159399

File tree

10 files changed

+899
-0
lines changed

10 files changed

+899
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package ongi.step.controller;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import ongi.security.CustomUserDetails;
5+
import ongi.step.dto.FamilyStepResponse;
6+
import ongi.step.dto.StepUpsertRequest;
7+
import ongi.step.service.StepService;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
10+
import org.springframework.web.bind.annotation.*;
11+
12+
import jakarta.validation.Valid;
13+
import java.util.UUID;
14+
15+
@RestController
16+
@RequestMapping("/steps")
17+
@RequiredArgsConstructor
18+
public class StepController {
19+
20+
private final StepService stepService;
21+
22+
@PostMapping
23+
public ResponseEntity<Void> upsertStep(
24+
@Valid @RequestBody StepUpsertRequest request,
25+
@AuthenticationPrincipal CustomUserDetails userDetails
26+
) {
27+
UUID userId = userDetails.getUser().getUuid();
28+
stepService.upsertStep(userId, request);
29+
return ResponseEntity.ok().build();
30+
}
31+
32+
@GetMapping
33+
public ResponseEntity<FamilyStepResponse> getFamilySteps(
34+
@AuthenticationPrincipal CustomUserDetails userDetails
35+
) {
36+
UUID userId = userDetails.getUser().getUuid();
37+
FamilyStepResponse response = stepService.getFamilySteps(userId);
38+
return ResponseEntity.ok(response);
39+
}
40+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package ongi.step.dto;
2+
3+
import java.time.LocalDate;
4+
import java.util.List;
5+
import java.util.UUID;
6+
7+
public record FamilyStepResponse(
8+
Integer totalSteps,
9+
LocalDate date,
10+
List<MemberStepInfo> memberSteps
11+
) {
12+
13+
public record MemberStepInfo(
14+
UUID userId,
15+
String userName,
16+
Integer steps
17+
) {
18+
}
19+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package ongi.step.dto;
2+
3+
import jakarta.validation.constraints.Min;
4+
import jakarta.validation.constraints.NotNull;
5+
6+
public record StepUpsertRequest(
7+
@NotNull(message = "걸음 수는 필수입니다.")
8+
@Min(value = 0, message = "걸음 수는 0 이상이어야 합니다.")
9+
Integer steps
10+
) {
11+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package ongi.step.entity;
2+
3+
import jakarta.persistence.*;
4+
import lombok.*;
5+
import org.springframework.data.annotation.CreatedDate;
6+
import org.springframework.data.annotation.LastModifiedDate;
7+
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
8+
9+
import java.time.LocalDate;
10+
import java.time.LocalDateTime;
11+
import java.util.UUID;
12+
13+
@Entity
14+
@Table(uniqueConstraints = {
15+
@UniqueConstraint(columnNames = {"family_id", "user_id", "date"})
16+
})
17+
@AllArgsConstructor
18+
@NoArgsConstructor
19+
@Builder
20+
@Getter
21+
@EntityListeners(AuditingEntityListener.class)
22+
public class Step {
23+
24+
@Id
25+
@GeneratedValue
26+
@Column(nullable = false, updatable = false)
27+
private Long id;
28+
29+
@Column(nullable = false)
30+
private String familyId;
31+
32+
@Column(nullable = false)
33+
private UUID userId;
34+
35+
@Column(nullable = false)
36+
private Integer steps;
37+
38+
@Column(nullable = false)
39+
private LocalDate date;
40+
41+
@CreatedDate
42+
@Column(updatable = false, nullable = false)
43+
private LocalDateTime createdAt;
44+
45+
@LastModifiedDate
46+
@Column(nullable = false)
47+
private LocalDateTime updatedAt;
48+
49+
public void updateSteps(Integer newSteps) {
50+
this.steps = newSteps;
51+
}
52+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package ongi.step.repository;
2+
3+
import ongi.step.entity.Step;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.stereotype.Repository;
6+
7+
import java.time.LocalDate;
8+
import java.util.List;
9+
import java.util.Optional;
10+
import java.util.UUID;
11+
12+
@Repository
13+
public interface StepRepository extends JpaRepository<Step, Long> {
14+
15+
List<Step> findByFamilyIdAndDate(String familyId, LocalDate date);
16+
17+
Optional<Step> findByFamilyIdAndUserIdAndDate(String familyId, UUID userId, LocalDate date);
18+
19+
Integer getTotalStepsByFamilyIdAndDate(String familyId, LocalDate date);
20+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package ongi.step.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import ongi.exception.EntityNotFoundException;
5+
import ongi.family.entity.Family;
6+
import ongi.family.repository.FamilyRepository;
7+
import ongi.step.dto.FamilyStepResponse;
8+
import ongi.step.dto.StepUpsertRequest;
9+
import ongi.step.entity.Step;
10+
import ongi.step.repository.StepRepository;
11+
import ongi.user.entity.User;
12+
import ongi.user.repository.UserRepository;
13+
import org.springframework.stereotype.Service;
14+
import org.springframework.transaction.annotation.Transactional;
15+
16+
import java.time.LocalDate;
17+
import java.util.List;
18+
import java.util.Map;
19+
import java.util.UUID;
20+
import java.util.stream.Collectors;
21+
22+
@Service
23+
@RequiredArgsConstructor
24+
@Transactional(readOnly = true)
25+
public class StepService {
26+
27+
private final StepRepository stepRepository;
28+
private final FamilyRepository familyRepository;
29+
private final UserRepository userRepository;
30+
31+
@Transactional
32+
public void upsertStep(UUID userId, StepUpsertRequest request) {
33+
Family family = familyRepository.findByMembersContains(userId)
34+
.orElseThrow(() -> new EntityNotFoundException("가족을 찾을 수 없습니다."));
35+
36+
String familyId = family.getCode();
37+
LocalDate today = LocalDate.now();
38+
39+
var existingStep = stepRepository.findByFamilyIdAndUserIdAndDate(familyId, userId, today);
40+
41+
if (existingStep.isPresent()) {
42+
Step step = existingStep.get();
43+
step.updateSteps(request.steps());
44+
} else {
45+
Step newStep = Step.builder()
46+
.familyId(familyId)
47+
.userId(userId)
48+
.steps(request.steps())
49+
.date(today)
50+
.build();
51+
stepRepository.save(newStep);
52+
}
53+
}
54+
55+
public FamilyStepResponse getFamilySteps(UUID userId) {
56+
Family family = familyRepository.findByMembersContains(userId)
57+
.orElseThrow(() -> new EntityNotFoundException("가족을 찾을 수 없습니다."));
58+
59+
String familyId = family.getCode();
60+
LocalDate today = LocalDate.now();
61+
62+
List<Step> familySteps = stepRepository.findByFamilyIdAndDate(familyId, today);
63+
64+
Integer totalSteps = stepRepository.getTotalStepsByFamilyIdAndDate(familyId, today);
65+
66+
Map<UUID, Integer> userStepMap = familySteps.stream()
67+
.collect(Collectors.toMap(Step::getUserId, Step::getSteps));
68+
69+
List<UUID> memberIds = family.getMembers();
70+
Map<UUID, User> memberMap = userRepository.findAllById(memberIds).stream()
71+
.collect(Collectors.toMap(User::getUuid, user1 -> user1));
72+
73+
List<FamilyStepResponse.MemberStepInfo> memberSteps = memberIds.stream()
74+
.map(memberId -> {
75+
User member = memberMap.get(memberId);
76+
Integer steps = userStepMap.getOrDefault(memberId, 0);
77+
78+
return new FamilyStepResponse.MemberStepInfo(
79+
memberId,
80+
member.getName(),
81+
steps
82+
);
83+
})
84+
.collect(Collectors.toList());
85+
86+
return new FamilyStepResponse(
87+
totalSteps,
88+
today,
89+
memberSteps
90+
);
91+
}
92+
}

0 commit comments

Comments
 (0)