diff --git a/src/main/java/aplp/backend/lms/course/domain/entities/Course.java b/src/main/java/aplp/backend/lms/course/domain/entities/Course.java index 08d5ff4..5414639 100644 --- a/src/main/java/aplp/backend/lms/course/domain/entities/Course.java +++ b/src/main/java/aplp/backend/lms/course/domain/entities/Course.java @@ -1,5 +1,6 @@ package aplp.backend.lms.course.domain.entities; +import aplp.backend.core.common.entity.BaseEntity; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; @@ -18,31 +19,11 @@ import java.time.Instant; @Getter @Setter @NoArgsConstructor -public class Course { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; +public class Course extends BaseEntity { @Column(nullable = false, length = 200) private String title; @Column(length = 1000) private String description; - - @CreatedBy - @Column(name = "created_by", updatable = false) - private Long createdBy; - - @LastModifiedBy - @Column(name = "updated_by") - private Long updatedBy; - - @CreatedDate - @Column(name = "created_at", nullable = false, updatable = false) - private Instant createdAt; - - @LastModifiedDate - @Column(name = "updated_at", nullable = false) - private Instant updatedAt; } \ No newline at end of file diff --git a/src/main/java/aplp/backend/lms/lesson/api/controller/LessonController.java b/src/main/java/aplp/backend/lms/lesson/api/controller/LessonController.java new file mode 100644 index 0000000..17af4e7 --- /dev/null +++ b/src/main/java/aplp/backend/lms/lesson/api/controller/LessonController.java @@ -0,0 +1,48 @@ +package aplp.backend.lms.lesson.api.controller; + +import aplp.backend.lms.lesson.application.dtos.LessonReqDto; +import aplp.backend.lms.lesson.application.dtos.LessonResDto; +import aplp.backend.lms.lesson.application.services.LessonService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/api/lessons") +@RequiredArgsConstructor +public class LessonController { + + private final LessonService lessonService; + + @GetMapping + public List getAll() { + return lessonService.getAll(); + } + + @GetMapping("/{id}") + public LessonResDto getById(@PathVariable Long id) { + return lessonService.getById(id); + } + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public LessonResDto create(@Valid @RequestBody LessonReqDto request) { + return lessonService.create(request); + } + + @PutMapping("/{id}") + public LessonResDto update( + @PathVariable Long id, + @Valid @RequestBody LessonReqDto request) { + return lessonService.update(id, request); + } + + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void delete(@PathVariable Long id) { + lessonService.delete(id); + } +} diff --git a/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonReqDto.java b/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonReqDto.java new file mode 100644 index 0000000..8c4af4e --- /dev/null +++ b/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonReqDto.java @@ -0,0 +1,17 @@ +package aplp.backend.lms.lesson.application.dtos; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +public record LessonReqDto( + @NotBlank(message = "Lesson code is required") + @Size(max = 50, message = "Lesson code must not exceed 50 characters") + String lessonCode, + + @NotBlank(message = "Lesson name is required") + @Size(max = 200, message = "Lesson name must not exceed 200 characters") + String lessonName, + + @Size(max = 1000, message = "Description must not exceed 1000 characters") + String description +) {} diff --git a/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonResDto.java b/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonResDto.java new file mode 100644 index 0000000..fc7774b --- /dev/null +++ b/src/main/java/aplp/backend/lms/lesson/application/dtos/LessonResDto.java @@ -0,0 +1,25 @@ +package aplp.backend.lms.lesson.application.dtos; + +import aplp.backend.lms.lesson.domain.entities.Lesson; + +import java.time.LocalDateTime; + +public record LessonResDto( + Long id, + String lessonCode, + String lessonName, + String description, + LocalDateTime createdAt, + LocalDateTime updatedAt +) { + public static LessonResDto from(Lesson lesson) { + return new LessonResDto( + lesson.getId(), + lesson.getLessonCode(), + lesson.getLessonName(), + lesson.getDescription(), + lesson.getCreatedAt(), + lesson.getUpdatedAt() + ); + } +} diff --git a/src/main/java/aplp/backend/lms/lesson/application/mappers/LessonMapper.java b/src/main/java/aplp/backend/lms/lesson/application/mappers/LessonMapper.java new file mode 100644 index 0000000..a655a76 --- /dev/null +++ b/src/main/java/aplp/backend/lms/lesson/application/mappers/LessonMapper.java @@ -0,0 +1,20 @@ +package aplp.backend.lms.lesson.application.mappers; + +import aplp.backend.lms.lesson.application.dtos.LessonReqDto; +import aplp.backend.lms.lesson.application.dtos.LessonResDto; +import aplp.backend.lms.lesson.domain.entities.Lesson; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; + +@Mapper(componentModel = "spring") +public interface LessonMapper { + + LessonResDto toResponse(Lesson lesson); + + Lesson toEntity(LessonReqDto request); + + void updateEntity( + LessonReqDto request, + @MappingTarget Lesson lesson + ); +} diff --git a/src/main/java/aplp/backend/lms/lesson/application/services/LessonService.java b/src/main/java/aplp/backend/lms/lesson/application/services/LessonService.java new file mode 100644 index 0000000..babdc95 --- /dev/null +++ b/src/main/java/aplp/backend/lms/lesson/application/services/LessonService.java @@ -0,0 +1,52 @@ +package aplp.backend.lms.lesson.application.services; + +import aplp.backend.core.common.exception.ResourceNotFoundException; +import aplp.backend.lms.lesson.application.dtos.LessonReqDto; +import aplp.backend.lms.lesson.application.dtos.LessonResDto; +import aplp.backend.lms.lesson.application.mappers.LessonMapper; +import aplp.backend.lms.lesson.domain.entities.Lesson; +import aplp.backend.lms.lesson.domain.repositories.LessonRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class LessonService { + + private final LessonRepository lessonRepository; + private final LessonMapper lessonMapper; + + public List getAll() { + return lessonRepository.findAll() + .stream() + .map(LessonResDto::from) + .toList(); + } + + public LessonResDto getById(Long id) { + Lesson lesson = lessonRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Lesson not found")); + + return LessonResDto.from(lesson); + } + + public LessonResDto create(LessonReqDto request) { + Lesson lesson = lessonMapper.toEntity(request); + return lessonMapper.toResponse(lessonRepository.save(lesson)); + } + + public LessonResDto update(Long id, LessonReqDto request) { + Lesson lesson = lessonRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Lesson not found")); + + lessonMapper.updateEntity(request, lesson); + + return lessonMapper.toResponse(lessonRepository.save(lesson)); + } + + public void delete(Long id) { + lessonRepository.deleteById(id); + } +} diff --git a/src/main/java/aplp/backend/lms/lesson/domain/entities/Lesson.java b/src/main/java/aplp/backend/lms/lesson/domain/entities/Lesson.java new file mode 100644 index 0000000..dd69bf3 --- /dev/null +++ b/src/main/java/aplp/backend/lms/lesson/domain/entities/Lesson.java @@ -0,0 +1,26 @@ +package aplp.backend.lms.lesson.domain.entities; + +import aplp.backend.core.common.entity.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Table(name = "lessons") +@Getter +@Setter +@NoArgsConstructor +public class Lesson extends BaseEntity { + + @Column(name = "lesson_code", nullable = false, length = 50, unique = true) + private String lessonCode; + + @Column(name = "lesson_name", nullable = false, length = 200) + private String lessonName; + + @Column(length = 1000) + private String description; +} diff --git a/src/main/java/aplp/backend/lms/lesson/domain/repositories/LessonRepository.java b/src/main/java/aplp/backend/lms/lesson/domain/repositories/LessonRepository.java new file mode 100644 index 0000000..808dcd6 --- /dev/null +++ b/src/main/java/aplp/backend/lms/lesson/domain/repositories/LessonRepository.java @@ -0,0 +1,19 @@ +package aplp.backend.lms.lesson.domain.repositories; + +import aplp.backend.lms.lesson.domain.entities.Lesson; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface LessonRepository { + + Optional findById(Long id); + + List findAll(); + + Lesson save(Lesson lesson); + + void deleteById(Long id); +} diff --git a/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonJpaRepository.java b/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonJpaRepository.java new file mode 100644 index 0000000..e0ae1ae --- /dev/null +++ b/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonJpaRepository.java @@ -0,0 +1,9 @@ +package aplp.backend.lms.lesson.infrastructure.persistence; + +import aplp.backend.lms.lesson.domain.entities.Lesson; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface LessonJpaRepository extends JpaRepository { +} diff --git a/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonRepositoryImpl.java b/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonRepositoryImpl.java new file mode 100644 index 0000000..87efb8b --- /dev/null +++ b/src/main/java/aplp/backend/lms/lesson/infrastructure/persistence/LessonRepositoryImpl.java @@ -0,0 +1,35 @@ +package aplp.backend.lms.lesson.infrastructure.persistence; + +import aplp.backend.lms.lesson.domain.entities.Lesson; +import aplp.backend.lms.lesson.domain.repositories.LessonRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class LessonRepositoryImpl implements LessonRepository { + private final LessonJpaRepository repository; + + @Override + public Optional findById(Long id) { + return repository.findById(id); + } + + @Override + public List findAll() { + return repository.findAll(); + } + + @Override + public Lesson save(Lesson lesson) { + return repository.save(lesson); + } + + @Override + public void deleteById(Long id) { + repository.deleteById(id); + } +} diff --git a/src/main/resources/db/migration/V2__course_audit_columns.sql b/src/main/resources/db/migration/V2__course_audit_columns.sql deleted file mode 100644 index 27f1181..0000000 --- a/src/main/resources/db/migration/V2__course_audit_columns.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Flyway Migration V2: Add audit columns to courses - -ALTER TABLE courses - ADD COLUMN IF NOT EXISTS created_by BIGINT, - ADD COLUMN IF NOT EXISTS updated_by BIGINT, - ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT now(); diff --git a/src/main/resources/db/migration/V2__lesson_module.sql b/src/main/resources/db/migration/V2__lesson_module.sql new file mode 100644 index 0000000..bebe101 --- /dev/null +++ b/src/main/resources/db/migration/V2__lesson_module.sql @@ -0,0 +1,14 @@ +-- Flyway Migration V3: Create lessons table + +CREATE TABLE IF NOT EXISTS lessons ( + id BIGSERIAL PRIMARY KEY, + lesson_code VARCHAR(50) NOT NULL UNIQUE, + lesson_name VARCHAR(200) NOT NULL, + description VARCHAR(1000), + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + created_by VARCHAR(255), + updated_by VARCHAR(255), + deleted_at TIMESTAMPTZ, + deleted_by VARCHAR(255) +);