feat: update codes

This commit is contained in:
2026-09-09 22:17:56 +07:00
parent a48d6deb06
commit bba3ed3365
12 changed files with 135 additions and 28 deletions
@@ -5,16 +5,38 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.List;
@Configuration @Configuration
public class SecurityConfig { public class SecurityConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of(
"http://localhost:5173",
"http://localhost:3000"
));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("*"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean @Bean
public SecurityFilterChain securityFilterChain( public SecurityFilterChain securityFilterChain(
HttpSecurity http HttpSecurity http
) throws Exception { ) throws Exception {
http http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth .authorizeHttpRequests(auth -> auth
.anyRequest().permitAll() .anyRequest().permitAll()
@@ -1,6 +1,25 @@
package aplp.backend.lms.course.application.dtos; package aplp.backend.lms.course.application.dtos;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public record CourseReqDto( public record CourseReqDto(
@NotBlank(message = "Course code is required")
@Size(max = 50, message = "Course code must not exceed 50 characters")
String courseCode,
@NotBlank(message = "Title is required")
@Size(max = 200, message = "Title must not exceed 200 characters")
String title, String title,
String description
@Size(max = 255, message = "Slug must not exceed 255 characters")
String slug,
@Size(max = 1000, message = "Description must not exceed 1000 characters")
String description,
@Size(max = 500, message = "Image URL must not exceed 500 characters")
String image,
Boolean isPublished
) {} ) {}
@@ -2,16 +2,30 @@ package aplp.backend.lms.course.application.dtos;
import aplp.backend.lms.course.domain.entities.Course; import aplp.backend.lms.course.domain.entities.Course;
import java.time.LocalDateTime;
public record CourseResDto( public record CourseResDto(
Long id, Long id,
String courseCode,
String title, String title,
String description String slug,
String description,
String image,
boolean isPublished,
LocalDateTime createdAt,
LocalDateTime updatedAt
) { ) {
public static CourseResDto from(Course course) { public static CourseResDto from(Course course) {
return new CourseResDto( return new CourseResDto(
course.getId(), course.getId(),
course.getCourseCode(),
course.getTitle(), course.getTitle(),
course.getDescription() course.getSlug(),
course.getDescription(),
course.getImage(),
course.isPublished(),
course.getCreatedAt(),
course.getUpdatedAt()
); );
} }
} }
@@ -5,14 +5,8 @@ import jakarta.persistence.*;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; 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 org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.Instant;
@Entity @Entity
@Table(name = "courses") @Table(name = "courses")
@EntityListeners(AuditingEntityListener.class) @EntityListeners(AuditingEntityListener.class)
@@ -21,9 +15,21 @@ import java.time.Instant;
@NoArgsConstructor @NoArgsConstructor
public class Course extends BaseEntity { public class Course extends BaseEntity {
@Column(name = "course_code", nullable = false, length = 50, unique = true)
private String courseCode;
@Column(nullable = false, length = 200) @Column(nullable = false, length = 200)
private String title; private String title;
@Column(nullable = false, unique = true, length = 255)
private String slug;
@Column(length = 1000) @Column(length = 1000)
private String description; private String description;
@Column(length = 500)
private String image;
@Column(name = "is_published", nullable = false)
private boolean isPublished;
} }
@@ -1,17 +1,27 @@
package aplp.backend.lms.lesson.application.dtos; package aplp.backend.lms.lesson.application.dtos;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
public record LessonReqDto( public record LessonReqDto(
@NotBlank(message = "Lesson code is required") @NotNull(message = "Course ID is required")
@Size(max = 50, message = "Lesson code must not exceed 50 characters") Long courseId,
String lessonCode,
@NotBlank(message = "Lesson name is required") @NotBlank(message = "Title is required")
@Size(max = 200, message = "Lesson name must not exceed 200 characters") @Size(max = 200, message = "Title must not exceed 200 characters")
String lessonName, String title,
@Size(max = 255, message = "Slug must not exceed 255 characters")
String slug,
@Size(max = 1000, message = "Description must not exceed 1000 characters") @Size(max = 1000, message = "Description must not exceed 1000 characters")
String description String description,
@NotNull(message = "Position is required")
@Min(value = 1, message = "Position must be at least 1")
Integer position,
Boolean isPublished
) {} ) {}
@@ -6,18 +6,24 @@ import java.time.LocalDateTime;
public record LessonResDto( public record LessonResDto(
Long id, Long id,
String lessonCode, Long courseId,
String lessonName, String title,
String slug,
String description, String description,
int position,
boolean isPublished,
LocalDateTime createdAt, LocalDateTime createdAt,
LocalDateTime updatedAt LocalDateTime updatedAt
) { ) {
public static LessonResDto from(Lesson lesson) { public static LessonResDto from(Lesson lesson) {
return new LessonResDto( return new LessonResDto(
lesson.getId(), lesson.getId(),
lesson.getLessonCode(), lesson.getCourse().getId(),
lesson.getLessonName(), lesson.getTitle(),
lesson.getSlug(),
lesson.getDescription(), lesson.getDescription(),
lesson.getPosition(),
lesson.isPublished(),
lesson.getCreatedAt(), lesson.getCreatedAt(),
lesson.getUpdatedAt() lesson.getUpdatedAt()
); );
@@ -1,20 +1,31 @@
package aplp.backend.lms.lesson.application.mappers; package aplp.backend.lms.lesson.application.mappers;
import aplp.backend.lms.course.domain.entities.Course;
import aplp.backend.lms.lesson.application.dtos.LessonReqDto; import aplp.backend.lms.lesson.application.dtos.LessonReqDto;
import aplp.backend.lms.lesson.application.dtos.LessonResDto; import aplp.backend.lms.lesson.application.dtos.LessonResDto;
import aplp.backend.lms.lesson.domain.entities.Lesson; import aplp.backend.lms.lesson.domain.entities.Lesson;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget; import org.mapstruct.MappingTarget;
@Mapper(componentModel = "spring") @Mapper(componentModel = "spring")
public interface LessonMapper { public interface LessonMapper {
@Mapping(source = "course.id", target = "courseId")
LessonResDto toResponse(Lesson lesson); LessonResDto toResponse(Lesson lesson);
@Mapping(source = "courseId", target = "course")
Lesson toEntity(LessonReqDto request); Lesson toEntity(LessonReqDto request);
void updateEntity( void updateEntity(
LessonReqDto request, LessonReqDto request,
@MappingTarget Lesson lesson @MappingTarget Lesson lesson
); );
default Course mapCourse(Long courseId) {
if (courseId == null) return null;
Course course = new Course();
course.setId(courseId);
return course;
}
} }
@@ -1,9 +1,8 @@
package aplp.backend.lms.lesson.domain.entities; package aplp.backend.lms.lesson.domain.entities;
import aplp.backend.core.common.entity.BaseEntity; import aplp.backend.core.common.entity.BaseEntity;
import jakarta.persistence.Column; import aplp.backend.lms.course.domain.entities.Course;
import jakarta.persistence.Entity; import jakarta.persistence.*;
import jakarta.persistence.Table;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
@@ -15,12 +14,22 @@ import lombok.Setter;
@NoArgsConstructor @NoArgsConstructor
public class Lesson extends BaseEntity { public class Lesson extends BaseEntity {
@Column(name = "lesson_code", nullable = false, length = 50, unique = true) @ManyToOne(fetch = FetchType.LAZY)
private String lessonCode; @JoinColumn(name = "course_id", nullable = false)
private Course course;
@Column(name = "lesson_name", nullable = false, length = 200) @Column(nullable = false, length = 200)
private String lessonName; private String title;
@Column(nullable = false, unique = true, length = 255)
private String slug;
@Column(length = 1000) @Column(length = 1000)
private String description; private String description;
@Column(nullable = false)
private int position;
@Column(name = "is_published", nullable = false)
private boolean isPublished;
} }
@@ -13,6 +13,8 @@ public interface LessonRepository {
List<Lesson> findAll(); List<Lesson> findAll();
List<Lesson> findByCourseId(Long courseId);
Lesson save(Lesson lesson); Lesson save(Lesson lesson);
void deleteById(Long id); void deleteById(Long id);
@@ -4,6 +4,9 @@ import aplp.backend.lms.lesson.domain.entities.Lesson;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.List;
@Repository @Repository
public interface LessonJpaRepository extends JpaRepository<Lesson, Long> { public interface LessonJpaRepository extends JpaRepository<Lesson, Long> {
List<Lesson> findByCourseIdOrderByPositionAsc(Long courseId);
} }
@@ -23,6 +23,11 @@ public class LessonRepositoryImpl implements LessonRepository {
return repository.findAll(); return repository.findAll();
} }
@Override
public List<Lesson> findByCourseId(Long courseId) {
return repository.findByCourseIdOrderByPositionAsc(courseId);
}
@Override @Override
public Lesson save(Lesson lesson) { public Lesson save(Lesson lesson) {
return repository.save(lesson); return repository.save(lesson);
+1 -1
View File
@@ -3,7 +3,7 @@ spring:
name: lms name: lms
datasource: datasource:
url: jdbc:postgresql://192.168.2.100:5432/aplp url: jdbc:postgresql://pgsql.koda.id.vn:5432/aplp
username: postgres username: postgres
password: Pa55w0rd password: Pa55w0rd