feat: update codes
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
package aplp.backend.lms.common.api;
|
||||||
|
|
||||||
|
import aplp.backend.core.common.exception.ResourceNotFoundException;
|
||||||
|
import aplp.backend.core.common.response.ErrorResponse;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
@ExceptionHandler(ResourceNotFoundException.class)
|
||||||
|
public ResponseEntity<ErrorResponse> handleNotFound(
|
||||||
|
ResourceNotFoundException ex
|
||||||
|
) {
|
||||||
|
return ResponseEntity
|
||||||
|
.status(HttpStatus.NOT_FOUND)
|
||||||
|
.body(new ErrorResponse(
|
||||||
|
HttpStatus.NOT_FOUND.value(),
|
||||||
|
ex.getMessage(),
|
||||||
|
LocalDateTime.now()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public ResponseEntity<ErrorResponse> handleException(
|
||||||
|
Exception ex
|
||||||
|
) {
|
||||||
|
return ResponseEntity
|
||||||
|
.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
|
.body(new ErrorResponse(
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR.value(),
|
||||||
|
"Internal server error",
|
||||||
|
LocalDateTime.now()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package aplp.backend.lms.common.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.domain.AuditorAware;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||||
|
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
|
||||||
|
public class JpaAuditingConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AuditorAware<Long> auditorProvider() {
|
||||||
|
return () -> {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (authentication == null
|
||||||
|
|| !authentication.isAuthenticated()
|
||||||
|
|| authentication instanceof AnonymousAuthenticationToken) {
|
||||||
|
return java.util.Optional.empty();
|
||||||
|
}
|
||||||
|
if (authentication.getPrincipal() instanceof Long userId) {
|
||||||
|
return java.util.Optional.of(userId);
|
||||||
|
}
|
||||||
|
return java.util.Optional.empty();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package aplp.backend.lms.common.config;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
|
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
||||||
|
import io.swagger.v3.oas.annotations.info.Info;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||||
|
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@OpenAPIDefinition(
|
||||||
|
info = @Info(
|
||||||
|
title = "APLP LMS API",
|
||||||
|
version = "v1",
|
||||||
|
description = "APLP Learning Management System API"
|
||||||
|
),
|
||||||
|
security = {
|
||||||
|
@SecurityRequirement(name = "bearerAuth")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@SecurityScheme(
|
||||||
|
name = "bearerAuth",
|
||||||
|
type = SecuritySchemeType.HTTP,
|
||||||
|
bearerFormat = "JWT",
|
||||||
|
scheme = "bearer"
|
||||||
|
)
|
||||||
|
public class OpenApiConfig {
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package aplp.backend.lms.common.security;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain securityFilterChain(
|
||||||
|
HttpSecurity http
|
||||||
|
) throws Exception {
|
||||||
|
|
||||||
|
http
|
||||||
|
.csrf(AbstractHttpConfigurer::disable)
|
||||||
|
.authorizeHttpRequests(auth -> auth
|
||||||
|
.anyRequest().permitAll()
|
||||||
|
);
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package aplp.backend.lms.course.api.controller;
|
||||||
|
|
||||||
|
import aplp.backend.lms.course.application.dtos.CourseReqDto;
|
||||||
|
import aplp.backend.lms.course.application.dtos.CourseResDto;
|
||||||
|
import aplp.backend.lms.course.application.services.CourseService;
|
||||||
|
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/courses")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CourseController {
|
||||||
|
|
||||||
|
private final CourseService courseService;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<CourseResDto> getAll() {
|
||||||
|
return courseService.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public CourseResDto getById(@PathVariable Long id) {
|
||||||
|
return courseService.getById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
@ResponseStatus(HttpStatus.CREATED)
|
||||||
|
public CourseResDto create(@Valid @RequestBody CourseReqDto request) {
|
||||||
|
return courseService.create(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public CourseResDto update(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody CourseReqDto request) {
|
||||||
|
return courseService.update(id, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
|
public void delete(@PathVariable Long id) {
|
||||||
|
courseService.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package aplp.backend.lms.course.application.dtos;
|
||||||
|
|
||||||
|
public record CourseReqDto(
|
||||||
|
String title,
|
||||||
|
String description
|
||||||
|
) {}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package aplp.backend.lms.course.application.dtos;
|
||||||
|
|
||||||
|
import aplp.backend.lms.course.domain.entities.Course;
|
||||||
|
|
||||||
|
public record CourseResDto(
|
||||||
|
Long id,
|
||||||
|
String title,
|
||||||
|
String description
|
||||||
|
) {
|
||||||
|
public static CourseResDto from(Course course) {
|
||||||
|
return new CourseResDto(
|
||||||
|
course.getId(),
|
||||||
|
course.getTitle(),
|
||||||
|
course.getDescription()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package aplp.backend.lms.course.application.mappers;
|
||||||
|
|
||||||
|
import aplp.backend.lms.course.application.dtos.CourseReqDto;
|
||||||
|
import aplp.backend.lms.course.application.dtos.CourseResDto;
|
||||||
|
import aplp.backend.lms.course.domain.entities.Course;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
|
||||||
|
@Mapper(componentModel = "spring")
|
||||||
|
public interface CourseMapper {
|
||||||
|
|
||||||
|
CourseResDto toResponse(Course course);
|
||||||
|
|
||||||
|
Course toEntity(CourseReqDto request);
|
||||||
|
|
||||||
|
void updateEntity(
|
||||||
|
CourseReqDto request,
|
||||||
|
@MappingTarget Course course
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package aplp.backend.lms.course.application.services;
|
||||||
|
|
||||||
|
import aplp.backend.core.common.exception.ResourceNotFoundException;
|
||||||
|
import aplp.backend.lms.course.application.dtos.CourseReqDto;
|
||||||
|
import aplp.backend.lms.course.application.dtos.CourseResDto;
|
||||||
|
import aplp.backend.lms.course.application.mappers.CourseMapper;
|
||||||
|
import aplp.backend.lms.course.domain.entities.Course;
|
||||||
|
import aplp.backend.lms.course.domain.repositories.CourseRepository;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CourseService {
|
||||||
|
|
||||||
|
private final CourseRepository courseRepository;
|
||||||
|
private final CourseMapper courseMapper;
|
||||||
|
|
||||||
|
public List<CourseResDto> getAll() {
|
||||||
|
return courseRepository.findAll()
|
||||||
|
.stream()
|
||||||
|
.map(CourseResDto::from)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CourseResDto getById(Long id) {
|
||||||
|
Course course = courseRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("Course not found"));
|
||||||
|
|
||||||
|
return CourseResDto.from(course);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CourseResDto create(CourseReqDto request) {
|
||||||
|
Course course = courseMapper.toEntity(request);
|
||||||
|
return courseMapper.toResponse(courseRepository.save(course));
|
||||||
|
}
|
||||||
|
|
||||||
|
public CourseResDto update(Long id, CourseReqDto request) {
|
||||||
|
Course course = courseRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("Course not found"));
|
||||||
|
|
||||||
|
courseMapper.updateEntity(request, course);
|
||||||
|
|
||||||
|
return courseMapper.toResponse(courseRepository.save(course));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(Long id) {
|
||||||
|
courseRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package aplp.backend.lms.course.domain.entities;
|
||||||
|
|
||||||
|
import jakarta.persistence.*;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.springframework.data.annotation.CreatedBy;
|
||||||
|
import org.springframework.data.annotation.CreatedDate;
|
||||||
|
import org.springframework.data.annotation.LastModifiedBy;
|
||||||
|
import org.springframework.data.annotation.LastModifiedDate;
|
||||||
|
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "courses")
|
||||||
|
@EntityListeners(AuditingEntityListener.class)
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Course {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package aplp.backend.lms.course.domain.repositories;
|
||||||
|
|
||||||
|
import aplp.backend.lms.course.domain.entities.Course;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface CourseRepository {
|
||||||
|
|
||||||
|
Optional<Course> findById(Long id);
|
||||||
|
|
||||||
|
List<Course> findAll();
|
||||||
|
|
||||||
|
Course save(Course course);
|
||||||
|
|
||||||
|
void deleteById(Long id);
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package aplp.backend.lms.course.infrastructure.persistence;
|
||||||
|
|
||||||
|
import aplp.backend.lms.course.domain.entities.Course;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface CourseJpaRepository extends JpaRepository<Course, Long> {
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package aplp.backend.lms.course.infrastructure.persistence;
|
||||||
|
|
||||||
|
import aplp.backend.lms.course.domain.entities.Course;
|
||||||
|
import aplp.backend.lms.course.domain.repositories.CourseRepository;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CourseRepositoryImpl implements CourseRepository {
|
||||||
|
private final CourseJpaRepository repository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Course> findById(Long id) {
|
||||||
|
return repository.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Course> findAll() {
|
||||||
|
return repository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Course save(Course course) {
|
||||||
|
return repository.save(course);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteById(Long id) {
|
||||||
|
repository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
-- 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();
|
||||||
Reference in New Issue
Block a user