Skip to content

Commit 137e83b

Browse files
authored
Merge pull request #74 from Nexters/dev
동기부여 관련 신규 기능 개발
2 parents 96c77d3 + ca2aa09 commit 137e83b

File tree

14 files changed

+631
-2
lines changed

14 files changed

+631
-2
lines changed

src/main/java/donmani/donmani_server/expense/controller/ExpenseController.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212

1313
import donmani.donmani_server.common.httpStatus.HttpStatusDTO;
1414
import donmani.donmani_server.expense.service.ExpenseService;
15+
import donmani.donmani_server.feedback.service.FeedbackService;
1516
import lombok.RequiredArgsConstructor;
1617

1718
@RestController
1819
@RequiredArgsConstructor
1920
public class ExpenseController {
2021
private final ExpenseService expenseService;
22+
private final FeedbackService feedbackService;
2123

2224
@GetMapping("expenses/calendar/{userKey}")
2325
public ResponseEntity<ExpenseResponseDTO> getExpensesCalendar(
@@ -75,6 +77,7 @@ public ResponseEntity<Void> addExpense(@RequestBody ExpenseRequestDTO request) {
7577
public ResponseEntity<HttpStatusDTO<Void>> addExpenseV1(@RequestBody ExpenseRequestDTO request) {
7678
try {
7779
expenseService.addExpense(request);
80+
feedbackService.addFeedback(request);
7881

7982
// 1. 소비 기록 성공 -> 201
8083
return ResponseEntity.ok(HttpStatusDTO.response(HttpStatus.CREATED.value(), "성공", null));

src/main/java/donmani/donmani_server/expense/entity/Expense.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package donmani.donmani_server.expense.entity;
22

3+
import donmani.donmani_server.feedback.entity.Feedback;
34
import jakarta.persistence.*;
45
import lombok.*;
56
import java.time.LocalDateTime;
67

8+
import com.fasterxml.jackson.annotation.JsonManagedReference;
9+
710
@Entity
811
@Getter
912
@Setter
@@ -34,4 +37,12 @@ public class Expense {
3437
*/
3538
private LocalDateTime createdDate; // 서버에 기록된 일자
3639
private LocalDateTime updateDate;
40+
41+
/*
42+
- 2025.05.25
43+
- feedback 양방향 연관 관계 추가
44+
*/
45+
@OneToOne(mappedBy = "expense")
46+
@JsonManagedReference
47+
private Feedback feedback;
3748
}

src/main/java/donmani/donmani_server/expense/repository/ExpenseRepository.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,15 @@ public interface ExpenseRepository extends JpaRepository<Expense, Long> {
2222

2323
@Query("SELECT e FROM Expense e WHERE e.createdAt IN :localDateTimes ORDER BY e.createdAt DESC")
2424
List<Expense> findByCreatedAtIn(@Param("userId") Long userId, List<LocalDateTime> localDateTimes);
25+
26+
// 소비가 GOOD, BAD 모두 존재하는 경우 랜덤으로 하나만 피드백 카드 생성
27+
@Query("SELECT e FROM Expense e WHERE e.userId = :userId AND DATE_FORMAT(e.createdAt, '%Y%m%d') = DATE_FORMAT(:date, '%Y%m%d') ORDER BY RAND() LIMIT 1")
28+
Expense findExpenseByUserIdAndAndCreatedAt(Long userId, LocalDateTime date);
29+
30+
// @Query("SELECT DISTINCT e.createdAt FROM Expense e WHERE DATE_FORMAT(e.createdAt, '%Y%m%d') >= DATE_FORMAT('20250525', '%Y%m%d') AND e.userId = :userId")
31+
@Query("SELECT DISTINCT e.createdAt FROM Expense e WHERE e.userId = :userId")
32+
List<LocalDateTime> findTotalExpensesCount(Long userId);
33+
34+
Expense findExpenseById(Long id);
35+
2536
}

src/main/java/donmani/donmani_server/expense/service/ExpenseService.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
import donmani.donmani_server.expense.dto.*;
1313
import donmani.donmani_server.expense.entity.CategoryType;
1414
import donmani.donmani_server.expense.entity.FlagType;
15+
1516
import org.springframework.data.domain.Page;
1617
import org.springframework.data.domain.PageRequest;
1718
import org.springframework.stereotype.Service;
1819
import org.springframework.transaction.annotation.Transactional;
1920

2021
import donmani.donmani_server.expense.entity.Expense;
2122
import donmani.donmani_server.expense.repository.ExpenseRepository;
23+
import donmani.donmani_server.user.entity.User;
2224
import donmani.donmani_server.user.service.UserService;
2325
import jakarta.persistence.EntityNotFoundException;
2426
import lombok.RequiredArgsConstructor;
@@ -49,6 +51,7 @@ public void addExpense(ExpenseRequestDTO request) {
4951
.userId(userId)
5052
.createdAt(record.getDate().atStartOfDay())
5153
.createdDate(localDateTime)
54+
.updateDate(localDateTime)
5255
.build());
5356
}
5457

@@ -57,6 +60,7 @@ public void addExpense(ExpenseRequestDTO request) {
5760
.userId(userId)
5861
.createdAt(record.getDate().atStartOfDay())
5962
.createdDate(localDateTime)
63+
.updateDate(localDateTime)
6064
.flag(content.getFlag())
6165
.category(content.getCategory())
6266
.memo(content.getMemo())
@@ -224,4 +228,32 @@ public CategoryStatisticsDTO getCategoryStatistics(String userKey, int year, int
224228
.build();
225229
}
226230

231+
@Transactional(readOnly = true)
232+
public Expense getExpenseSubmitToday(Long userId, LocalDateTime date) {
233+
Expense expense = expenseRepository.findExpenseByUserIdAndAndCreatedAt(userId, date);
234+
235+
return expense;
236+
}
237+
238+
public Integer getTotalExpensesCount(String userKey) {
239+
// 1. 유저 정보 확인
240+
User user = userService.getUser(userKey);
241+
242+
// 오늘 기록된 소비 확인
243+
// ver 2.0.0 이후로 기록된 소비만
244+
LocalDateTime baseTime = LocalDateTime.of(2025, 5, 26, 0, 0); // 2025-05-30 00:00
245+
246+
List<LocalDateTime> createdAts = expenseRepository.findTotalExpensesCount(user.getId());
247+
248+
createdAts
249+
.stream()
250+
.filter(createdAt -> createdAt.isEqual(baseTime) || createdAt.isAfter(baseTime))
251+
.collect(Collectors.toList());
252+
253+
return createdAts == null || createdAts.isEmpty() ? 0 : createdAts.size();
254+
}
255+
256+
public Expense getExpense(Long id) {
257+
return expenseRepository.findExpenseById(id);
258+
}
227259
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package donmani.donmani_server.feedback.controller;
2+
3+
import org.springframework.http.HttpStatus;
4+
import org.springframework.http.ResponseEntity;
5+
import org.springframework.web.bind.annotation.GetMapping;
6+
import org.springframework.web.bind.annotation.PathVariable;
7+
import org.springframework.web.bind.annotation.PostMapping;
8+
import org.springframework.web.bind.annotation.RestController;
9+
10+
import donmani.donmani_server.common.httpStatus.HttpStatusDTO;
11+
import donmani.donmani_server.expense.service.ExpenseService;
12+
import donmani.donmani_server.feedback.dto.FeedbackNotOpenedResponseDTO;
13+
import donmani.donmani_server.feedback.dto.FeedbackOpenResponseDTO;
14+
import donmani.donmani_server.feedback.service.FeedbackService;
15+
16+
import jakarta.validation.Valid;
17+
import lombok.AllArgsConstructor;
18+
19+
@RestController
20+
@AllArgsConstructor
21+
public class FeedbackController {
22+
private final FeedbackService feedbackService;
23+
private final ExpenseService expenseService;
24+
25+
@GetMapping("api/v1/feedback/{userKey}")
26+
public ResponseEntity<HttpStatusDTO<FeedbackNotOpenedResponseDTO>> getNotOpenedFeedbackV1(@Valid @PathVariable String userKey) {
27+
try {
28+
// 1. 아직 열지 않은 피드백이 하나라도 있으면 true, 모든 피드백을 열었으면 false
29+
// - 최근 생성된 피드백부터 내림차순으로 정렬
30+
Boolean isNotOpened = feedbackService.isNotOpenedFeedback(userKey);
31+
32+
// 2. 피드백을 처음 여는 상황이면 true, 이미 열은 피드백이 하나라도 있으면 false
33+
// - 최근 생성된 피드백부터 내림차순으로 정렬
34+
Boolean isFirstOpened = feedbackService.isFirstOpenedFeedback(userKey);
35+
36+
// 3. ver 2.0.0 이후로 기록된 소비 확인
37+
Integer totalCount = expenseService.getTotalExpensesCount(userKey);
38+
39+
FeedbackNotOpenedResponseDTO feedbackNotOpenedResponseDTO = new FeedbackNotOpenedResponseDTO(isNotOpened, isFirstOpened, totalCount);
40+
41+
// 1. 피드백 조회 성공 -> 200
42+
return ResponseEntity.ok(HttpStatusDTO.response(HttpStatus.OK.value(), "성공", feedbackNotOpenedResponseDTO));
43+
} catch (Exception e) {
44+
// 2. 피드백 조회 실패 -> 500
45+
return ResponseEntity.ok(HttpStatusDTO.response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "모든 피드백을 열었습니다.", null));
46+
}
47+
}
48+
49+
@PostMapping("api/v1/feedback/{userKey}")
50+
public ResponseEntity<HttpStatusDTO<FeedbackOpenResponseDTO>> openFeedbackV1(@Valid @PathVariable String userKey) {
51+
try {
52+
FeedbackOpenResponseDTO response = feedbackService.openFeedback(userKey);
53+
54+
// 1. 피드백 열기 성공 -> 201
55+
return ResponseEntity.ok(HttpStatusDTO.response(HttpStatus.CREATED.value(), "성공", response));
56+
} catch (Exception e) {
57+
// 2. 피드백 열기 실패 -> 500
58+
return ResponseEntity.ok(HttpStatusDTO.response(HttpStatus.INTERNAL_SERVER_ERROR.value(), "모든 피드백을 열었습니다.", null));
59+
}
60+
}
61+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package donmani.donmani_server.feedback.dto;
2+
3+
import java.util.List;
4+
5+
import donmani.donmani_server.expense.dto.RecordDTO;
6+
import lombok.AllArgsConstructor;
7+
import lombok.Builder;
8+
import lombok.Getter;
9+
import lombok.NoArgsConstructor;
10+
import lombok.Setter;
11+
12+
@Getter
13+
@Setter
14+
@NoArgsConstructor
15+
@AllArgsConstructor
16+
@Builder
17+
public class FeedbackNotOpenedResponseDTO {
18+
private Boolean isNotOpened;
19+
private Boolean isFirstOpen;
20+
private Integer totalCount;
21+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package donmani.donmani_server.feedback.dto;
2+
3+
import donmani.donmani_server.expense.entity.CategoryType;
4+
import jakarta.persistence.EnumType;
5+
import jakarta.persistence.Enumerated;
6+
import lombok.AllArgsConstructor;
7+
import lombok.Builder;
8+
import lombok.Getter;
9+
import lombok.NoArgsConstructor;
10+
import lombok.Setter;
11+
12+
13+
@Getter
14+
@Setter
15+
@NoArgsConstructor
16+
@AllArgsConstructor
17+
@Builder
18+
public class FeedbackOpenResponseDTO {
19+
private String title;
20+
21+
private String content;
22+
23+
private String name;
24+
25+
@Enumerated(EnumType.STRING)
26+
private CategoryType category;
27+
28+
private boolean flagType;
29+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package donmani.donmani_server.feedback.entity;
2+
3+
import java.time.LocalDateTime;
4+
5+
import com.fasterxml.jackson.annotation.JsonBackReference;
6+
7+
import donmani.donmani_server.expense.entity.Expense;
8+
import donmani.donmani_server.user.entity.User;
9+
import jakarta.persistence.Entity;
10+
import jakarta.persistence.FetchType;
11+
import jakarta.persistence.GeneratedValue;
12+
import jakarta.persistence.GenerationType;
13+
import jakarta.persistence.Id;
14+
import jakarta.persistence.JoinColumn;
15+
import jakarta.persistence.ManyToOne;
16+
import jakarta.persistence.OneToOne;
17+
import lombok.AllArgsConstructor;
18+
import lombok.Builder;
19+
import lombok.Getter;
20+
import lombok.NoArgsConstructor;
21+
import lombok.Setter;
22+
23+
@Entity
24+
@Getter
25+
@Setter
26+
@Builder
27+
@NoArgsConstructor
28+
@AllArgsConstructor
29+
public class Feedback {
30+
@Id
31+
@GeneratedValue(strategy = GenerationType.IDENTITY)
32+
private Long id;
33+
34+
private LocalDateTime createdDate;
35+
36+
private LocalDateTime updateDate;
37+
38+
@ManyToOne(fetch = FetchType.LAZY)
39+
@JoinColumn(name = "user_id")
40+
@JsonBackReference
41+
private User user;
42+
43+
@OneToOne
44+
@JoinColumn(name = "expense_id")
45+
@JsonBackReference
46+
private Expense expense;
47+
48+
private boolean isOpened;
49+
50+
private String title;
51+
52+
private String content;
53+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package donmani.donmani_server.feedback.provider;
2+
import lombok.AllArgsConstructor;
3+
import lombok.Getter;
4+
import lombok.ToString;
5+
6+
@Getter
7+
@AllArgsConstructor
8+
@ToString
9+
public class FeedbackTemplate {
10+
private final String title;
11+
private final String content;
12+
}

0 commit comments

Comments
 (0)