Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e0b27a4
chore: ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ddl auto ์„ค์ • ๋ณ€๊ฒฝ
KoungQ May 29, 2026
04d0be2
fix: ์ด๋ฉ”์ผ ์ธ์ฆ ๋กœ์ง ์ˆ˜์ •
KoungQ May 31, 2026
5f090bf
feat: ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ์‹œ ์ด๋ฉ”์ผ ์ธ์ฆ ๋ฐ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ๋กœ์ง ์ถ”๊ฐ€
KoungQ May 31, 2026
112797c
fix: ๋ถˆํ•„์š”ํ•œ DI ์ œ๊ฑฐ
KoungQ May 31, 2026
bf9f075
fix: ์บ˜๋ฆฐ๋” ์—”ํ‹ฐํ‹ฐ ์ˆ˜์ •
KoungQ May 31, 2026
765d82b
fix: ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ๊ฐ’ ์ œํ•œ
KoungQ May 31, 2026
d9c0247
fix: ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ ๋กœ์ง ์ˆ˜์ •
KoungQ May 31, 2026
24e0de4
fix: ์‚ฌ์šฉ์ž ์—ด๊ฑฐ ๊ณต๊ฒฉ ๋ฐฉ์–ด
KoungQ May 31, 2026
3729f09
feat: ์ด๋ฉ”์ผ ์ „์†ก ๋กœ์ง ๋ ˆ์ดํŠธ๋ฆฌ๋ฐ‹ ์ถ”๊ฐ€
KoungQ May 31, 2026
e38de9a
fix: ํšŒ์›๊ฐ€์ž… ์ฒด๋ฅ˜ ์‹œ๊ฐ„ ์—ฐ์žฅ
KoungQ May 31, 2026
9a03a51
fix: ์ด๋ฉ”์ผ ์ „์†ก ๋กœ์ง ๊ฒ€์ฆ
KoungQ May 31, 2026
97eba3c
fix: Discord ์›นํ›… ์˜ค๋ฅ˜ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ
KoungQ May 31, 2026
194ca80
test: ๊ธฐ์กด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ˆ˜์ •
KoungQ May 31, 2026
ab9fd81
fix: CI ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์— Redis ์„œ๋น„์Šค ์ปจํ…Œ์ด๋„ˆ ์ถ”๊ฐ€
KoungQ May 31, 2026
4a17361
fix: ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ˆ˜์ •
KoungQ May 31, 2026
25dedc0
Merge pull request #110 from DorumDorum/feat/#109/๋น„๋ฐ€๋ฒˆํ˜ธ-์ฐพ๊ธฐ-api-๊ธฐ๋Šฅ-๊ตฌํ˜„
KoungQ May 31, 2026
5664538
fix: ์ผ์ • ์‹œ๊ฐ„ ๋„ ํ—ˆ์šฉ
KoungQ May 31, 2026
17e82f1
fix: ๋ฐฉ ์ฐพ๊ธฐ ๊ธฐ๋Šฅ ์ˆ˜์ •
KoungQ May 31, 2026
28d29dd
fix: ๋ฐฉ์žฅ์˜ ํ•œ๋งˆ๋”” ์ˆ˜์ •
KoungQ May 31, 2026
422a975
feat: ์ฒดํฌ๋ฆฌ์ŠคํŠธ ๊ธฐ๋ฐ˜ ์ถ”์ฒœ ๊ธฐ๋Šฅ ์ถ”๊ฐ€
KoungQ May 31, 2026
78e6164
test: ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ˆ˜์ •
KoungQ Jun 2, 2026
d01059c
fix: mapper ์˜ค๋ฅ˜ ์ˆ˜์ •
KoungQ Jun 2, 2026
647385b
Merge pull request #112 from DorumDorum/feat/#111/๋ฐฉ-์ฐพ๊ธฐ-ํŽ˜์ด์ง€-๋ถ€๊ฐ€-๊ธฐ๋Šฅ-๊ตฌํ˜„
KoungQ Jun 2, 2026
07ca492
hotfix: ddl-auto validate -> create
ydking0911 Jun 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ jobs:
name: Test
runs-on: ubuntu-latest

services:
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5

steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package com.project.dorumdorum.domain.calendar.application.dto.response;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.project.dorumdorum.domain.calendar.domain.entity.CalendarEventType;

import java.time.LocalDate;
import java.time.LocalTime;

public record CalendarEventResponse(
LocalDate date,
String title
String title,
String content,
@JsonFormat(pattern = "HH:mm") LocalTime time,
CalendarEventType type
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
@Mapper(componentModel = "spring")
public interface CalendarEventMapper {
@Mapping(target = "date", source = "eventDate")
@Mapping(target = "time", source = "eventTime")
@Mapping(target = "type", source = "eventType")
CalendarEventResponse toResponse(CalendarEvent event);
List<CalendarEventResponse> toResponseList(List<CalendarEvent> events);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
import jakarta.persistence.Table;
import lombok.*;

import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;

import javax.annotation.Nullable;
import java.time.LocalDate;
import java.time.LocalTime;

@Entity
@Getter
Expand All @@ -27,4 +32,13 @@ public class CalendarEvent extends BaseEntity {

@Column(nullable = false)
private String title;

@Column(nullable = false)
private String content;

private LocalTime eventTime;

@Enumerated(EnumType.STRING)
@Column(nullable = false)
private CalendarEventType eventType;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.project.dorumdorum.domain.calendar.domain.entity;

public enum CalendarEventType {
CHECK, // ์ ํ˜ธ
CLEAN, // ์ฒญ์†Œ
NOTICE, // ๊ณต์ง€
EVENT // ํ–‰์‚ฌ
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public interface UserChecklistMapper {
@Mapping(target = "userNo", source = "userNo")
@Mapping(target = "createdAt", ignore = true)
@Mapping(target = "updatedAt", ignore = true)
@Mapping(target = "deletedAt", ignore = true)
UserChecklist toUserChecklist(String userNo, CreateUserChecklistRequest request);

@Mapping(target = "userChecklistNo", ignore = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.project.dorumdorum.domain.checklist.domain.entity.RoomRule;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

Expand All @@ -16,4 +17,7 @@ public interface RoomRuleRepository extends JpaRepository<RoomRule, String> {
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("UPDATE RoomRule rr SET rr.deletedAt = CURRENT_TIMESTAMP WHERE rr.room.roomNo = :roomNo AND rr.deletedAt IS NULL")
void deleteByRoomNo(@Param("roomNo") String roomNo);

@Query("SELECT rr FROM RoomRule rr JOIN FETCH rr.room r WHERE r.roomStatus = 'CONFIRM_PENDING' AND r.deletedAt IS NULL AND rr.deletedAt IS NULL")
List<RoomRule> findAllActiveWithRoom();
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ public interface UserChecklistRepository extends JpaRepository<UserChecklist, St

Optional<UserChecklist> findByUserNo(String userNo);

boolean existsByUserNo(String userNo);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ public record RoomCreateRequest(
@NotNull Integer capacity,
@NotNull ResidencePeriod residencePeriod,
@NotBlank String title,
String notes,
@Valid @NotNull CreateRoomRuleRequest rule
) {}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
import jakarta.validation.constraints.NotBlank;

public record UpdateRoomTitleRequest(
@NotBlank String title
@NotBlank String title,
String notes
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.project.dorumdorum.domain.room.application.dto.response;

import com.project.dorumdorum.global.pagination.CursorPage;

import java.util.List;

public record FindRoomsPageResponse(
List<FindRoomsResponse> items,
String nextCursor,
boolean hasNext,
Long totalCount
) {
public static FindRoomsPageResponse of(CursorPage<FindRoomsResponse> page, Long totalCount) {
return new FindRoomsPageResponse(page.items(), page.nextCursor(), page.hasNext(), totalCount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ public record FindRoomsResponse(
Integer currentMateCount,
LocalDateTime createdAt,
String title,
String notes,
String hostNickname,
String hostMajor,
String hostStudentYear,
RoomStatus roomStatus,
String residencePeriod, // ๊ฑฐ์ฃผ๊ธฐ๊ฐ„ (์˜ˆ: "ํ•™๊ธฐ(16์ฃผ)", "๋ฐ˜๊ธฐ(24์ฃผ)", "๊ณ„์ ˆํ•™๊ธฐ")
Integer remaining
) {}
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.project.dorumdorum.domain.room.application.dto.response;

import com.project.dorumdorum.domain.room.domain.entity.RoomStatus;
import com.project.dorumdorum.domain.room.domain.entity.RoomType;
import lombok.Builder;

import java.util.List;

@Builder
public record RecommendedRoomResponse(
String roomNo,
String title,
RoomType roomType,
Integer capacity,
Integer currentMateCount,
Integer remaining,
RoomStatus roomStatus,
String residencePeriod,
String hostNickname,
String matchLabel,
int matchedCount,
int totalCount,
List<String> highlights
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.project.dorumdorum.domain.room.application.dto.response;

import java.util.List;

public record RecommendedRoomsPageResponse(
boolean hasChecklist,
List<RecommendedRoomResponse> items
) {}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.project.dorumdorum.domain.room.application.usecase;

import com.project.dorumdorum.domain.room.application.dto.request.ChecklistFilterRequest;
import com.project.dorumdorum.domain.room.application.dto.response.FindRoomsPageResponse;
import com.project.dorumdorum.domain.room.application.dto.response.FindRoomsResponse;
import com.project.dorumdorum.domain.room.domain.service.RoomService;
import com.project.dorumdorum.domain.user.domain.entity.Gender;
Expand Down Expand Up @@ -29,7 +30,7 @@ public class FindRoomsUseCase {
* - ํ•„ํ„ฐ์™€ ์ปค์„œ ๊ธฐ์ค€์œผ๋กœ ๋ฐฉ ๋ชฉ๋ก์„ ์กฐํšŒ
* - ๋‹ค์Œ ํŽ˜์ด์ง€ ์ปค์„œ๋ฅผ ํฌํ•จํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜
*/
public CursorPage<FindRoomsResponse> execute(String userNo, ChecklistFilterRequest request) {
public FindRoomsPageResponse execute(String userNo, ChecklistFilterRequest request) {
Gender gender = userService.findById(userNo).getGender();
CursorQueryParams params = PaginationHelper.prepareCursorQuery(request.cursor(), LIMIT);

Expand All @@ -42,12 +43,18 @@ public CursorPage<FindRoomsResponse> execute(String userNo, ChecklistFilterReque
params.limitPlusOne()
);

return PaginationHelper.buildCursorPage(
CursorPage<FindRoomsResponse> page = PaginationHelper.buildCursorPage(
responses,
LIMIT,
last -> request.sortType() == ChecklistFilterRequest.SortType.REMAINING
? CursorCodec.encode(last.remaining(), last.createdAt(), last.roomNo())
: CursorCodec.encode(last.createdAt(), last.roomNo())
);

Long totalCount = request.cursor() == null
? roomService.countSearchResults(gender, request)
: null;

return FindRoomsPageResponse.of(page, totalCount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package com.project.dorumdorum.domain.room.application.usecase;

import com.project.dorumdorum.domain.checklist.domain.entity.ChecklistBase;
import com.project.dorumdorum.domain.checklist.domain.entity.RoomRule;
import com.project.dorumdorum.domain.checklist.domain.entity.UserChecklist;
import com.project.dorumdorum.domain.checklist.domain.repository.RoomRuleRepository;
import com.project.dorumdorum.domain.checklist.domain.repository.UserChecklistRepository;
import com.project.dorumdorum.domain.checklist.domain.service.UserChecklistService;
import com.project.dorumdorum.domain.room.application.dto.response.RecommendedRoomResponse;
import com.project.dorumdorum.domain.room.application.dto.response.RecommendedRoomsPageResponse;
import com.project.dorumdorum.domain.room.domain.entity.Room;
import com.project.dorumdorum.domain.user.domain.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class LoadRecommendedRoomsUseCase {

private static final double STRONG_MATCH_THRESHOLD = 0.85;
private static final double MATCH_THRESHOLD = 0.65;

private final UserChecklistService userChecklistService;
private final UserChecklistRepository userChecklistRepository;
private final RoomRuleRepository roomRuleRepository;
private final UserService userService;

public RecommendedRoomsPageResponse execute(String userNo) {
if (!userChecklistRepository.existsByUserNo(userNo)) {
return new RecommendedRoomsPageResponse(false, List.of());
}

UserChecklist my = userChecklistService.findByUserNo(userNo);
List<RoomRule> roomRules = roomRuleRepository.findAllActiveWithRoom();

List<RecommendedRoomResponse> items = roomRules.stream()
.map(rr -> buildResponse(my, rr))
.filter(r -> r != null)
.sorted(Comparator.comparingDouble(r -> -(double) r.matchedCount() / Math.max(r.totalCount(), 1)))
.toList();

return new RecommendedRoomsPageResponse(true, items);
}

private RecommendedRoomResponse buildResponse(UserChecklist my, RoomRule rr) {
List<FieldResult> fields = compareFields(my, rr);

int totalCount = (int) fields.stream().filter(FieldResult::filled).count();
if (totalCount == 0) return null;

int matchedCount = (int) fields.stream().filter(f -> f.filled() && f.match()).count();
double score = (double) matchedCount / totalCount;

String matchLabel;
if (score >= STRONG_MATCH_THRESHOLD) matchLabel = "์ž˜ ๋งž์•„์š”";
else if (score >= MATCH_THRESHOLD) matchLabel = "๊ดœ์ฐฎ์•„์š”";
else return null;

List<String> highlights = fields.stream()
.filter(f -> f.filled() && f.match())
.limit(3)
.map(FieldResult::label)
.toList();

Room room = rr.getRoom();
String hostNickname = userService.findById(room.getHostUserNo()).getNickname();

return RecommendedRoomResponse.builder()
.roomNo(room.getRoomNo())
.title(room.getTitle())
.roomType(room.getRoomType())
.capacity(room.getCapacity())
.currentMateCount(room.getCurrentMateCount())
.remaining(room.getRemaining())
.roomStatus(room.getRoomStatus())
.residencePeriod(room.getResidencePeriod().name())
.hostNickname(hostNickname)
.matchLabel(matchLabel)
.matchedCount(matchedCount)
.totalCount(totalCount)
.highlights(highlights)
.build();
}

private List<FieldResult> compareFields(UserChecklist my, ChecklistBase room) {
List<FieldResult> results = new ArrayList<>();
addField(results, "์ทจ์นจ", my.getBedtime(), room.getBedtime());
addField(results, "๊ธฐ์ƒ", my.getWakeUp(), room.getWakeUp());
addField(results, "๊ท€๊ฐ€", my.getReturnHome(), room.getReturnHome());
addField(results, "์ฒญ์†Œ", my.getCleaning(), room.getCleaning());
addField(results, "๋ฐฉ์—์„œ ์ „ํ™”", my.getPhoneCall(), room.getPhoneCall());
addField(results, "์ž ๊ท€", my.getSleepLight(), room.getSleepLight());
addField(results, "์ž ๋ฒ„๋ฆ‡", my.getSleepHabit(), room.getSleepHabit());
addField(results, "์ฝ”๊ณจ์ด", my.getSnoring(), room.getSnoring());
addField(results, "์ƒค์›Œ ์‹œ๊ฐ„", my.getShowerTime(), room.getShowerTime());
addField(results, "๋ฐฉ์—์„œ ์ทจ์‹", my.getEating(), room.getEating());
addField(results, "์†Œ๋“ฑ", my.getLightsOut(), room.getLightsOut());
addField(results, "๋ณธ๊ฐ€ ์ฃผ๊ธฐ", my.getHomeVisit(), room.getHomeVisit());
addField(results, "ํก์—ฐ", my.getSmoking(), room.getSmoking());
addField(results, "๋ƒ‰์žฅ๊ณ ", my.getRefrigerator(), room.getRefrigerator());
addField(results, "์•Œ๋žŒ", my.getAlarm(), room.getAlarm());
addField(results, "์ด์–ดํฐ", my.getEarphone(), room.getEarphone());
addField(results, "ํ‚ค์Šคํ‚จ", my.getKeyskin(), room.getKeyskin());
addField(results, "๋”์œ„", my.getHeat(), room.getHeat());
addField(results, "์ถ”์œ„", my.getCold(), room.getCold());
addField(results, "๊ณต๋ถ€", my.getStudy(), room.getStudy());
addField(results, "์“ฐ๋ ˆ๊ธฐํ†ต", my.getTrashCan(), room.getTrashCan());
return results;
}

private void addField(List<FieldResult> results, String label, Object myValue, Object roomValue) {
boolean filled = myValue != null && !(myValue instanceof String s && s.isBlank());
boolean match = filled && Objects.equals(myValue, roomValue);
results.add(new FieldResult(label, filled, match));
}

private record FieldResult(String label, boolean filled, boolean match) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ public void execute(String userNo, String roomNo, UpdateRoomTitleRequest request
throw new RestApiException(NO_PERMISSION_ON_ROOM);

room.updateTitle(request.title().trim());
room.updateNotes(request.notes());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public class Room extends BaseEntity {
@Column(nullable = false)
private String title;

@Column(columnDefinition = "TEXT")
private String notes;

@Column(nullable = false)
private String hostUserNo;

Expand Down Expand Up @@ -127,4 +130,8 @@ public void updateResidencePeriod(ResidencePeriod residencePeriod) {
public void updateTitle(String title) {
this.title = title;
}

public void updateNotes(String notes) {
this.notes = notes;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.project.dorumdorum.domain.room.domain.entity;

public enum RoomType {
TYPE_1, TYPE_2, TYPE_MEDICAL
TYPE_1, TYPE_2, TYPE_3, TYPE_MEDICAL
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ List<FindRoomsResponse> findByCursor(
int limitPlusOne
);

long countByFilter(Gender gender, ChecklistFilterRequest request);

Optional<FindRoomsResponse> findMyRoom(String userNo);

List<FindRoomsResponse> findLikedRooms(String userNo);
Expand Down
Loading
Loading