Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request.CurriculumUpdateRequest;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.response.AdminCurriculumSummaryResponse;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.response.AdminCurriculumTreeResponse;
import com.bcsdlab.bcsdinternalapiv2.global.controller.dto.request.PublishRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
Expand All @@ -19,6 +20,15 @@
@SecurityRequirement(name = "JWT")
public interface AdminCurriculumApi {

@ApiResponses(value = {
@ApiResponse(responseCode = "200"),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))),
})
@Operation(summary = "주차 > 토픽 > 세부항목 3단 트리 전체 조회")
AdminCurriculumTreeResponse getTree(@PathVariable Long id);

@ApiResponses(value = {
@ApiResponse(responseCode = "200"),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request.CurriculumUpdateRequest;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.response.AdminCurriculumSummaryResponse;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.response.AdminCurriculumTreeResponse;
import com.bcsdlab.bcsdinternalapiv2.curriculum.service.AdminCurriculumService;
import com.bcsdlab.bcsdinternalapiv2.curriculum.service.AdminCurriculumTreeService;
import com.bcsdlab.bcsdinternalapiv2.global.controller.dto.request.PublishRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
Expand All @@ -21,6 +24,13 @@
public class AdminCurriculumController implements AdminCurriculumApi {

private final AdminCurriculumService adminCurriculumService;
private final AdminCurriculumTreeService adminCurriculumTreeService;

@Override
@GetMapping("/{id}")
public AdminCurriculumTreeResponse getTree(@PathVariable Long id) {
return adminCurriculumTreeService.getTree(id);
}

@Override
@PutMapping("/{id}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.bcsdlab.bcsdinternalapiv2.curriculum.controller;

import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request.WeekRequest;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.response.CurriculumWeekResponse;
import com.bcsdlab.bcsdinternalapiv2.global.controller.dto.request.OrderRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;

@Tag(name = "관리자 - 커리큘럼 주차 API")
@SecurityRequirement(name = "JWT")
public interface AdminCurriculumWeekApi {

@ApiResponses(value = {
@ApiResponse(responseCode = "201"),
@ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))),
})
@Operation(summary = "주차 추가", description = "weekTo가 없으면 단일 주차, 있으면 범위다. weekTo < weekFrom이면 400(AC-2.5).")
ResponseEntity<CurriculumWeekResponse> createWeek(@PathVariable Long curriculumId,
@RequestBody @Valid WeekRequest request);

@ApiResponses(value = {
@ApiResponse(responseCode = "204"),
@ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
})
@Operation(summary = "주차 순서 변경")
ResponseEntity<Void> reorderWeeks(@PathVariable Long curriculumId, @RequestBody @Valid OrderRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.bcsdlab.bcsdinternalapiv2.curriculum.controller;

import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request.WeekRequest;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.response.CurriculumWeekResponse;
import com.bcsdlab.bcsdinternalapiv2.curriculum.service.AdminCurriculumTreeService;
import com.bcsdlab.bcsdinternalapiv2.global.controller.dto.request.OrderRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/v1/admin/curriculums/{curriculumId}/weeks")
@RequiredArgsConstructor
public class AdminCurriculumWeekController implements AdminCurriculumWeekApi {

private final AdminCurriculumTreeService adminCurriculumTreeService;

@Override
@PostMapping
public ResponseEntity<CurriculumWeekResponse> createWeek(@PathVariable Long curriculumId,
@Valid @RequestBody WeekRequest request) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(adminCurriculumTreeService.createWeek(curriculumId, request));
}

@Override
@PatchMapping("/order")
public ResponseEntity<Void> reorderWeeks(@PathVariable Long curriculumId, @Valid @RequestBody OrderRequest request) {
adminCurriculumTreeService.reorderWeeks(curriculumId, request);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.bcsdlab.bcsdinternalapiv2.curriculum.controller;

import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request.TopicDetailsReplaceRequest;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request.TopicRequest;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.response.CurriculumTopicResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.util.List;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;

@Tag(name = "관리자 - 커리큘럼 토픽 API")
@SecurityRequirement(name = "JWT")
public interface AdminTopicApi {

@ApiResponses(value = {
@ApiResponse(responseCode = "200"),
@ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))),
})
@Operation(summary = "토픽 제목 수정")
CurriculumTopicResponse updateTopic(@PathVariable Long id, @RequestBody @Valid TopicRequest request);

@ApiResponses(value = {
@ApiResponse(responseCode = "204"),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))),
})
@Operation(summary = "토픽 삭제", description = "하위 세부항목이 cascade로 함께 삭제된다.")
ResponseEntity<Void> deleteTopic(@PathVariable Long id);

@ApiResponses(value = {
@ApiResponse(responseCode = "200"),
@ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))),
})
@Operation(summary = "세부 항목 전체 교체", description = "빈 배열이면 전부 삭제된다(AC-2.9, 에러가 아니다).")
List<String> replaceDetails(@PathVariable Long id, @RequestBody @Valid TopicDetailsReplaceRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.bcsdlab.bcsdinternalapiv2.curriculum.controller;

import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request.TopicDetailsReplaceRequest;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request.TopicRequest;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.response.CurriculumTopicResponse;
import com.bcsdlab.bcsdinternalapiv2.curriculum.service.AdminCurriculumTreeService;
import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/v1/admin/topics")
@RequiredArgsConstructor
public class AdminTopicController implements AdminTopicApi {

private final AdminCurriculumTreeService adminCurriculumTreeService;

@Override
@PutMapping("/{id}")
public CurriculumTopicResponse updateTopic(@PathVariable Long id, @Valid @RequestBody TopicRequest request) {
return adminCurriculumTreeService.updateTopic(id, request);
}

@Override
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteTopic(@PathVariable Long id) {
adminCurriculumTreeService.deleteTopic(id);
return ResponseEntity.noContent().build();
}

@Override
@PutMapping("/{id}/details")
public List<String> replaceDetails(@PathVariable Long id, @Valid @RequestBody TopicDetailsReplaceRequest request) {
return adminCurriculumTreeService.replaceDetails(id, request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.bcsdlab.bcsdinternalapiv2.curriculum.controller;

import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request.TopicRequest;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request.WeekRequest;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.response.CurriculumTopicResponse;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.response.CurriculumWeekResponse;
import com.bcsdlab.bcsdinternalapiv2.global.controller.dto.request.OrderRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;

@Tag(name = "관리자 - 커리큘럼 주차 API")
@SecurityRequirement(name = "JWT")
public interface AdminWeekApi {

@ApiResponses(value = {
@ApiResponse(responseCode = "200"),
@ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))),
})
@Operation(summary = "주차 라벨 수정")
CurriculumWeekResponse updateWeek(@PathVariable Long id, @RequestBody @Valid WeekRequest request);

@ApiResponses(value = {
@ApiResponse(responseCode = "204"),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))),
})
@Operation(summary = "주차 삭제", description = "하위 토픽·세부항목이 cascade로 함께 삭제된다(AC-2.3).")
ResponseEntity<Void> deleteWeek(@PathVariable Long id);

@ApiResponses(value = {
@ApiResponse(responseCode = "201"),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))),
})
@Operation(summary = "토픽 추가")
ResponseEntity<CurriculumTopicResponse> createTopic(@PathVariable Long id, @RequestBody @Valid TopicRequest request);

@ApiResponses(value = {
@ApiResponse(responseCode = "204"),
@ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
})
@Operation(summary = "토픽 순서 변경", description = "번호(1,2,3…)는 배열 순서로 자동 재부여된다(AC-2.6).")
ResponseEntity<Void> reorderTopics(@PathVariable Long id, @RequestBody @Valid OrderRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.bcsdlab.bcsdinternalapiv2.curriculum.controller;

import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request.TopicRequest;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request.WeekRequest;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.response.CurriculumTopicResponse;
import com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.response.CurriculumWeekResponse;
import com.bcsdlab.bcsdinternalapiv2.curriculum.service.AdminCurriculumTreeService;
import com.bcsdlab.bcsdinternalapiv2.global.controller.dto.request.OrderRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/v1/admin/weeks")
@RequiredArgsConstructor
public class AdminWeekController implements AdminWeekApi {

private final AdminCurriculumTreeService adminCurriculumTreeService;

@Override
@PutMapping("/{id}")
public CurriculumWeekResponse updateWeek(@PathVariable Long id, @Valid @RequestBody WeekRequest request) {
return adminCurriculumTreeService.updateWeek(id, request);
}

@Override
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteWeek(@PathVariable Long id) {
adminCurriculumTreeService.deleteWeek(id);
return ResponseEntity.noContent().build();
}

@Override
@PostMapping("/{id}/topics")
public ResponseEntity<CurriculumTopicResponse> createTopic(@PathVariable Long id,
@Valid @RequestBody TopicRequest request) {
return ResponseEntity.status(HttpStatus.CREATED).body(adminCurriculumTreeService.createTopic(id, request));
}

@Override
@PatchMapping("/{id}/topics/order")
public ResponseEntity<Void> reorderTopics(@PathVariable Long id, @Valid @RequestBody OrderRequest request) {
adminCurriculumTreeService.reorderTopics(id, request);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.util.List;

/**
* 세부 항목 전체 교체. 빈 배열이면 전부 삭제된다(AC-2.9, 에러가 아니다).
*/
public record TopicDetailsReplaceRequest(
@NotNull
List<@NotBlank @Size(max = 300) String> contents
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record TopicRequest(
@NotBlank
@Size(max = 200)
String title
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.bcsdlab.bcsdinternalapiv2.curriculum.controller.dto.request;

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;

/**
* 주차 생성/수정 공용. weekTo가 null이면 단일 주차, 있으면 범위(예: 14~17주차)다.
* weekTo &lt; weekFrom은 서비스 레이어에서 걸러 WEEK_RANGE_INVALID(400)를 던진다 —
* DB의 ck_week_range도 같은 제약을 갖고 있지만, 더 명확한 메시지를 먼저 준다.
*/
public record WeekRequest(
@NotNull
@Min(1)
@Max(99)
Integer weekFrom,

Integer weekTo
) {
}
Loading