Skip to content

Commit cf41276

Browse files
authored
Merge pull request #157 from Money-Touch/fix/#156
[#156] 🐛Fix: 고정비 등록 시 메모 사항 제거 및 고정비&내 소비 루틴 목록 조회 시 연도, 월 필터링 추가
2 parents 9a32111 + d68d05f commit cf41276

File tree

14 files changed

+84
-51
lines changed

14 files changed

+84
-51
lines changed

src/main/java/com/server/money_touch/domain/consumptionRecord/converter/consumptionRecord/ConsumptionRecordConverter.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,39 @@
1414
import java.time.LocalDate;
1515
import java.time.LocalDateTime;
1616
import java.util.List;
17+
import java.util.Optional;
1718

1819
public class ConsumptionRecordConverter {
1920

2021
// 일일 소비 기록 시 소비 카테고리 엔티티 생성
21-
public static ConsumptionRecord toDailyConsumptionRecord(User user, ConsumptionCategory consumptionCategory, HouseholdConsumptionRequest.DailyConsumptionCreateDTO requestDTO, Boolean isPublic) {
22-
return ConsumptionRecord.builder()
22+
public static ConsumptionRecord toDailyConsumptionRecord(
23+
User user,
24+
ConsumptionCategory consumptionCategory,
25+
HouseholdConsumptionRequest.DailyConsumptionCreateDTO requestDTO,
26+
Boolean isPublic
27+
) {
28+
var builder = ConsumptionRecord.builder()
2329
.user(user)
2430
.consumptionCategory(consumptionCategory)
2531
.amount(requestDTO.getAmount())
2632
.content(requestDTO.getContent())
27-
.memo(requestDTO.getMemo())
2833
.consumeDate(requestDTO.getConsumeDate())
2934
.isPublic(isPublic) // 일일 소비 기록은 피드 없이 가계부에만 등록
3035
.commentCount(0)
3136
.wiseCount(0)
3237
.wasteCount(0)
33-
.viewCount(0)
34-
.build();
38+
.viewCount(0);
39+
40+
// memo가 있을 때(공백 제외)만 세팅
41+
Optional.ofNullable(requestDTO.getMemo())
42+
.map(String::trim)
43+
.filter(s -> !s.isEmpty())
44+
.ifPresent(builder::memo);
45+
46+
return builder.build();
3547
}
3648

49+
3750
// 소비 기록 응답
3851
public static ConsumptionRecordResponse.ConsumptionRecordCreateResultDTO toConsumptionRecordCreateResultDTO(Long consumptionRecordId){
3952
return ConsumptionRecordResponse.ConsumptionRecordCreateResultDTO.builder()

src/main/java/com/server/money_touch/domain/fixedConsumption/controller/FixedConsumptionController.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public class FixedConsumptionController {
3636

3737
@Operation(
3838
summary = "고정비 등록 API",
39-
description = "고정비 등록 API 입니다. 금액, 카테고리, 항목명, 메모를 RequestBody로 입력받아 고정비를 등록합니다."
39+
description = "고정비 등록 API 입니다. 금액, 카테고리, 항목명, 메모를 RequestBody로 입력받아 고정비를 등록합니다. 메모가 없다면 RequestBody에 포함하지 않아도 됩니다."
4040
)
4141
@ApiSuccessCodeExample(resultClass = FixedConsumptionResponse.FixedConsumptionCreateResultDTO.class)
4242
@ApiErrorCodeExamples({
@@ -107,12 +107,14 @@ public ApiResponse<String> deleteFixedConsumption(@PathVariable Long fixedConsum
107107
@ApiErrorCodeExample(value = ErrorStatus.class, name = "_INTERNAL_SERVER_ERROR"),
108108
})
109109
@Parameters({
110+
@Parameter(name = "year", description = "조회하려는 소비 연도", example = "2025", required = true),
111+
@Parameter(name = "month", description = "조회하려는 소비 월", example = "7", required = true),
110112
@Parameter(name = "cursorId", description = "커서 (이전 요청의 마지막 cursorId). 첫 요청 시 생략", example = "3", required = false)
111113
})
112114
@GetMapping("list")
113-
public ApiResponse<FixedConsumptionResponse.FixedConsumptionCursorResultDTO> getFixedConsumptions(@RequestParam(required = false) Long cursorId, HttpServletRequest servletRequest) {
115+
public ApiResponse<FixedConsumptionResponse.FixedConsumptionCursorResultDTO> getFixedConsumptions(@RequestParam(required = true) Integer year, @RequestParam(required = true) Integer month, @RequestParam(required = false) Long cursorId, HttpServletRequest servletRequest) {
114116
Long userId = authUtil.getUserIdFromRequest(servletRequest);
115-
FixedConsumptionResponse.FixedConsumptionCursorResultDTO response = fixedConsumptionQueryService.getFixedConsumptions(userId, cursorId);
117+
FixedConsumptionResponse.FixedConsumptionCursorResultDTO response = fixedConsumptionQueryService.getFixedConsumptions(userId, year, month, cursorId);
116118
return ApiResponse.onSuccess(response);
117119
}
118120

src/main/java/com/server/money_touch/domain/fixedConsumption/dto/FixedConsumptionRequest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ public static class FixedConsumptionCreateDTO{
2929
private String content;
3030

3131
@Schema(description = "메모", example = "가족 공유 요금제")
32-
@NotNull(message = "메모는 필수입니다.")
3332
@Size(max = 1000, message = "항목명은 1000자 이하로 입력해주세요.")
3433
private String memo;
3534
}

src/main/java/com/server/money_touch/domain/fixedConsumption/entity/FixedConsumption.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class FixedConsumption extends BaseEntity {
1717
@Column(nullable = false, length = 20)
1818
private String fixedConsumptionContent;
1919

20-
@Column(nullable = false, length = 1000)
20+
@Column(length = 1000)
2121
private String fixedConsumptionMemo;
2222

2323
@Column(nullable = false, length = 20)

src/main/java/com/server/money_touch/domain/fixedConsumption/repository/FixedConsumptionRepositoryCustom.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88

99
public interface FixedConsumptionRepositoryCustom {
1010
// 커서 기반 고정비 목록 조회
11-
Slice<FixedConsumption> findFixedConsumptionsByCursor(Long userId, Long cursorId, Pageable pageable);
11+
Slice<FixedConsumption> findFixedConsumptionsByCursor(Long userId, Long cursorId, int year, int month, Pageable pageable);
1212
}

src/main/java/com/server/money_touch/domain/fixedConsumption/repository/FixedConsumptionRepositoryImpl.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import org.springframework.data.domain.SliceImpl;
1111
import org.springframework.stereotype.Repository;
1212

13+
import java.time.LocalDate;
14+
import java.time.LocalDateTime;
1315
import java.util.List;
1416

1517
@RequiredArgsConstructor
@@ -21,13 +23,23 @@ public class FixedConsumptionRepositoryImpl implements FixedConsumptionRepositor
2123

2224
// 커서 기반 고정비 목록 조회
2325
@Override
24-
public Slice<FixedConsumption> findFixedConsumptionsByCursor(Long userId, Long cursorId, Pageable pageable) {
26+
public Slice<FixedConsumption> findFixedConsumptionsByCursor(
27+
Long userId, Long cursorId, int year, int month, Pageable pageable) {
28+
2529
BooleanBuilder condition = new BooleanBuilder();
2630
condition.and(fixed.user.id.eq(userId));
31+
32+
// 커서 조건
2733
if (cursorId != null) {
28-
condition.and(fixed.id.lt(cursorId)); // id 역순으로 작아야 이후 커서
34+
condition.and(fixed.id.lt(cursorId));
2935
}
3036

37+
// ✅ 해당 달보다 이후에 생성된 고정비는 보이지 않도록 필터링
38+
LocalDate firstDay = LocalDate.of(year, month, 1);
39+
LocalDateTime nextMonthStart = firstDay.plusMonths(1).atStartOfDay();
40+
41+
condition.and(fixed.createdAt.before(nextMonthStart));
42+
3143
int pageSize = pageable.getPageSize();
3244
List<FixedConsumption> results = queryFactory
3345
.selectFrom(fixed)
@@ -38,7 +50,7 @@ public Slice<FixedConsumption> findFixedConsumptionsByCursor(Long userId, Long c
3850

3951
boolean hasNext = results.size() > pageSize;
4052
if (hasNext) {
41-
results.remove(pageSize); // Slice에 반환할 범위까지만 유지
53+
results.remove(pageSize);
4254
}
4355

4456
return new SliceImpl<>(results, pageable, hasNext);

src/main/java/com/server/money_touch/domain/fixedConsumption/service/FixedConsumptionCommandServiceImpl.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
import java.time.LocalDate;
3131
import java.time.LocalDateTime;
3232
import java.util.Map;
33-
import java.util.Optional;
34-
import java.util.concurrent.atomic.AtomicInteger;
3533
import java.util.stream.Collectors;
3634

3735

src/main/java/com/server/money_touch/domain/fixedConsumption/service/FixedConsumptionQueryService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ public interface FixedConsumptionQueryService {
88
Boolean existsFixedConsumptionById(Long fixedConsumptionId);
99

1010
// 고정비 목록 조회 (커서 기반 무한스크롤)
11-
FixedConsumptionResponse.FixedConsumptionCursorResultDTO getFixedConsumptions(@ExistUser Long userId, Long cursorId);
11+
FixedConsumptionResponse.FixedConsumptionCursorResultDTO getFixedConsumptions(@ExistUser Long userId, Integer year, Integer month, Long cursorId);
1212
}

src/main/java/com/server/money_touch/domain/fixedConsumption/service/FixedConsumptionQueryServiceImpl.java

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,37 +42,33 @@ public Boolean existsFixedConsumptionById(Long fixedConsumptionId) {
4242
* - 페이지 사이즈는 고정(PAGE_SIZE)
4343
* - 커서 기반 페이징 방식으로, 마지막 조회된 고정비 ID 이후 데이터를 가져옵니다.
4444
* - 응답에는 고정비 목록, 다음 커서 ID 및 페이징 정보가 포함됩니다.
45-
*
46-
* @param userId 사용자 ID
47-
* @param cursorId 마지막으로 조회된 고정비 ID (null이면 첫 페이지)
48-
* @return FixedConsumptionCursorResultDTO (고정비 목록 + 페이징 정보)
4945
*/
5046
@Override
51-
public FixedConsumptionResponse.FixedConsumptionCursorResultDTO getFixedConsumptions(Long userId, Long cursorId) {
47+
public FixedConsumptionResponse.FixedConsumptionCursorResultDTO getFixedConsumptions(
48+
Long userId, Integer year, Integer month, Long cursorId) {
49+
5250
// 사용자 조회
5351
User user = userRepository.findById(userId)
5452
.orElseThrow(() -> new ErrorHandler(ErrorStatus.USER_NOT_FOUND));
5553

5654
Pageable pageable = PageRequest.of(0, PAGE_SIZE);
5755

58-
// 고정비 목록 조회 (Slice)
56+
// 고정비 목록 조회 (Slice) - year, month 추가
5957
Slice<FixedConsumption> slice = fixedConsumptionRepository
60-
.findFixedConsumptionsByCursor(userId, cursorId, pageable);
58+
.findFixedConsumptionsByCursor(userId, cursorId, year, month, pageable);
6159

62-
// 고정비 엔티티 → DTO 변환
6360
List<FixedConsumptionResponse.FixedConsumptionDetailDTO> content = slice.getContent().stream()
6461
.map(FixedConsumptionConverter::toFixedConsumptionDetailDTO)
65-
.collect(Collectors.toList());
62+
.toList();
6663

6764
// 다음 커서 ID 설정
6865
Long nextCursorId = content.isEmpty() ? null : content.get(content.size() - 1).getFixedConsumptionId();
6966

70-
log.info("고정비 목록 조회(커서 기반 무한스크롤) 완료 - userId: {}, cursorId: {}, nextCursorId: {}", userId, cursorId, nextCursorId);
7167
return FixedConsumptionConverter.toFixedConsumptionCursorResultDTO(
7268
content,
7369
slice.hasNext(),
7470
nextCursorId,
75-
cursorId == null // 첫 페이지 여부
71+
cursorId == null
7672
);
7773
}
7874
}

src/main/java/com/server/money_touch/domain/routine/controller/RoutineController.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,27 @@ public ApiResponse<RoutineResponse.RoutineCreateResultDTO> postRoutine(@Valid @R
7575
// 내 소비 루틴 목록 조회
7676
@Operation(
7777
summary = "내 소비 루틴 목록 조회 API (커서 기반 무한스크롤)",
78-
description = "가계부에서 사용자가 등록한 소비 루틴 목록을 스크롤 형식으로 조회하는 API입니다."
78+
description = "가계부에서 사용자가 등록한 소비 루틴 목록을 스크롤 형식으로 조회하는 API입니다. 특정 연도/월별 조회가 가능합니다."
7979
)
8080
@ApiErrorCodeExamples({
8181
@ApiErrorCodeExample(value = ErrorStatus.class, name = "USER_NOT_FOUND"),
8282
@ApiErrorCodeExample(value = ErrorStatus.class, name = "_BAD_REQUEST"),
8383
@ApiErrorCodeExample(value = ErrorStatus.class, name = "_INTERNAL_SERVER_ERROR"),
8484
})
85-
@Parameter(name = "cursorId", description = "커서(이전 요청에서 마지막 소비 루틴 아이디), 첫번째 요청일 시에는 파라미터에 포함하지 않아도 됩니다.", example = "10", required = false)
85+
@Parameters({
86+
@Parameter(name = "cursorId", description = "커서(이전 요청에서 마지막 루틴 ID), 첫 요청 시 생략 가능", example = "10", required = false),
87+
@Parameter(name = "year", description = "조회할 연도", example = "2025", required = true),
88+
@Parameter(name = "month", description = "조회할 월", example = "7", required = true)
89+
})
8690
@GetMapping("/users")
87-
public ApiResponse<RoutineResponse.MyRoutineListDTO> getMyRoutines(@RequestParam(required = false) Long cursorId, HttpServletRequest servletRequest) {
91+
public ApiResponse<RoutineResponse.MyRoutineListDTO> getMyRoutines(
92+
@RequestParam int year,
93+
@RequestParam int month,
94+
@RequestParam(required = false) Long cursorId,
95+
HttpServletRequest servletRequest) {
96+
8897
Long userId = authUtil.getUserIdFromRequest(servletRequest);
89-
RoutineResponse.MyRoutineListDTO response = routineQueryService.getMyRoutineList(userId, cursorId);
98+
RoutineResponse.MyRoutineListDTO response = routineQueryService.getMyRoutineList(userId, year, month, cursorId);
9099
return ApiResponse.onSuccess(response);
91100
}
92101

0 commit comments

Comments
 (0)