Skip to content

Commit fdc9f9c

Browse files
authored
feat:레슨수정강사용 (#108)
1 parent 0c74146 commit fdc9f9c

File tree

14 files changed

+430
-26
lines changed

14 files changed

+430
-26
lines changed

src/main/java/com/threestar/trainus/domain/lesson/student/dto/LessonDetailResponseDto.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@ public record LessonDetailResponseDto(
3737
LocalDateTime updatedAt,
3838
List<String> lessonImages
3939
) {
40-
}
40+
}

src/main/java/com/threestar/trainus/domain/lesson/student/mapper/LessonApplicationMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ public static MyLessonApplicationListResponseDto toDtoListWithCount(List<LessonA
4646
count
4747
);
4848
}
49-
}
49+
}

src/main/java/com/threestar/trainus/domain/lesson/teacher/controller/AdminLessonController.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.springframework.web.bind.annotation.DeleteMapping;
66
import org.springframework.web.bind.annotation.GetMapping;
77
import org.springframework.web.bind.annotation.ModelAttribute;
8+
import org.springframework.web.bind.annotation.PatchMapping;
89
import org.springframework.web.bind.annotation.PathVariable;
910
import org.springframework.web.bind.annotation.PostMapping;
1011
import org.springframework.web.bind.annotation.RequestBody;
@@ -20,6 +21,8 @@
2021
import com.threestar.trainus.domain.lesson.teacher.dto.LessonApplicationListWrapperDto;
2122
import com.threestar.trainus.domain.lesson.teacher.dto.LessonCreateRequestDto;
2223
import com.threestar.trainus.domain.lesson.teacher.dto.LessonResponseDto;
24+
import com.threestar.trainus.domain.lesson.teacher.dto.LessonUpdateRequestDto;
25+
import com.threestar.trainus.domain.lesson.teacher.dto.LessonUpdateResponseDto;
2326
import com.threestar.trainus.domain.lesson.teacher.dto.ParticipantListResponseDto;
2427
import com.threestar.trainus.domain.lesson.teacher.dto.ParticipantListWrapperDto;
2528
import com.threestar.trainus.domain.lesson.teacher.entity.ApplicationAction;
@@ -150,4 +153,15 @@ public ResponseEntity<PagedResponse<CreatedLessonListWrapperDto>> getCreatedLess
150153
return PagedResponse.ok("개설한 레슨 목록 조회 완료.", wrapperDto, responseDto.count(), HttpStatus.OK);
151154
}
152155

156+
@PatchMapping("/lessons/{lessonId}")
157+
@Operation(summary = "레슨 수정 api", description = "레슨 정보를 수정")
158+
public ResponseEntity<BaseResponse<LessonUpdateResponseDto>> updateLesson(
159+
@PathVariable Long lessonId,
160+
@Valid @RequestBody LessonUpdateRequestDto requestDto,
161+
@LoginUser Long loginUserId) {
162+
163+
LessonUpdateResponseDto responseDto = adminLessonService.updateLesson(lessonId, requestDto, loginUserId);
164+
return BaseResponse.ok("레슨이 수정되었습니다.", responseDto, HttpStatus.OK);
165+
}
166+
153167
}

src/main/java/com/threestar/trainus/domain/lesson/teacher/dto/LessonCreateRequestDto.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,4 @@ public record LessonCreateRequestDto(
6767
List<String> lessonImages
6868

6969
) {
70-
71-
public LessonCreateRequestDto {
72-
// null 체크 등 추가 검증 로직
73-
if (lessonImages == null) {
74-
lessonImages = List.of(); // 빈 리스트로 초기화
75-
}
76-
}
7770
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.threestar.trainus.domain.lesson.teacher.dto;
2+
3+
import java.time.LocalDateTime;
4+
import java.util.List;
5+
6+
import com.threestar.trainus.domain.lesson.teacher.entity.Category;
7+
8+
import jakarta.validation.constraints.Max;
9+
import jakarta.validation.constraints.Min;
10+
import jakarta.validation.constraints.Size;
11+
12+
/**
13+
* 레슨 수정 요청 데이터
14+
*/
15+
public record LessonUpdateRequestDto(
16+
17+
@Size(max = 50, message = "레슨명은 50자 이내여야 합니다.")
18+
String lessonName,
19+
20+
@Size(max = 255, message = "레슨 설명은 255자 이내여야 합니다.")
21+
String description,
22+
23+
Category category,
24+
25+
@Min(value = 0, message = "가격은 0원 이상이어야 합니다.")
26+
Integer price,
27+
28+
@Min(value = 1, message = "최대 참가 인원은 1명 이상이어야 합니다.")
29+
@Max(value = 100, message = "최대 참가 인원은 100명 이하여야 합니다.")
30+
Integer maxParticipants,
31+
32+
LocalDateTime startAt,
33+
34+
LocalDateTime endAt,
35+
36+
LocalDateTime openTime,
37+
38+
Boolean openRun,
39+
40+
@Size(max = 10, message = "시/도는 10자 이하여야 합니다.")
41+
String city,
42+
43+
@Size(max = 10, message = "시/군/구는 10자 이하여야 합니다.")
44+
String district,
45+
46+
@Size(max = 10, message = "읍/면/동은 10자 이하여야 합니다.")
47+
String dong,
48+
49+
@Size(max = 25, message = "상세주소는 25자 이하여야 합니다.")
50+
String addressDetail,
51+
52+
@Size(max = 5, message = "이미지는 최대 5장까지 첨부 가능합니다.")
53+
List<String> lessonImages
54+
55+
) {
56+
57+
// 레슨이름, 설명, 이미지는 항상 수정가능
58+
public boolean hasBasicInfoChanges() {
59+
return lessonName != null || description != null || (lessonImages != null && !lessonImages.isEmpty());
60+
}
61+
62+
// 제한된 필드들은 수정(참가자 없을때만 가능)
63+
public boolean hasRestrictedChanges() {
64+
return category != null || price != null || startAt != null || endAt != null || openTime != null
65+
|| openRun != null || city != null || district != null || dong != null || addressDetail != null;
66+
}
67+
68+
//시간 관련 필드 수정 체크
69+
public boolean hasTimeChanges() {
70+
return startAt != null || endAt != null;
71+
}
72+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.threestar.trainus.domain.lesson.teacher.dto;
2+
3+
import java.time.LocalDateTime;
4+
import java.util.List;
5+
6+
import com.threestar.trainus.domain.lesson.teacher.entity.Category;
7+
import com.threestar.trainus.domain.lesson.teacher.entity.LessonStatus;
8+
9+
import lombok.Builder;
10+
11+
/**
12+
* 레슨 수정 응답 데이터
13+
*/
14+
@Builder
15+
public record LessonUpdateResponseDto(
16+
Long id,
17+
String lessonName,
18+
String description,
19+
Long lessonLeader,
20+
Category category,
21+
Integer price,
22+
Integer maxParticipants,
23+
LocalDateTime startAt,
24+
LocalDateTime endAt,
25+
LocalDateTime openTime,
26+
Boolean openRun,
27+
String city,
28+
String district,
29+
String dong,
30+
String addressDetail,
31+
LessonStatus status,
32+
LocalDateTime createdAt,
33+
LocalDateTime updatedAt,
34+
List<String> lessonImages
35+
) {
36+
}

src/main/java/com/threestar/trainus/domain/lesson/teacher/entity/Lesson.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import java.time.LocalDateTime;
44

55
import com.threestar.trainus.global.entity.BaseDateEntity;
6+
import com.threestar.trainus.global.exception.domain.ErrorCode;
7+
import com.threestar.trainus.global.exception.handler.BusinessException;
68

79
import jakarta.persistence.Column;
810
import jakarta.persistence.Entity;
@@ -130,4 +132,83 @@ public void incrementParticipantCount() {
130132
}
131133
}
132134

135+
//레슨 이름 수정
136+
public void updateLessonName(String lessonName) {
137+
if (lessonName != null && !lessonName.trim().isEmpty()) {
138+
this.lessonName = lessonName;
139+
}
140+
}
141+
142+
//레슨 설명 수정
143+
public void updateDescription(String description) {
144+
if (description != null && !description.trim().isEmpty()) {
145+
this.description = description;
146+
}
147+
}
148+
149+
//카테고리 수정
150+
public void updateCategory(Category category) {
151+
if (category != null) {
152+
this.category = category;
153+
}
154+
}
155+
156+
//가격 수정
157+
public void updatePrice(Integer price) {
158+
if (price != null) {
159+
this.price = price;
160+
}
161+
}
162+
163+
//최대 참가자수 수정(참가자가 이미 있는 경우는 참가자수 증가만 가능)
164+
public void updateMaxParticipants(Integer newMaxParticipants, boolean hasParticipants) {
165+
if (newMaxParticipants != null) {
166+
if (hasParticipants && newMaxParticipants < this.maxParticipants) {
167+
throw new BusinessException(ErrorCode.LESSON_MAX_PARTICIPANTS_CANNOT_DECREASE);
168+
}
169+
this.maxParticipants = newMaxParticipants;
170+
}
171+
}
172+
173+
//레슨 시간 수정
174+
public void updateLessonTime(LocalDateTime startAt, LocalDateTime endAt) {
175+
if (startAt != null) {
176+
this.startAt = startAt;
177+
}
178+
if (endAt != null) {
179+
this.endAt = endAt;
180+
}
181+
}
182+
183+
//오픈시각 수정
184+
public void updateOpenTime(LocalDateTime openTime) {
185+
this.openTime = openTime;
186+
}
187+
188+
//참여방식 수정
189+
public void updateOpenRun(Boolean openRun) {
190+
if (openRun != null) {
191+
this.openRun = openRun;
192+
}
193+
}
194+
195+
//지역정보 수정
196+
public void updateLocation(String city, String district, String dong) {
197+
if (city != null && !city.trim().isEmpty()) {
198+
this.city = city;
199+
}
200+
if (district != null && !district.trim().isEmpty()) {
201+
this.district = district;
202+
}
203+
if (dong != null && !dong.trim().isEmpty()) {
204+
this.dong = dong;
205+
}
206+
}
207+
208+
//상세주소 수정
209+
public void updateAddressDetail(String addressDetail) {
210+
if (addressDetail != null && !addressDetail.trim().isEmpty()) {
211+
this.addressDetail = addressDetail;
212+
}
213+
}
133214
}

src/main/java/com/threestar/trainus/domain/lesson/teacher/mapper/LessonMapper.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.threestar.trainus.domain.lesson.student.dto.LessonDetailResponseDto;
66
import com.threestar.trainus.domain.lesson.teacher.dto.LessonCreateRequestDto;
77
import com.threestar.trainus.domain.lesson.teacher.dto.LessonResponseDto;
8+
import com.threestar.trainus.domain.lesson.teacher.dto.LessonUpdateResponseDto;
89
import com.threestar.trainus.domain.lesson.teacher.entity.Lesson;
910
import com.threestar.trainus.domain.lesson.teacher.entity.LessonImage;
1011
import com.threestar.trainus.domain.profile.entity.Profile;
@@ -98,4 +99,34 @@ public static LessonDetailResponseDto toLessonDetailDto(
9899
.lessonImages(lessonImages)
99100
.build();
100101
}
102+
103+
//레슨 수정
104+
public static LessonUpdateResponseDto toUpdateResponseDto(Lesson lesson, List<LessonImage> lessonImages) {
105+
// 이미지 엔티티 목록에서 URL만 추출
106+
List<String> imageUrls = lessonImages.stream()
107+
.map(LessonImage::getImageUrl)
108+
.toList();
109+
110+
return LessonUpdateResponseDto.builder()
111+
.id(lesson.getId())
112+
.lessonName(lesson.getLessonName())
113+
.description(lesson.getDescription())
114+
.lessonLeader(lesson.getLessonLeader())
115+
.category(lesson.getCategory())
116+
.price(lesson.getPrice())
117+
.maxParticipants(lesson.getMaxParticipants())
118+
.startAt(lesson.getStartAt())
119+
.endAt(lesson.getEndAt())
120+
.openTime(lesson.getOpenTime())
121+
.openRun(lesson.getOpenRun())
122+
.city(lesson.getCity())
123+
.district(lesson.getDistrict())
124+
.dong(lesson.getDong())
125+
.addressDetail(lesson.getAddressDetail())
126+
.status(lesson.getStatus())
127+
.createdAt(lesson.getCreatedAt())
128+
.updatedAt(lesson.getUpdatedAt())
129+
.lessonImages(imageUrls)
130+
.build();
131+
}
101132
}

src/main/java/com/threestar/trainus/domain/lesson/teacher/repository/LessonRepository.java

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,43 @@ public interface LessonRepository extends JpaRepository<Lesson, Long> {
1818
Optional<Lesson> findByIdAndDeletedAtIsNull(Long lessonId);
1919

2020
// 중복 레슨 검증(같은 강사가 같은 이름과 시작시간으로 레슨 생성했는지 체크)
21-
@Query("SELECT COUNT(l) > 0 FROM Lesson l WHERE " + "l.lessonLeader = :lessonLeader AND "
22-
+ "l.lessonName = :lessonName AND " + "l.startAt = :startAt AND " + "l.deletedAt IS NULL")
21+
@Query("""
22+
SELECT COUNT(l) > 0 FROM Lesson l
23+
WHERE l.lessonLeader = :lessonLeader
24+
AND l.lessonName = :lessonName
25+
AND l.startAt = :startAt
26+
AND l.deletedAt IS NULL
27+
""")
2328
boolean existsDuplicateLesson(@Param("lessonLeader") Long lessonLeader,
2429
@Param("lessonName") String lessonName,
2530
@Param("startAt") LocalDateTime startAt);
2631

27-
// 시간 겹침 검증(같은 강사가 동일 시간대에 다른 레슨이 있는지 체크)
28-
@Query(
29-
"SELECT COUNT(l) > 0 FROM Lesson l WHERE " + "l.lessonLeader = :lessonLeader AND " + "l.deletedAt IS NULL AND "
30-
+ "(l.startAt < :endAt AND l.endAt > :startAt)")
32+
// 시간 겹침 검증(같은 강사가 동일 시간대에 다른 레슨이 있는지 체크) ->레슨생성시에 사용
33+
@Query("""
34+
SELECT COUNT(l) > 0 FROM Lesson l
35+
WHERE l.lessonLeader = :lessonLeader
36+
AND l.deletedAt IS NULL
37+
AND (l.startAt < :endAt AND l.endAt > :startAt)
38+
""")
3139
boolean hasTimeConflictLesson(@Param("lessonLeader") Long lessonLeader,
3240
@Param("startAt") LocalDateTime startAt,
3341
@Param("endAt") LocalDateTime endAt);
3442

43+
//시간 겹침 검증 ->레슨 수정시 사용 (현재 수정중인 레슨 제외)
44+
@Query("""
45+
SELECT COUNT(l) > 0 FROM Lesson l
46+
WHERE l.lessonLeader = :lessonLeader
47+
AND l.deletedAt IS NULL
48+
AND l.id != :excludeLessonId
49+
AND (l.startAt < :endAt AND l.endAt > :startAt)
50+
""")
51+
boolean hasTimeConflictForUpdate(
52+
@Param("lessonLeader") Long lessonLeader,
53+
@Param("startAt") LocalDateTime startAt,
54+
@Param("endAt") LocalDateTime endAt,
55+
@Param("excludeLessonId") Long excludeLessonId
56+
);
57+
3558
// 강사가 개설한 레슨 목록 조회 (페이징)
3659
Page<Lesson> findByLessonLeaderAndDeletedAtIsNull(Long lessonLeader, Pageable pageable);
3760

@@ -51,8 +74,7 @@ Page<Lesson> findByLessonLeaderAndStatusAndDeletedAtIsNull(Long lessonLeader, Le
5174
:search IS NULL OR
5275
LOWER(l.lessonName) LIKE LOWER(CONCAT('%', :search, '%'))
5376
)
54-
"""
55-
)
77+
""")
5678
Page<Lesson> findBySearchConditions(
5779
@Param("category") Category category,
5880
@Param("city") String city,

0 commit comments

Comments
 (0)