feat: add lesson

This commit is contained in:
2026-09-03 21:50:43 +07:00
parent 6d4964782f
commit a48d6deb06
12 changed files with 267 additions and 28 deletions
@@ -1,5 +1,6 @@
package aplp.backend.lms.course.domain.entities; package aplp.backend.lms.course.domain.entities;
import aplp.backend.core.common.entity.BaseEntity;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -18,31 +19,11 @@ import java.time.Instant;
@Getter @Getter
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor
public class Course { public class Course extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 200) @Column(nullable = false, length = 200)
private String title; private String title;
@Column(length = 1000) @Column(length = 1000)
private String description; 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;
} }
@@ -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<LessonResDto> 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);
}
}
@@ -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
) {}
@@ -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()
);
}
}
@@ -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
);
}
@@ -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<LessonResDto> 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);
}
}
@@ -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;
}
@@ -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<Lesson> findById(Long id);
List<Lesson> findAll();
Lesson save(Lesson lesson);
void deleteById(Long id);
}
@@ -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<Lesson, Long> {
}
@@ -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<Lesson> findById(Long id) {
return repository.findById(id);
}
@Override
public List<Lesson> findAll() {
return repository.findAll();
}
@Override
public Lesson save(Lesson lesson) {
return repository.save(lesson);
}
@Override
public void deleteById(Long id) {
repository.deleteById(id);
}
}
@@ -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();
@@ -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)
);