Spaces:
Sleeping
Sleeping
Optimize Performance
Browse files- .env +3 -0
- src/main/java/com/darkfantasy/config/AsyncConfig.java +41 -0
- src/main/java/com/darkfantasy/config/CacheConfig.java +9 -0
- src/main/java/com/darkfantasy/controller/UserController.java +1 -7
- src/main/java/com/darkfantasy/dto/article/ArticleResponse.java +9 -11
- src/main/java/com/darkfantasy/entity/Article.java +5 -1
- src/main/java/com/darkfantasy/entity/User.java +6 -2
- src/main/java/com/darkfantasy/entity/World.java +14 -1
- src/main/java/com/darkfantasy/repository/ArticleRepository.java +12 -0
- src/main/java/com/darkfantasy/repository/AuditLogRepository.java +4 -0
- src/main/java/com/darkfantasy/repository/ContactMessageRepository.java +6 -0
- src/main/java/com/darkfantasy/repository/ContributorRepository.java +5 -2
- src/main/java/com/darkfantasy/repository/FaqRepository.java +13 -1
- src/main/java/com/darkfantasy/repository/StoryRepository.java +6 -0
- src/main/java/com/darkfantasy/repository/WorldRepository.java +10 -0
- src/main/java/com/darkfantasy/security/CustomUserDetails.java +50 -0
- src/main/java/com/darkfantasy/security/ForceChangePasswordInterceptor.java +3 -0
- src/main/java/com/darkfantasy/service/AuditLogService.java +0 -5
- src/main/java/com/darkfantasy/service/CustomUserDetailsService.java +2 -6
- src/main/java/com/darkfantasy/service/EmailService.java +5 -0
- src/main/java/com/darkfantasy/service/UserService.java +2 -0
- src/main/java/com/darkfantasy/service/impl/ArticleServiceImpl.java +10 -15
- src/main/java/com/darkfantasy/service/impl/AuditLogServiceImpl.java +4 -29
- src/main/java/com/darkfantasy/service/impl/ContactMessageServiceImpl.java +22 -10
- src/main/java/com/darkfantasy/service/impl/ContributorServiceImpl.java +8 -16
- src/main/java/com/darkfantasy/service/impl/EmailServiceImpl.java +27 -0
- src/main/java/com/darkfantasy/service/impl/FaqServiceImpl.java +20 -17
- src/main/java/com/darkfantasy/service/impl/GameCharacterServiceImpl.java +17 -14
- src/main/java/com/darkfantasy/service/impl/StoryServiceImpl.java +17 -13
- src/main/java/com/darkfantasy/service/impl/UserServiceImpl.java +45 -34
- src/main/java/com/darkfantasy/service/impl/WorldServiceImpl.java +28 -17
- src/main/java/com/darkfantasy/util/SecurityUtil.java +19 -27
- src/main/resources/application.properties +34 -2
.env
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SPRING_DATASOURCE_URL=jdbc:mariadb://localhost:3306/MOONBLIGHT
|
| 2 |
+
SPRING_DATASOURCE_USERNAME=root
|
| 3 |
+
SPRING_DATASOURCE_PASSWORD=QQQewqwqa401!
|
src/main/java/com/darkfantasy/config/AsyncConfig.java
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.config;
|
| 2 |
+
|
| 3 |
+
import java.util.concurrent.Executor;
|
| 4 |
+
import java.util.concurrent.ThreadPoolExecutor;
|
| 5 |
+
|
| 6 |
+
import org.springframework.context.annotation.Bean;
|
| 7 |
+
import org.springframework.context.annotation.Configuration;
|
| 8 |
+
import org.springframework.scheduling.annotation.EnableAsync;
|
| 9 |
+
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
| 10 |
+
|
| 11 |
+
@Configuration
|
| 12 |
+
@EnableAsync
|
| 13 |
+
public class AsyncConfig {
|
| 14 |
+
@Bean(name = "taskExecutor")
|
| 15 |
+
public Executor taskExecutor() {
|
| 16 |
+
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
| 17 |
+
|
| 18 |
+
// 1. Số lượng luồng CƠ BẢN luôn được duy trì sẵn sàng (như nhân viên trực ca)
|
| 19 |
+
executor.setCorePoolSize(5);
|
| 20 |
+
|
| 21 |
+
// 2. Kích thước HÀNG ĐỢI. Nếu 5 luồng trên đều bận, các task mới sẽ vào đây xếp
|
| 22 |
+
// hàng
|
| 23 |
+
executor.setQueueCapacity(500);
|
| 24 |
+
|
| 25 |
+
// 3. Số lượng luồng TỐI ĐA. Nếu 500 chỗ xếp hàng đều chật kín, hệ thống mới gọi
|
| 26 |
+
// thêm "nhân viên thời vụ" (tối đa lên 20 luồng)
|
| 27 |
+
executor.setMaxPoolSize(20);
|
| 28 |
+
|
| 29 |
+
// Đặt tên cho luồng để sau này log ra nhìn phát biết ngay
|
| 30 |
+
executor.setThreadNamePrefix("BlightMoon-Async-");
|
| 31 |
+
|
| 32 |
+
// 4. CHÍNH SÁCH XỬ LÝ QUÁ TẢI (Cực kỳ quan trọng)
|
| 33 |
+
// Nếu cả 20 luồng đều bận và 500 chỗ xếp hàng đều đầy thì làm gì?
|
| 34 |
+
// CallerRunsPolicy: Bắt chính cái luồng gọi (ví dụ luồng đang xử lý API) tự đi
|
| 35 |
+
// mà thực thi task đó luôn, tuyệt đối không quăng lỗi làm crash ứng dụng.
|
| 36 |
+
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
| 37 |
+
|
| 38 |
+
executor.initialize();
|
| 39 |
+
return executor;
|
| 40 |
+
}
|
| 41 |
+
}
|
src/main/java/com/darkfantasy/config/CacheConfig.java
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.config;
|
| 2 |
+
|
| 3 |
+
import org.springframework.cache.annotation.EnableCaching;
|
| 4 |
+
import org.springframework.context.annotation.Configuration;
|
| 5 |
+
|
| 6 |
+
@Configuration
|
| 7 |
+
@EnableCaching
|
| 8 |
+
public class CacheConfig {
|
| 9 |
+
}
|
src/main/java/com/darkfantasy/controller/UserController.java
CHANGED
|
@@ -102,13 +102,7 @@ public class UserController {
|
|
| 102 |
HttpServletResponse response,
|
| 103 |
Authentication authentication) {
|
| 104 |
if (authentication != null) {
|
| 105 |
-
|
| 106 |
-
UserResponse user = userService.findByUsername(username);
|
| 107 |
-
auditLogService.log(
|
| 108 |
-
LogEntityType.USER,
|
| 109 |
-
user.getId(),
|
| 110 |
-
LogAction.LOGOUT,
|
| 111 |
-
"Đăng xuất");
|
| 112 |
}
|
| 113 |
|
| 114 |
new SecurityContextLogoutHandler()
|
|
|
|
| 102 |
HttpServletResponse response,
|
| 103 |
Authentication authentication) {
|
| 104 |
if (authentication != null) {
|
| 105 |
+
userService.logLogout();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
}
|
| 107 |
|
| 108 |
new SecurityContextLogoutHandler()
|
src/main/java/com/darkfantasy/dto/article/ArticleResponse.java
CHANGED
|
@@ -1,8 +1,9 @@
|
|
| 1 |
package com.darkfantasy.dto.article;
|
| 2 |
|
|
|
|
|
|
|
| 3 |
import com.darkfantasy.entity.Article;
|
| 4 |
import com.darkfantasy.entity.User;
|
| 5 |
-
import com.darkfantasy.entity.enums.ArticleType;
|
| 6 |
import com.darkfantasy.util.TimeUtil;
|
| 7 |
|
| 8 |
import lombok.AllArgsConstructor;
|
|
@@ -19,6 +20,7 @@ public class ArticleResponse {
|
|
| 19 |
private Long id;
|
| 20 |
private String title;
|
| 21 |
private String content;
|
|
|
|
| 22 |
private String authorUsername;
|
| 23 |
private String thumbnailUrl;
|
| 24 |
private String type;
|
|
@@ -33,26 +35,22 @@ public class ArticleResponse {
|
|
| 33 |
.id(article.getId())
|
| 34 |
.title(article.getTitle())
|
| 35 |
.content(article.getContent())
|
|
|
|
| 36 |
.authorUsername(article.getCreatedBy().getUsername())
|
| 37 |
.thumbnailUrl(article.getThumbnailUrl())
|
| 38 |
.type(article.getType().getDisplayName())
|
| 39 |
-
.createdByUserName(article.getCreatedBy()!= null? article.getCreatedBy().getUsername() : null)
|
| 40 |
.createdAt(TimeUtil.formatInstant(article.getCreatedAt()))
|
| 41 |
-
.updatedByUserName(article.getUpdatedBy()!= null? article.getUpdatedBy().getUsername() : null)
|
| 42 |
.updatedAt(TimeUtil.formatInstant(article.getUpdatedAt()))
|
| 43 |
.deleted(article.isDeleted())
|
| 44 |
.build();
|
| 45 |
}
|
| 46 |
|
| 47 |
-
|
| 48 |
-
if (content == null) {
|
| 49 |
return "";
|
| 50 |
}
|
| 51 |
-
|
| 52 |
-
if (content.length() <= 150) {
|
| 53 |
-
return content;
|
| 54 |
-
}
|
| 55 |
-
|
| 56 |
-
return content.substring(0, 150) + "...";
|
| 57 |
}
|
| 58 |
}
|
|
|
|
| 1 |
package com.darkfantasy.dto.article;
|
| 2 |
|
| 3 |
+
import java.util.Optional;
|
| 4 |
+
|
| 5 |
import com.darkfantasy.entity.Article;
|
| 6 |
import com.darkfantasy.entity.User;
|
|
|
|
| 7 |
import com.darkfantasy.util.TimeUtil;
|
| 8 |
|
| 9 |
import lombok.AllArgsConstructor;
|
|
|
|
| 20 |
private Long id;
|
| 21 |
private String title;
|
| 22 |
private String content;
|
| 23 |
+
private String summary;
|
| 24 |
private String authorUsername;
|
| 25 |
private String thumbnailUrl;
|
| 26 |
private String type;
|
|
|
|
| 35 |
.id(article.getId())
|
| 36 |
.title(article.getTitle())
|
| 37 |
.content(article.getContent())
|
| 38 |
+
.summary(generateSummary(article.getContent()))
|
| 39 |
.authorUsername(article.getCreatedBy().getUsername())
|
| 40 |
.thumbnailUrl(article.getThumbnailUrl())
|
| 41 |
.type(article.getType().getDisplayName())
|
| 42 |
+
.createdByUserName(article.getCreatedBy() != null ? article.getCreatedBy().getUsername() : null)
|
| 43 |
.createdAt(TimeUtil.formatInstant(article.getCreatedAt()))
|
| 44 |
+
.updatedByUserName(article.getUpdatedBy() != null ? article.getUpdatedBy().getUsername() : null)
|
| 45 |
.updatedAt(TimeUtil.formatInstant(article.getUpdatedAt()))
|
| 46 |
.deleted(article.isDeleted())
|
| 47 |
.build();
|
| 48 |
}
|
| 49 |
|
| 50 |
+
private static String generateSummary(String content) {
|
| 51 |
+
if (content == null || content.isBlank()) {
|
| 52 |
return "";
|
| 53 |
}
|
| 54 |
+
return content.length() <= 150 ? content : content.substring(0, 150) + "...";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
}
|
| 56 |
}
|
src/main/java/com/darkfantasy/entity/Article.java
CHANGED
|
@@ -13,6 +13,7 @@ import jakarta.persistence.FetchType;
|
|
| 13 |
import jakarta.persistence.GeneratedValue;
|
| 14 |
import jakarta.persistence.GenerationType;
|
| 15 |
import jakarta.persistence.Id;
|
|
|
|
| 16 |
import jakarta.persistence.JoinColumn;
|
| 17 |
import jakarta.persistence.ManyToOne;
|
| 18 |
import jakarta.persistence.Table;
|
|
@@ -23,7 +24,10 @@ import lombok.NoArgsConstructor;
|
|
| 23 |
import lombok.Setter;
|
| 24 |
|
| 25 |
@Entity
|
| 26 |
-
@Table(name = "articles"
|
|
|
|
|
|
|
|
|
|
| 27 |
@Getter
|
| 28 |
@Setter
|
| 29 |
@NoArgsConstructor
|
|
|
|
| 13 |
import jakarta.persistence.GeneratedValue;
|
| 14 |
import jakarta.persistence.GenerationType;
|
| 15 |
import jakarta.persistence.Id;
|
| 16 |
+
import jakarta.persistence.Index;
|
| 17 |
import jakarta.persistence.JoinColumn;
|
| 18 |
import jakarta.persistence.ManyToOne;
|
| 19 |
import jakarta.persistence.Table;
|
|
|
|
| 24 |
import lombok.Setter;
|
| 25 |
|
| 26 |
@Entity
|
| 27 |
+
@Table(name = "articles", indexes = {
|
| 28 |
+
@Index(name = "idx_article_deleted", columnList = "deleted"),
|
| 29 |
+
@Index(name = "idx_article_created_at", columnList = "created_at")
|
| 30 |
+
})
|
| 31 |
@Getter
|
| 32 |
@Setter
|
| 33 |
@NoArgsConstructor
|
src/main/java/com/darkfantasy/entity/User.java
CHANGED
|
@@ -36,6 +36,7 @@ import jakarta.persistence.Entity;
|
|
| 36 |
import jakarta.persistence.GeneratedValue;
|
| 37 |
import jakarta.persistence.GenerationType;
|
| 38 |
import jakarta.persistence.Id;
|
|
|
|
| 39 |
import jakarta.persistence.Table;
|
| 40 |
import lombok.AllArgsConstructor;
|
| 41 |
import lombok.Builder;
|
|
@@ -44,7 +45,10 @@ import lombok.NoArgsConstructor;
|
|
| 44 |
import lombok.Setter;
|
| 45 |
|
| 46 |
@Entity
|
| 47 |
-
@Table(name = "users"
|
|
|
|
|
|
|
|
|
|
| 48 |
@Getter
|
| 49 |
@Setter
|
| 50 |
@NoArgsConstructor
|
|
@@ -74,7 +78,7 @@ public class User {
|
|
| 74 |
@UpdateTimestamp
|
| 75 |
@Column(name = "updated_at", nullable = false)
|
| 76 |
private Instant updatedAt;
|
| 77 |
-
|
| 78 |
@Column(name = "must_change_password", nullable = false)
|
| 79 |
@Builder.Default
|
| 80 |
private boolean mustChangePassword = false;
|
|
|
|
| 36 |
import jakarta.persistence.GeneratedValue;
|
| 37 |
import jakarta.persistence.GenerationType;
|
| 38 |
import jakarta.persistence.Id;
|
| 39 |
+
import jakarta.persistence.Index;
|
| 40 |
import jakarta.persistence.Table;
|
| 41 |
import lombok.AllArgsConstructor;
|
| 42 |
import lombok.Builder;
|
|
|
|
| 45 |
import lombok.Setter;
|
| 46 |
|
| 47 |
@Entity
|
| 48 |
+
@Table(name = "users", indexes = {
|
| 49 |
+
@Index(name = "idx_user_email", columnList = "email", unique = true),
|
| 50 |
+
@Index(name = "idx_user_username", columnList = "username", unique = true)
|
| 51 |
+
})
|
| 52 |
@Getter
|
| 53 |
@Setter
|
| 54 |
@NoArgsConstructor
|
|
|
|
| 78 |
@UpdateTimestamp
|
| 79 |
@Column(name = "updated_at", nullable = false)
|
| 80 |
private Instant updatedAt;
|
| 81 |
+
|
| 82 |
@Column(name = "must_change_password", nullable = false)
|
| 83 |
@Builder.Default
|
| 84 |
private boolean mustChangePassword = false;
|
src/main/java/com/darkfantasy/entity/World.java
CHANGED
|
@@ -11,6 +11,7 @@ import jakarta.persistence.FetchType;
|
|
| 11 |
import jakarta.persistence.GeneratedValue;
|
| 12 |
import jakarta.persistence.GenerationType;
|
| 13 |
import jakarta.persistence.Id;
|
|
|
|
| 14 |
import jakarta.persistence.JoinColumn;
|
| 15 |
import jakarta.persistence.ManyToOne;
|
| 16 |
import jakarta.persistence.Table;
|
|
@@ -21,7 +22,19 @@ import lombok.NoArgsConstructor;
|
|
| 21 |
import lombok.Setter;
|
| 22 |
|
| 23 |
@Entity
|
| 24 |
-
@Table(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
@Getter
|
| 26 |
@Setter
|
| 27 |
@NoArgsConstructor
|
|
|
|
| 11 |
import jakarta.persistence.GeneratedValue;
|
| 12 |
import jakarta.persistence.GenerationType;
|
| 13 |
import jakarta.persistence.Id;
|
| 14 |
+
import jakarta.persistence.Index;
|
| 15 |
import jakarta.persistence.JoinColumn;
|
| 16 |
import jakarta.persistence.ManyToOne;
|
| 17 |
import jakarta.persistence.Table;
|
|
|
|
| 22 |
import lombok.Setter;
|
| 23 |
|
| 24 |
@Entity
|
| 25 |
+
@Table(
|
| 26 |
+
name = "worlds",
|
| 27 |
+
indexes = {
|
| 28 |
+
// 1. Index đơn cho các cột hay dùng trong mệnh đề WHERE
|
| 29 |
+
@Index(name = "idx_world_deleted", columnList = "deleted"),
|
| 30 |
+
|
| 31 |
+
// 2. Index cho cột hay dùng để sắp xếp (ORDER BY)
|
| 32 |
+
@Index(name = "idx_world_priority", columnList = "priority"),
|
| 33 |
+
|
| 34 |
+
// 3. Index kép (Composite Index):
|
| 35 |
+
@Index(name = "idx_world_deleted_priority", columnList = "deleted, priority")
|
| 36 |
+
}
|
| 37 |
+
)
|
| 38 |
@Getter
|
| 39 |
@Setter
|
| 40 |
@NoArgsConstructor
|
src/main/java/com/darkfantasy/repository/ArticleRepository.java
CHANGED
|
@@ -4,6 +4,7 @@ import java.util.Optional;
|
|
| 4 |
|
| 5 |
import org.springframework.data.domain.Page;
|
| 6 |
import org.springframework.data.domain.Pageable;
|
|
|
|
| 7 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 8 |
import org.springframework.stereotype.Repository;
|
| 9 |
|
|
@@ -12,7 +13,18 @@ import com.darkfantasy.entity.enums.ArticleType;
|
|
| 12 |
|
| 13 |
@Repository
|
| 14 |
public interface ArticleRepository extends JpaRepository<Article, Long> {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
Page<Article> findByDeletedFalseOrderByCreatedAtDesc(Pageable pageable);
|
|
|
|
|
|
|
| 16 |
Optional<Article> findFirstByTypeAndDeletedFalseOrderByCreatedAtDesc(ArticleType type);
|
|
|
|
|
|
|
| 17 |
Optional<Article> findByIdAndDeletedFalse(Long id);
|
|
|
|
|
|
|
|
|
|
| 18 |
}
|
|
|
|
| 4 |
|
| 5 |
import org.springframework.data.domain.Page;
|
| 6 |
import org.springframework.data.domain.Pageable;
|
| 7 |
+
import org.springframework.data.jpa.repository.EntityGraph;
|
| 8 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 9 |
import org.springframework.stereotype.Repository;
|
| 10 |
|
|
|
|
| 13 |
|
| 14 |
@Repository
|
| 15 |
public interface ArticleRepository extends JpaRepository<Article, Long> {
|
| 16 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 17 |
+
Page<Article> findAll(Pageable pageable);
|
| 18 |
+
|
| 19 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 20 |
Page<Article> findByDeletedFalseOrderByCreatedAtDesc(Pageable pageable);
|
| 21 |
+
|
| 22 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 23 |
Optional<Article> findFirstByTypeAndDeletedFalseOrderByCreatedAtDesc(ArticleType type);
|
| 24 |
+
|
| 25 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 26 |
Optional<Article> findByIdAndDeletedFalse(Long id);
|
| 27 |
+
|
| 28 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 29 |
+
Page<Article> findByIdNotAndDeletedFalseOrderByCreatedAtDesc(Long id, Pageable pageable);
|
| 30 |
}
|
src/main/java/com/darkfantasy/repository/AuditLogRepository.java
CHANGED
|
@@ -2,6 +2,7 @@ package com.darkfantasy.repository;
|
|
| 2 |
|
| 3 |
import org.springframework.data.domain.Page;
|
| 4 |
import org.springframework.data.domain.Pageable;
|
|
|
|
| 5 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 6 |
import org.springframework.stereotype.Repository;
|
| 7 |
|
|
@@ -10,4 +11,7 @@ import com.darkfantasy.entity.AuditLog;
|
|
| 10 |
@Repository
|
| 11 |
public interface AuditLogRepository extends JpaRepository<AuditLog, Long> {
|
| 12 |
Page<AuditLog> findAllByOrderByCreatedAtDesc(Pageable pageable);
|
|
|
|
|
|
|
|
|
|
| 13 |
}
|
|
|
|
| 2 |
|
| 3 |
import org.springframework.data.domain.Page;
|
| 4 |
import org.springframework.data.domain.Pageable;
|
| 5 |
+
import org.springframework.data.jpa.repository.EntityGraph;
|
| 6 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 7 |
import org.springframework.stereotype.Repository;
|
| 8 |
|
|
|
|
| 11 |
@Repository
|
| 12 |
public interface AuditLogRepository extends JpaRepository<AuditLog, Long> {
|
| 13 |
Page<AuditLog> findAllByOrderByCreatedAtDesc(Pageable pageable);
|
| 14 |
+
|
| 15 |
+
@EntityGraph(attributePaths = {"user"})
|
| 16 |
+
Page<AuditLog> findAll(Pageable pageable);
|
| 17 |
}
|
src/main/java/com/darkfantasy/repository/ContactMessageRepository.java
CHANGED
|
@@ -2,6 +2,7 @@ package com.darkfantasy.repository;
|
|
| 2 |
|
| 3 |
import org.springframework.data.domain.Page;
|
| 4 |
import org.springframework.data.domain.Pageable;
|
|
|
|
| 5 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 6 |
import org.springframework.stereotype.Repository;
|
| 7 |
|
|
@@ -10,6 +11,11 @@ import com.darkfantasy.entity.ContactMessage;
|
|
| 10 |
@Repository
|
| 11 |
public interface ContactMessageRepository extends JpaRepository<ContactMessage, Long> {
|
| 12 |
Page<ContactMessage> findByProcessedFalse(Pageable pageable);
|
|
|
|
| 13 |
Page<ContactMessage> findByProcessedTrue(Pageable pageable);
|
|
|
|
| 14 |
Long countByProcessedFalse();
|
|
|
|
|
|
|
|
|
|
| 15 |
}
|
|
|
|
| 2 |
|
| 3 |
import org.springframework.data.domain.Page;
|
| 4 |
import org.springframework.data.domain.Pageable;
|
| 5 |
+
import org.springframework.data.jpa.repository.EntityGraph;
|
| 6 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 7 |
import org.springframework.stereotype.Repository;
|
| 8 |
|
|
|
|
| 11 |
@Repository
|
| 12 |
public interface ContactMessageRepository extends JpaRepository<ContactMessage, Long> {
|
| 13 |
Page<ContactMessage> findByProcessedFalse(Pageable pageable);
|
| 14 |
+
|
| 15 |
Page<ContactMessage> findByProcessedTrue(Pageable pageable);
|
| 16 |
+
|
| 17 |
Long countByProcessedFalse();
|
| 18 |
+
|
| 19 |
+
@EntityGraph(attributePaths = { "processedBy" })
|
| 20 |
+
Page<ContactMessage> findAll(Pageable pageable);
|
| 21 |
}
|
src/main/java/com/darkfantasy/repository/ContributorRepository.java
CHANGED
|
@@ -4,14 +4,17 @@ import java.util.Optional;
|
|
| 4 |
|
| 5 |
import org.springframework.data.domain.Page;
|
| 6 |
import org.springframework.data.domain.Pageable;
|
|
|
|
| 7 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 8 |
import org.springframework.stereotype.Repository;
|
| 9 |
|
| 10 |
import com.darkfantasy.entity.Contributor;
|
| 11 |
|
| 12 |
-
|
| 13 |
@Repository
|
| 14 |
-
public interface ContributorRepository extends JpaRepository<Contributor, Long>{
|
|
|
|
| 15 |
Page<Contributor> findByDeletedFalseOrderByPriorityDesc(Pageable pageable);
|
|
|
|
|
|
|
| 16 |
Optional<Contributor> findByIdAndDeletedFalse(Long id);
|
| 17 |
}
|
|
|
|
| 4 |
|
| 5 |
import org.springframework.data.domain.Page;
|
| 6 |
import org.springframework.data.domain.Pageable;
|
| 7 |
+
import org.springframework.data.jpa.repository.EntityGraph;
|
| 8 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 9 |
import org.springframework.stereotype.Repository;
|
| 10 |
|
| 11 |
import com.darkfantasy.entity.Contributor;
|
| 12 |
|
|
|
|
| 13 |
@Repository
|
| 14 |
+
public interface ContributorRepository extends JpaRepository<Contributor, Long> {
|
| 15 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 16 |
Page<Contributor> findByDeletedFalseOrderByPriorityDesc(Pageable pageable);
|
| 17 |
+
|
| 18 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 19 |
Optional<Contributor> findByIdAndDeletedFalse(Long id);
|
| 20 |
}
|
src/main/java/com/darkfantasy/repository/FaqRepository.java
CHANGED
|
@@ -4,15 +4,27 @@ import java.util.List;
|
|
| 4 |
|
| 5 |
import org.springframework.data.domain.Page;
|
| 6 |
import org.springframework.data.domain.Pageable;
|
|
|
|
| 7 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 8 |
import org.springframework.stereotype.Repository;
|
| 9 |
|
| 10 |
import com.darkfantasy.entity.Faq;
|
| 11 |
|
| 12 |
@Repository
|
| 13 |
-
public interface FaqRepository extends JpaRepository<Faq, Long>{
|
|
|
|
| 14 |
Page<Faq> findAllByOrderByPriorityDesc(Pageable pageable);
|
|
|
|
|
|
|
| 15 |
Page<Faq> findByDeletedFalseOrderByPriorityDesc(Pageable pageable);
|
|
|
|
|
|
|
| 16 |
Page<Faq> findAllByOrderByIdAsc(Pageable pageable);
|
|
|
|
|
|
|
| 17 |
List<Faq> findByDeletedFalseOrderByPriorityDesc();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
}
|
|
|
|
| 4 |
|
| 5 |
import org.springframework.data.domain.Page;
|
| 6 |
import org.springframework.data.domain.Pageable;
|
| 7 |
+
import org.springframework.data.jpa.repository.EntityGraph;
|
| 8 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 9 |
import org.springframework.stereotype.Repository;
|
| 10 |
|
| 11 |
import com.darkfantasy.entity.Faq;
|
| 12 |
|
| 13 |
@Repository
|
| 14 |
+
public interface FaqRepository extends JpaRepository<Faq, Long> {
|
| 15 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 16 |
Page<Faq> findAllByOrderByPriorityDesc(Pageable pageable);
|
| 17 |
+
|
| 18 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 19 |
Page<Faq> findByDeletedFalseOrderByPriorityDesc(Pageable pageable);
|
| 20 |
+
|
| 21 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 22 |
Page<Faq> findAllByOrderByIdAsc(Pageable pageable);
|
| 23 |
+
|
| 24 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 25 |
List<Faq> findByDeletedFalseOrderByPriorityDesc();
|
| 26 |
+
|
| 27 |
+
@Override
|
| 28 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 29 |
+
Page<Faq> findAll(Pageable pageable);
|
| 30 |
}
|
src/main/java/com/darkfantasy/repository/StoryRepository.java
CHANGED
|
@@ -4,6 +4,7 @@ import java.util.List;
|
|
| 4 |
|
| 5 |
import org.springframework.data.domain.Page;
|
| 6 |
import org.springframework.data.domain.Pageable;
|
|
|
|
| 7 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 8 |
import org.springframework.stereotype.Repository;
|
| 9 |
|
|
@@ -11,7 +12,12 @@ import com.darkfantasy.entity.Story;
|
|
| 11 |
|
| 12 |
@Repository
|
| 13 |
public interface StoryRepository extends JpaRepository<Story, Long> {
|
|
|
|
| 14 |
Page<Story> findAllByOrderByIdAsc(Pageable pageable);
|
|
|
|
|
|
|
| 15 |
List<Story> findByDeletedFalseOrderByPriorityDesc();
|
|
|
|
|
|
|
| 16 |
Story findTopByDeletedFalseOrderByPriorityDesc();
|
| 17 |
}
|
|
|
|
| 4 |
|
| 5 |
import org.springframework.data.domain.Page;
|
| 6 |
import org.springframework.data.domain.Pageable;
|
| 7 |
+
import org.springframework.data.jpa.repository.EntityGraph;
|
| 8 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 9 |
import org.springframework.stereotype.Repository;
|
| 10 |
|
|
|
|
| 12 |
|
| 13 |
@Repository
|
| 14 |
public interface StoryRepository extends JpaRepository<Story, Long> {
|
| 15 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 16 |
Page<Story> findAllByOrderByIdAsc(Pageable pageable);
|
| 17 |
+
|
| 18 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 19 |
List<Story> findByDeletedFalseOrderByPriorityDesc();
|
| 20 |
+
|
| 21 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 22 |
Story findTopByDeletedFalseOrderByPriorityDesc();
|
| 23 |
}
|
src/main/java/com/darkfantasy/repository/WorldRepository.java
CHANGED
|
@@ -1,9 +1,11 @@
|
|
| 1 |
package com.darkfantasy.repository;
|
| 2 |
|
| 3 |
import java.util.List;
|
|
|
|
| 4 |
|
| 5 |
import org.springframework.data.domain.Page;
|
| 6 |
import org.springframework.data.domain.Pageable;
|
|
|
|
| 7 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 8 |
import org.springframework.stereotype.Repository;
|
| 9 |
|
|
@@ -11,7 +13,15 @@ import com.darkfantasy.entity.World;
|
|
| 11 |
|
| 12 |
@Repository
|
| 13 |
public interface WorldRepository extends JpaRepository<World, Long> {
|
|
|
|
| 14 |
Page<World> findAllByOrderByIdAsc(Pageable pageable);
|
|
|
|
|
|
|
| 15 |
List<World> findByDeletedFalseOrderByPriorityDesc();
|
|
|
|
|
|
|
| 16 |
World findTopByDeletedFalseOrderByPriorityDesc();
|
|
|
|
|
|
|
|
|
|
| 17 |
}
|
|
|
|
| 1 |
package com.darkfantasy.repository;
|
| 2 |
|
| 3 |
import java.util.List;
|
| 4 |
+
import java.util.Optional;
|
| 5 |
|
| 6 |
import org.springframework.data.domain.Page;
|
| 7 |
import org.springframework.data.domain.Pageable;
|
| 8 |
+
import org.springframework.data.jpa.repository.EntityGraph;
|
| 9 |
import org.springframework.data.jpa.repository.JpaRepository;
|
| 10 |
import org.springframework.stereotype.Repository;
|
| 11 |
|
|
|
|
| 13 |
|
| 14 |
@Repository
|
| 15 |
public interface WorldRepository extends JpaRepository<World, Long> {
|
| 16 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 17 |
Page<World> findAllByOrderByIdAsc(Pageable pageable);
|
| 18 |
+
|
| 19 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 20 |
List<World> findByDeletedFalseOrderByPriorityDesc();
|
| 21 |
+
|
| 22 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 23 |
World findTopByDeletedFalseOrderByPriorityDesc();
|
| 24 |
+
|
| 25 |
+
@EntityGraph(attributePaths = { "createdBy", "updatedBy" })
|
| 26 |
+
Optional<World> findById(Long id);
|
| 27 |
}
|
src/main/java/com/darkfantasy/security/CustomUserDetails.java
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.security;
|
| 2 |
+
|
| 3 |
+
import com.darkfantasy.entity.User;
|
| 4 |
+
import lombok.AllArgsConstructor;
|
| 5 |
+
import lombok.Getter;
|
| 6 |
+
import org.springframework.security.core.GrantedAuthority;
|
| 7 |
+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
| 8 |
+
import org.springframework.security.core.userdetails.UserDetails;
|
| 9 |
+
|
| 10 |
+
import java.util.Collection;
|
| 11 |
+
import java.util.List;
|
| 12 |
+
|
| 13 |
+
@Getter
|
| 14 |
+
@AllArgsConstructor
|
| 15 |
+
public class CustomUserDetails implements UserDetails {
|
| 16 |
+
private Long id;
|
| 17 |
+
private String username;
|
| 18 |
+
private String password;
|
| 19 |
+
private boolean isActive;
|
| 20 |
+
private Collection<? extends GrantedAuthority> authorities;
|
| 21 |
+
private String role;
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
public static CustomUserDetails build(User user) {
|
| 25 |
+
List<GrantedAuthority> authorities = List.of(
|
| 26 |
+
new SimpleGrantedAuthority("ROLE_" + user.getRole().name())
|
| 27 |
+
);
|
| 28 |
+
|
| 29 |
+
return new CustomUserDetails(
|
| 30 |
+
user.getId(),
|
| 31 |
+
user.getUsername(),
|
| 32 |
+
user.getPassword(),
|
| 33 |
+
user.isActive(),
|
| 34 |
+
authorities,
|
| 35 |
+
user.getRole().name()
|
| 36 |
+
);
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
@Override
|
| 40 |
+
public boolean isAccountNonExpired() { return true; }
|
| 41 |
+
|
| 42 |
+
@Override
|
| 43 |
+
public boolean isAccountNonLocked() { return true; }
|
| 44 |
+
|
| 45 |
+
@Override
|
| 46 |
+
public boolean isCredentialsNonExpired() { return true; }
|
| 47 |
+
|
| 48 |
+
@Override
|
| 49 |
+
public boolean isEnabled() { return isActive; }
|
| 50 |
+
}
|
src/main/java/com/darkfantasy/security/ForceChangePasswordInterceptor.java
CHANGED
|
@@ -23,6 +23,9 @@ public class ForceChangePasswordInterceptor implements HandlerInterceptor {
|
|
| 23 |
HttpServletResponse response,
|
| 24 |
Object handler)
|
| 25 |
throws Exception {
|
|
|
|
|
|
|
|
|
|
| 26 |
String username = SecurityUtil.getCurrentUserName();
|
| 27 |
if (username == null) {
|
| 28 |
return true;
|
|
|
|
| 23 |
HttpServletResponse response,
|
| 24 |
Object handler)
|
| 25 |
throws Exception {
|
| 26 |
+
if(!SecurityUtil.isAuthenticated()){
|
| 27 |
+
return true;
|
| 28 |
+
}
|
| 29 |
String username = SecurityUtil.getCurrentUserName();
|
| 30 |
if (username == null) {
|
| 31 |
return true;
|
src/main/java/com/darkfantasy/service/AuditLogService.java
CHANGED
|
@@ -9,11 +9,6 @@ import com.darkfantasy.entity.enums.LogAction;
|
|
| 9 |
import com.darkfantasy.entity.enums.LogEntityType;
|
| 10 |
|
| 11 |
public interface AuditLogService {
|
| 12 |
-
void log(
|
| 13 |
-
LogEntityType entityType,
|
| 14 |
-
Long entityId,
|
| 15 |
-
LogAction action,
|
| 16 |
-
String description);
|
| 17 |
|
| 18 |
void log(
|
| 19 |
User user,
|
|
|
|
| 9 |
import com.darkfantasy.entity.enums.LogEntityType;
|
| 10 |
|
| 11 |
public interface AuditLogService {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
void log(
|
| 14 |
User user,
|
src/main/java/com/darkfantasy/service/CustomUserDetailsService.java
CHANGED
|
@@ -7,6 +7,7 @@ import org.springframework.stereotype.Service;
|
|
| 7 |
|
| 8 |
import com.darkfantasy.entity.User;
|
| 9 |
import com.darkfantasy.repository.UserRepository;
|
|
|
|
| 10 |
|
| 11 |
import lombok.RequiredArgsConstructor;
|
| 12 |
|
|
@@ -22,12 +23,7 @@ public class CustomUserDetailsService implements UserDetailsService {
|
|
| 22 |
.orElseThrow(() -> new UsernameNotFoundException(
|
| 23 |
"Không tìm thấy tài khoản"));
|
| 24 |
|
| 25 |
-
return
|
| 26 |
-
.withUsername(user.getUsername())
|
| 27 |
-
.password(user.getPassword())
|
| 28 |
-
.disabled(!user.isActive())
|
| 29 |
-
.authorities("ROLE_" + user.getRole().name())
|
| 30 |
-
.build();
|
| 31 |
}
|
| 32 |
|
| 33 |
}
|
|
|
|
| 7 |
|
| 8 |
import com.darkfantasy.entity.User;
|
| 9 |
import com.darkfantasy.repository.UserRepository;
|
| 10 |
+
import com.darkfantasy.security.CustomUserDetails;
|
| 11 |
|
| 12 |
import lombok.RequiredArgsConstructor;
|
| 13 |
|
|
|
|
| 23 |
.orElseThrow(() -> new UsernameNotFoundException(
|
| 24 |
"Không tìm thấy tài khoản"));
|
| 25 |
|
| 26 |
+
return CustomUserDetails.build(user);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
}
|
| 28 |
|
| 29 |
}
|
src/main/java/com/darkfantasy/service/EmailService.java
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.service;
|
| 2 |
+
|
| 3 |
+
public interface EmailService {
|
| 4 |
+
void sendPasswordResetEmail(String to, String otp);
|
| 5 |
+
}
|
src/main/java/com/darkfantasy/service/UserService.java
CHANGED
|
@@ -41,4 +41,6 @@ public interface UserService {
|
|
| 41 |
void verifyOtp(VerifyOtpRequest request);
|
| 42 |
|
| 43 |
void resetPassword(ResetPasswordRequest request);
|
|
|
|
|
|
|
| 44 |
}
|
|
|
|
| 41 |
void verifyOtp(VerifyOtpRequest request);
|
| 42 |
|
| 43 |
void resetPassword(ResetPasswordRequest request);
|
| 44 |
+
|
| 45 |
+
void logLogout();
|
| 46 |
}
|
src/main/java/com/darkfantasy/service/impl/ArticleServiceImpl.java
CHANGED
|
@@ -26,6 +26,7 @@ import lombok.RequiredArgsConstructor;
|
|
| 26 |
|
| 27 |
@Service
|
| 28 |
@RequiredArgsConstructor
|
|
|
|
| 29 |
public class ArticleServiceImpl implements ArticleService {
|
| 30 |
private final UserRepository userRepository;
|
| 31 |
private final ArticleRepository articleRepository;
|
|
@@ -47,6 +48,7 @@ public class ArticleServiceImpl implements ArticleService {
|
|
| 47 |
article.setCreatedBy(currentUser);
|
| 48 |
Article savedArticle = articleRepository.save(article);
|
| 49 |
auditLogService.log(
|
|
|
|
| 50 |
LogEntityType.ARTICLE,
|
| 51 |
savedArticle.getId(),
|
| 52 |
LogAction.CREATE,
|
|
@@ -60,12 +62,6 @@ public class ArticleServiceImpl implements ArticleService {
|
|
| 60 |
|
| 61 |
Article article = findArticle(request.getId());
|
| 62 |
|
| 63 |
-
String currentUsername = SecurityUtil.getCurrentUserName();
|
| 64 |
-
|
| 65 |
-
if (currentUsername == null) {
|
| 66 |
-
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 67 |
-
}
|
| 68 |
-
|
| 69 |
User currentUser = getCurrentUser();
|
| 70 |
|
| 71 |
article.setTitle(request.getTitle());
|
|
@@ -78,6 +74,7 @@ public class ArticleServiceImpl implements ArticleService {
|
|
| 78 |
|
| 79 |
article.setUpdatedBy(currentUser);
|
| 80 |
auditLogService.log(
|
|
|
|
| 81 |
LogEntityType.ARTICLE,
|
| 82 |
article.getId(),
|
| 83 |
LogAction.UPDATE,
|
|
@@ -96,6 +93,7 @@ public class ArticleServiceImpl implements ArticleService {
|
|
| 96 |
article.setDeleted(true);
|
| 97 |
article.setUpdatedBy(currentUser);
|
| 98 |
auditLogService.log(
|
|
|
|
| 99 |
LogEntityType.ARTICLE,
|
| 100 |
article.getId(),
|
| 101 |
LogAction.DELETE,
|
|
@@ -147,6 +145,7 @@ public class ArticleServiceImpl implements ArticleService {
|
|
| 147 |
article.setDeleted(false);
|
| 148 |
article.setUpdatedBy(currentUser);
|
| 149 |
auditLogService.log(
|
|
|
|
| 150 |
LogEntityType.ARTICLE,
|
| 151 |
article.getId(),
|
| 152 |
LogAction.RESTORE,
|
|
@@ -155,12 +154,10 @@ public class ArticleServiceImpl implements ArticleService {
|
|
| 155 |
|
| 156 |
@Override
|
| 157 |
public List<ArticleResponse> getLatestArticlesExcept(Long id, int limit) {
|
| 158 |
-
Pageable pageable = PageRequest.of(0, limit
|
| 159 |
|
| 160 |
-
return articleRepository.
|
| 161 |
.stream()
|
| 162 |
-
.filter(article -> !article.getId().equals(id))
|
| 163 |
-
.limit(limit)
|
| 164 |
.map(ArticleResponse::fromEntity)
|
| 165 |
.toList();
|
| 166 |
}
|
|
@@ -177,14 +174,12 @@ public class ArticleServiceImpl implements ArticleService {
|
|
| 177 |
|
| 178 |
private User getCurrentUser() {
|
| 179 |
|
| 180 |
-
|
| 181 |
|
| 182 |
-
if (
|
| 183 |
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 184 |
}
|
| 185 |
-
|
| 186 |
-
return userRepository.findUserByUsername(currentUsername)
|
| 187 |
-
.orElseThrow(() -> new ResourceNotFoundException("Không tìm thấy người dùng"));
|
| 188 |
}
|
| 189 |
|
| 190 |
@Override
|
|
|
|
| 26 |
|
| 27 |
@Service
|
| 28 |
@RequiredArgsConstructor
|
| 29 |
+
@Transactional(readOnly = true)
|
| 30 |
public class ArticleServiceImpl implements ArticleService {
|
| 31 |
private final UserRepository userRepository;
|
| 32 |
private final ArticleRepository articleRepository;
|
|
|
|
| 48 |
article.setCreatedBy(currentUser);
|
| 49 |
Article savedArticle = articleRepository.save(article);
|
| 50 |
auditLogService.log(
|
| 51 |
+
currentUser,
|
| 52 |
LogEntityType.ARTICLE,
|
| 53 |
savedArticle.getId(),
|
| 54 |
LogAction.CREATE,
|
|
|
|
| 62 |
|
| 63 |
Article article = findArticle(request.getId());
|
| 64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
User currentUser = getCurrentUser();
|
| 66 |
|
| 67 |
article.setTitle(request.getTitle());
|
|
|
|
| 74 |
|
| 75 |
article.setUpdatedBy(currentUser);
|
| 76 |
auditLogService.log(
|
| 77 |
+
currentUser,
|
| 78 |
LogEntityType.ARTICLE,
|
| 79 |
article.getId(),
|
| 80 |
LogAction.UPDATE,
|
|
|
|
| 93 |
article.setDeleted(true);
|
| 94 |
article.setUpdatedBy(currentUser);
|
| 95 |
auditLogService.log(
|
| 96 |
+
currentUser,
|
| 97 |
LogEntityType.ARTICLE,
|
| 98 |
article.getId(),
|
| 99 |
LogAction.DELETE,
|
|
|
|
| 145 |
article.setDeleted(false);
|
| 146 |
article.setUpdatedBy(currentUser);
|
| 147 |
auditLogService.log(
|
| 148 |
+
currentUser,
|
| 149 |
LogEntityType.ARTICLE,
|
| 150 |
article.getId(),
|
| 151 |
LogAction.RESTORE,
|
|
|
|
| 154 |
|
| 155 |
@Override
|
| 156 |
public List<ArticleResponse> getLatestArticlesExcept(Long id, int limit) {
|
| 157 |
+
Pageable pageable = PageRequest.of(0, limit);
|
| 158 |
|
| 159 |
+
return articleRepository.findByIdNotAndDeletedFalseOrderByCreatedAtDesc(id, pageable)
|
| 160 |
.stream()
|
|
|
|
|
|
|
| 161 |
.map(ArticleResponse::fromEntity)
|
| 162 |
.toList();
|
| 163 |
}
|
|
|
|
| 174 |
|
| 175 |
private User getCurrentUser() {
|
| 176 |
|
| 177 |
+
Long currentUserId = SecurityUtil.getCurrentUserId();
|
| 178 |
|
| 179 |
+
if (currentUserId == null) {
|
| 180 |
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 181 |
}
|
| 182 |
+
return userRepository.getReferenceById(currentUserId);
|
|
|
|
|
|
|
| 183 |
}
|
| 184 |
|
| 185 |
@Override
|
src/main/java/com/darkfantasy/service/impl/AuditLogServiceImpl.java
CHANGED
|
@@ -2,6 +2,7 @@ package com.darkfantasy.service.impl;
|
|
| 2 |
|
| 3 |
import org.springframework.data.domain.Page;
|
| 4 |
import org.springframework.data.domain.Pageable;
|
|
|
|
| 5 |
import org.springframework.stereotype.Service;
|
| 6 |
import org.springframework.transaction.annotation.Transactional;
|
| 7 |
|
|
@@ -19,40 +20,12 @@ import lombok.RequiredArgsConstructor;
|
|
| 19 |
|
| 20 |
@Service
|
| 21 |
@RequiredArgsConstructor
|
|
|
|
| 22 |
public class AuditLogServiceImpl implements AuditLogService {
|
| 23 |
|
| 24 |
private final AuditLogRepository auditLogRepository;
|
| 25 |
private final UserRepository userRepository;
|
| 26 |
|
| 27 |
-
@Transactional
|
| 28 |
-
@Override
|
| 29 |
-
public void log(
|
| 30 |
-
LogEntityType entityType,
|
| 31 |
-
Long entityId,
|
| 32 |
-
LogAction action,
|
| 33 |
-
String description) {
|
| 34 |
-
|
| 35 |
-
String username = SecurityUtil.getCurrentUserName();
|
| 36 |
-
|
| 37 |
-
if (username == null || "anonymousUser".equals(username)) {
|
| 38 |
-
return;
|
| 39 |
-
}
|
| 40 |
-
|
| 41 |
-
User currentUser = userRepository
|
| 42 |
-
.findUserByUsername(username)
|
| 43 |
-
.orElseThrow(() -> new IllegalArgumentException(
|
| 44 |
-
"Không tìm thấy người dùng"));
|
| 45 |
-
|
| 46 |
-
AuditLog log = AuditLog.builder()
|
| 47 |
-
.user(currentUser)
|
| 48 |
-
.entityType(entityType)
|
| 49 |
-
.entityId(entityId)
|
| 50 |
-
.action(action)
|
| 51 |
-
.description(description)
|
| 52 |
-
.build();
|
| 53 |
-
|
| 54 |
-
auditLogRepository.save(log);
|
| 55 |
-
}
|
| 56 |
|
| 57 |
@Override
|
| 58 |
public Page<AuditLogResponse> getLogs(Pageable pageable) {
|
|
@@ -61,6 +34,7 @@ public class AuditLogServiceImpl implements AuditLogService {
|
|
| 61 |
.map(AuditLogResponse::fromEntity);
|
| 62 |
}
|
| 63 |
|
|
|
|
| 64 |
@Transactional
|
| 65 |
@Override
|
| 66 |
public void log(
|
|
@@ -80,4 +54,5 @@ public class AuditLogServiceImpl implements AuditLogService {
|
|
| 80 |
|
| 81 |
auditLogRepository.save(log);
|
| 82 |
}
|
|
|
|
| 83 |
}
|
|
|
|
| 2 |
|
| 3 |
import org.springframework.data.domain.Page;
|
| 4 |
import org.springframework.data.domain.Pageable;
|
| 5 |
+
import org.springframework.scheduling.annotation.Async;
|
| 6 |
import org.springframework.stereotype.Service;
|
| 7 |
import org.springframework.transaction.annotation.Transactional;
|
| 8 |
|
|
|
|
| 20 |
|
| 21 |
@Service
|
| 22 |
@RequiredArgsConstructor
|
| 23 |
+
@Transactional(readOnly = true)
|
| 24 |
public class AuditLogServiceImpl implements AuditLogService {
|
| 25 |
|
| 26 |
private final AuditLogRepository auditLogRepository;
|
| 27 |
private final UserRepository userRepository;
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
@Override
|
| 31 |
public Page<AuditLogResponse> getLogs(Pageable pageable) {
|
|
|
|
| 34 |
.map(AuditLogResponse::fromEntity);
|
| 35 |
}
|
| 36 |
|
| 37 |
+
@Async
|
| 38 |
@Transactional
|
| 39 |
@Override
|
| 40 |
public void log(
|
|
|
|
| 54 |
|
| 55 |
auditLogRepository.save(log);
|
| 56 |
}
|
| 57 |
+
|
| 58 |
}
|
src/main/java/com/darkfantasy/service/impl/ContactMessageServiceImpl.java
CHANGED
|
@@ -8,19 +8,24 @@ import org.springframework.transaction.annotation.Transactional;
|
|
| 8 |
import com.darkfantasy.dto.contact.ContactMessageResponse;
|
| 9 |
import com.darkfantasy.dto.contact.CreateContactMessageRequest;
|
| 10 |
import com.darkfantasy.entity.ContactMessage;
|
|
|
|
| 11 |
import com.darkfantasy.entity.enums.LogAction;
|
| 12 |
import com.darkfantasy.entity.enums.LogEntityType;
|
| 13 |
import com.darkfantasy.repository.ContactMessageRepository;
|
|
|
|
| 14 |
import com.darkfantasy.service.AuditLogService;
|
| 15 |
import com.darkfantasy.service.ContactMessageService;
|
|
|
|
| 16 |
|
| 17 |
import lombok.RequiredArgsConstructor;
|
| 18 |
|
| 19 |
@Service
|
| 20 |
@RequiredArgsConstructor
|
|
|
|
| 21 |
public class ContactMessageServiceImpl implements ContactMessageService {
|
| 22 |
private final ContactMessageRepository contactMessageRepository;
|
| 23 |
private final AuditLogService auditLogService;
|
|
|
|
| 24 |
|
| 25 |
@Transactional
|
| 26 |
@Override
|
|
@@ -32,11 +37,12 @@ public class ContactMessageServiceImpl implements ContactMessageService {
|
|
| 32 |
ContactMessage message = request.toEntity();
|
| 33 |
ContactMessage savedMessage = contactMessageRepository.save(message);
|
| 34 |
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
|
|
|
| 40 |
return ContactMessageResponse.fromEntity(savedMessage);
|
| 41 |
}
|
| 42 |
|
|
@@ -65,11 +71,17 @@ public class ContactMessageServiceImpl implements ContactMessageService {
|
|
| 65 |
|
| 66 |
ContactMessage message = findContactMessage(id);
|
| 67 |
message.setProcessed(true);
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
}
|
| 74 |
|
| 75 |
@Transactional
|
|
|
|
| 8 |
import com.darkfantasy.dto.contact.ContactMessageResponse;
|
| 9 |
import com.darkfantasy.dto.contact.CreateContactMessageRequest;
|
| 10 |
import com.darkfantasy.entity.ContactMessage;
|
| 11 |
+
import com.darkfantasy.entity.User;
|
| 12 |
import com.darkfantasy.entity.enums.LogAction;
|
| 13 |
import com.darkfantasy.entity.enums.LogEntityType;
|
| 14 |
import com.darkfantasy.repository.ContactMessageRepository;
|
| 15 |
+
import com.darkfantasy.repository.UserRepository;
|
| 16 |
import com.darkfantasy.service.AuditLogService;
|
| 17 |
import com.darkfantasy.service.ContactMessageService;
|
| 18 |
+
import com.darkfantasy.util.SecurityUtil;
|
| 19 |
|
| 20 |
import lombok.RequiredArgsConstructor;
|
| 21 |
|
| 22 |
@Service
|
| 23 |
@RequiredArgsConstructor
|
| 24 |
+
@Transactional(readOnly = true)
|
| 25 |
public class ContactMessageServiceImpl implements ContactMessageService {
|
| 26 |
private final ContactMessageRepository contactMessageRepository;
|
| 27 |
private final AuditLogService auditLogService;
|
| 28 |
+
private final UserRepository userRepository;
|
| 29 |
|
| 30 |
@Transactional
|
| 31 |
@Override
|
|
|
|
| 37 |
ContactMessage message = request.toEntity();
|
| 38 |
ContactMessage savedMessage = contactMessageRepository.save(message);
|
| 39 |
|
| 40 |
+
auditLogService.log(
|
| 41 |
+
null,
|
| 42 |
+
LogEntityType.CONTACT,
|
| 43 |
+
savedMessage.getId(),
|
| 44 |
+
LogAction.CREATE,
|
| 45 |
+
"Khách vãng lai (" + request.getVisitorEmail() + ") gửi liên hệ");
|
| 46 |
return ContactMessageResponse.fromEntity(savedMessage);
|
| 47 |
}
|
| 48 |
|
|
|
|
| 71 |
|
| 72 |
ContactMessage message = findContactMessage(id);
|
| 73 |
message.setProcessed(true);
|
| 74 |
+
Long currentUserId = SecurityUtil.getCurrentUserId();
|
| 75 |
+
if (currentUserId != null) {
|
| 76 |
+
User currentUser = userRepository.getReferenceById(currentUserId);
|
| 77 |
+
auditLogService.log(
|
| 78 |
+
currentUser,
|
| 79 |
+
LogEntityType.CONTACT,
|
| 80 |
+
message.getId(),
|
| 81 |
+
LogAction.PROCESS,
|
| 82 |
+
"Đánh dấu đã xử lý: " + message.getMessage());
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
}
|
| 86 |
|
| 87 |
@Transactional
|
src/main/java/com/darkfantasy/service/impl/ContributorServiceImpl.java
CHANGED
|
@@ -1,9 +1,6 @@
|
|
| 1 |
package com.darkfantasy.service.impl;
|
| 2 |
|
| 3 |
-
import java.util.List;
|
| 4 |
-
|
| 5 |
import org.springframework.data.domain.Page;
|
| 6 |
-
import org.springframework.data.domain.PageRequest;
|
| 7 |
import org.springframework.data.domain.Pageable;
|
| 8 |
import org.springframework.stereotype.Service;
|
| 9 |
import org.springframework.transaction.annotation.Transactional;
|
|
@@ -25,6 +22,7 @@ import lombok.RequiredArgsConstructor;
|
|
| 25 |
|
| 26 |
@Service
|
| 27 |
@RequiredArgsConstructor
|
|
|
|
| 28 |
public class ContributorServiceImpl implements ContributorService {
|
| 29 |
private final UserRepository userRepository;
|
| 30 |
private final ContributorRepository contributorRepository;
|
|
@@ -46,6 +44,7 @@ public class ContributorServiceImpl implements ContributorService {
|
|
| 46 |
contributor.setCreatedBy(currentUser);
|
| 47 |
Contributor savedContributor = contributorRepository.save(contributor);
|
| 48 |
auditLogService.log(
|
|
|
|
| 49 |
LogEntityType.CONTRIBUTOR,
|
| 50 |
savedContributor.getId(),
|
| 51 |
LogAction.CREATE,
|
|
@@ -59,12 +58,6 @@ public class ContributorServiceImpl implements ContributorService {
|
|
| 59 |
|
| 60 |
Contributor contributor = findContributor(request.getId());
|
| 61 |
|
| 62 |
-
String currentUsername = SecurityUtil.getCurrentUserName();
|
| 63 |
-
|
| 64 |
-
if (currentUsername == null) {
|
| 65 |
-
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 66 |
-
}
|
| 67 |
-
|
| 68 |
User currentUser = getCurrentUser();
|
| 69 |
|
| 70 |
contributor.setName(request.getName());
|
|
@@ -77,6 +70,7 @@ public class ContributorServiceImpl implements ContributorService {
|
|
| 77 |
|
| 78 |
contributor.setUpdatedBy(currentUser);
|
| 79 |
auditLogService.log(
|
|
|
|
| 80 |
LogEntityType.CONTRIBUTOR,
|
| 81 |
contributor.getId(),
|
| 82 |
LogAction.UPDATE,
|
|
@@ -95,6 +89,7 @@ public class ContributorServiceImpl implements ContributorService {
|
|
| 95 |
contributor.setDeleted(true);
|
| 96 |
contributor.setUpdatedBy(currentUser);
|
| 97 |
auditLogService.log(
|
|
|
|
| 98 |
LogEntityType.CONTRIBUTOR,
|
| 99 |
contributor.getId(),
|
| 100 |
LogAction.DELETE,
|
|
@@ -126,6 +121,7 @@ public class ContributorServiceImpl implements ContributorService {
|
|
| 126 |
contributor.setDeleted(false);
|
| 127 |
contributor.setUpdatedBy(currentUser);
|
| 128 |
auditLogService.log(
|
|
|
|
| 129 |
LogEntityType.CONTRIBUTOR,
|
| 130 |
contributor.getId(),
|
| 131 |
LogAction.RESTORE,
|
|
@@ -143,14 +139,10 @@ public class ContributorServiceImpl implements ContributorService {
|
|
| 143 |
}
|
| 144 |
|
| 145 |
private User getCurrentUser() {
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
if (currentUsername == null) {
|
| 150 |
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 151 |
}
|
| 152 |
-
|
| 153 |
-
return userRepository.findUserByUsername(currentUsername)
|
| 154 |
-
.orElseThrow(() -> new ResourceNotFoundException("Không tìm thấy người dùng"));
|
| 155 |
}
|
| 156 |
}
|
|
|
|
| 1 |
package com.darkfantasy.service.impl;
|
| 2 |
|
|
|
|
|
|
|
| 3 |
import org.springframework.data.domain.Page;
|
|
|
|
| 4 |
import org.springframework.data.domain.Pageable;
|
| 5 |
import org.springframework.stereotype.Service;
|
| 6 |
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
| 22 |
|
| 23 |
@Service
|
| 24 |
@RequiredArgsConstructor
|
| 25 |
+
@Transactional(readOnly = true)
|
| 26 |
public class ContributorServiceImpl implements ContributorService {
|
| 27 |
private final UserRepository userRepository;
|
| 28 |
private final ContributorRepository contributorRepository;
|
|
|
|
| 44 |
contributor.setCreatedBy(currentUser);
|
| 45 |
Contributor savedContributor = contributorRepository.save(contributor);
|
| 46 |
auditLogService.log(
|
| 47 |
+
currentUser,
|
| 48 |
LogEntityType.CONTRIBUTOR,
|
| 49 |
savedContributor.getId(),
|
| 50 |
LogAction.CREATE,
|
|
|
|
| 58 |
|
| 59 |
Contributor contributor = findContributor(request.getId());
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
User currentUser = getCurrentUser();
|
| 62 |
|
| 63 |
contributor.setName(request.getName());
|
|
|
|
| 70 |
|
| 71 |
contributor.setUpdatedBy(currentUser);
|
| 72 |
auditLogService.log(
|
| 73 |
+
currentUser,
|
| 74 |
LogEntityType.CONTRIBUTOR,
|
| 75 |
contributor.getId(),
|
| 76 |
LogAction.UPDATE,
|
|
|
|
| 89 |
contributor.setDeleted(true);
|
| 90 |
contributor.setUpdatedBy(currentUser);
|
| 91 |
auditLogService.log(
|
| 92 |
+
currentUser,
|
| 93 |
LogEntityType.CONTRIBUTOR,
|
| 94 |
contributor.getId(),
|
| 95 |
LogAction.DELETE,
|
|
|
|
| 121 |
contributor.setDeleted(false);
|
| 122 |
contributor.setUpdatedBy(currentUser);
|
| 123 |
auditLogService.log(
|
| 124 |
+
currentUser,
|
| 125 |
LogEntityType.CONTRIBUTOR,
|
| 126 |
contributor.getId(),
|
| 127 |
LogAction.RESTORE,
|
|
|
|
| 139 |
}
|
| 140 |
|
| 141 |
private User getCurrentUser() {
|
| 142 |
+
Long currentUserId = SecurityUtil.getCurrentUserId();
|
| 143 |
+
if (currentUserId == null) {
|
|
|
|
|
|
|
| 144 |
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 145 |
}
|
| 146 |
+
return userRepository.getReferenceById(currentUserId);
|
|
|
|
|
|
|
| 147 |
}
|
| 148 |
}
|
src/main/java/com/darkfantasy/service/impl/EmailServiceImpl.java
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.service.impl;
|
| 2 |
+
|
| 3 |
+
import org.springframework.mail.SimpleMailMessage;
|
| 4 |
+
import org.springframework.mail.javamail.JavaMailSender;
|
| 5 |
+
import org.springframework.scheduling.annotation.Async;
|
| 6 |
+
import org.springframework.stereotype.Service;
|
| 7 |
+
|
| 8 |
+
import com.darkfantasy.service.EmailService;
|
| 9 |
+
|
| 10 |
+
import lombok.RequiredArgsConstructor;
|
| 11 |
+
|
| 12 |
+
@Service
|
| 13 |
+
@RequiredArgsConstructor
|
| 14 |
+
public class EmailServiceImpl implements EmailService {
|
| 15 |
+
|
| 16 |
+
private final JavaMailSender mailSender;
|
| 17 |
+
|
| 18 |
+
@Async
|
| 19 |
+
@Override
|
| 20 |
+
public void sendPasswordResetEmail(String to, String otp) {
|
| 21 |
+
SimpleMailMessage message = new SimpleMailMessage();
|
| 22 |
+
message.setTo(to);
|
| 23 |
+
message.setSubject("Mã OTP đặt lại mật khẩu");
|
| 24 |
+
message.setText("Mã OTP của bạn là: " + otp + "\n\nMã có hiệu lực trong 5 phút.");
|
| 25 |
+
mailSender.send(message);
|
| 26 |
+
}
|
| 27 |
+
}
|
src/main/java/com/darkfantasy/service/impl/FaqServiceImpl.java
CHANGED
|
@@ -24,6 +24,7 @@ import lombok.RequiredArgsConstructor;
|
|
| 24 |
|
| 25 |
@Service
|
| 26 |
@RequiredArgsConstructor
|
|
|
|
| 27 |
public class FaqServiceImpl implements FaqService {
|
| 28 |
private final FaqRepository faqRepository;
|
| 29 |
private final UserRepository userRepository;
|
|
@@ -44,15 +45,17 @@ public class FaqServiceImpl implements FaqService {
|
|
| 44 |
@Transactional
|
| 45 |
@Override
|
| 46 |
public FaqResponse createFaq(CreateFaqRequest request) {
|
|
|
|
| 47 |
if (request == null) {
|
| 48 |
throw new IllegalArgumentException(
|
| 49 |
"Không thể tạo faq với dữ liệu null");
|
| 50 |
}
|
| 51 |
|
| 52 |
Faq faq = request.toEntity();
|
| 53 |
-
faq.setCreatedBy(
|
| 54 |
Faq savedFaq = faqRepository.save(faq);
|
| 55 |
auditLogService.log(
|
|
|
|
| 56 |
LogEntityType.FAQ,
|
| 57 |
savedFaq.getId(),
|
| 58 |
LogAction.CREATE,
|
|
@@ -63,6 +66,7 @@ public class FaqServiceImpl implements FaqService {
|
|
| 63 |
@Transactional
|
| 64 |
@Override
|
| 65 |
public FaqResponse updateFaq(UpdateFaqRequest request) {
|
|
|
|
| 66 |
if (request == null) {
|
| 67 |
throw new IllegalArgumentException(
|
| 68 |
"Không thể thay đổi faq với dữ liệu null");
|
|
@@ -71,8 +75,9 @@ public class FaqServiceImpl implements FaqService {
|
|
| 71 |
faq.setTitle(request.getTitle());
|
| 72 |
faq.setContent(request.getContent());
|
| 73 |
faq.setPriority(request.getPriority());
|
| 74 |
-
faq.setUpdatedBy(
|
| 75 |
auditLogService.log(
|
|
|
|
| 76 |
LogEntityType.FAQ,
|
| 77 |
faq.getId(),
|
| 78 |
LogAction.UPDATE,
|
|
@@ -83,13 +88,15 @@ public class FaqServiceImpl implements FaqService {
|
|
| 83 |
@Transactional
|
| 84 |
@Override
|
| 85 |
public void deleteFaq(Long id) {
|
|
|
|
| 86 |
if (id == null) {
|
| 87 |
throw new IllegalArgumentException("Không thể xóa faq với ID null");
|
| 88 |
}
|
| 89 |
Faq found = findFaq(id);
|
| 90 |
found.setDeleted(true);
|
| 91 |
-
found.setUpdatedBy(
|
| 92 |
auditLogService.log(
|
|
|
|
| 93 |
LogEntityType.FAQ,
|
| 94 |
found.getId(),
|
| 95 |
LogAction.DELETE,
|
|
@@ -99,13 +106,15 @@ public class FaqServiceImpl implements FaqService {
|
|
| 99 |
@Transactional
|
| 100 |
@Override
|
| 101 |
public void restoreFaq(Long id) {
|
|
|
|
| 102 |
if (id == null) {
|
| 103 |
throw new IllegalArgumentException("Không thể khôi phục faq với ID null");
|
| 104 |
}
|
| 105 |
Faq found = findFaq(id);
|
| 106 |
found.setDeleted(false);
|
| 107 |
-
found.setUpdatedBy(
|
| 108 |
auditLogService.log(
|
|
|
|
| 109 |
LogEntityType.FAQ,
|
| 110 |
found.getId(),
|
| 111 |
LogAction.RESTORE,
|
|
@@ -121,27 +130,21 @@ public class FaqServiceImpl implements FaqService {
|
|
| 121 |
.toList();
|
| 122 |
}
|
| 123 |
|
| 124 |
-
private Faq findFaq(Long id) {
|
| 125 |
-
return faqRepository.findById(id)
|
| 126 |
-
.orElseThrow(() -> new IllegalArgumentException("Không tìm thấy faq với ID: " + id));
|
| 127 |
-
}
|
| 128 |
-
|
| 129 |
@Override
|
| 130 |
public long count() {
|
| 131 |
return faqRepository.count();
|
| 132 |
}
|
| 133 |
|
| 134 |
private User getCurrentUser() {
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
if (currentUsername == null) {
|
| 139 |
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 140 |
}
|
| 141 |
-
|
| 142 |
-
return userRepository
|
| 143 |
-
.findUserByUsername(currentUsername)
|
| 144 |
-
.orElseThrow(() -> new IllegalArgumentException("Không tìm thấy người dùng"));
|
| 145 |
}
|
| 146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
}
|
|
|
|
| 24 |
|
| 25 |
@Service
|
| 26 |
@RequiredArgsConstructor
|
| 27 |
+
@Transactional(readOnly = true)
|
| 28 |
public class FaqServiceImpl implements FaqService {
|
| 29 |
private final FaqRepository faqRepository;
|
| 30 |
private final UserRepository userRepository;
|
|
|
|
| 45 |
@Transactional
|
| 46 |
@Override
|
| 47 |
public FaqResponse createFaq(CreateFaqRequest request) {
|
| 48 |
+
User currentUser = getCurrentUser();
|
| 49 |
if (request == null) {
|
| 50 |
throw new IllegalArgumentException(
|
| 51 |
"Không thể tạo faq với dữ liệu null");
|
| 52 |
}
|
| 53 |
|
| 54 |
Faq faq = request.toEntity();
|
| 55 |
+
faq.setCreatedBy(currentUser);
|
| 56 |
Faq savedFaq = faqRepository.save(faq);
|
| 57 |
auditLogService.log(
|
| 58 |
+
currentUser,
|
| 59 |
LogEntityType.FAQ,
|
| 60 |
savedFaq.getId(),
|
| 61 |
LogAction.CREATE,
|
|
|
|
| 66 |
@Transactional
|
| 67 |
@Override
|
| 68 |
public FaqResponse updateFaq(UpdateFaqRequest request) {
|
| 69 |
+
User currentUser = getCurrentUser();
|
| 70 |
if (request == null) {
|
| 71 |
throw new IllegalArgumentException(
|
| 72 |
"Không thể thay đổi faq với dữ liệu null");
|
|
|
|
| 75 |
faq.setTitle(request.getTitle());
|
| 76 |
faq.setContent(request.getContent());
|
| 77 |
faq.setPriority(request.getPriority());
|
| 78 |
+
faq.setUpdatedBy(currentUser);
|
| 79 |
auditLogService.log(
|
| 80 |
+
currentUser,
|
| 81 |
LogEntityType.FAQ,
|
| 82 |
faq.getId(),
|
| 83 |
LogAction.UPDATE,
|
|
|
|
| 88 |
@Transactional
|
| 89 |
@Override
|
| 90 |
public void deleteFaq(Long id) {
|
| 91 |
+
User currentUser = getCurrentUser();
|
| 92 |
if (id == null) {
|
| 93 |
throw new IllegalArgumentException("Không thể xóa faq với ID null");
|
| 94 |
}
|
| 95 |
Faq found = findFaq(id);
|
| 96 |
found.setDeleted(true);
|
| 97 |
+
found.setUpdatedBy(currentUser);
|
| 98 |
auditLogService.log(
|
| 99 |
+
currentUser,
|
| 100 |
LogEntityType.FAQ,
|
| 101 |
found.getId(),
|
| 102 |
LogAction.DELETE,
|
|
|
|
| 106 |
@Transactional
|
| 107 |
@Override
|
| 108 |
public void restoreFaq(Long id) {
|
| 109 |
+
User currentUser = getCurrentUser();
|
| 110 |
if (id == null) {
|
| 111 |
throw new IllegalArgumentException("Không thể khôi phục faq với ID null");
|
| 112 |
}
|
| 113 |
Faq found = findFaq(id);
|
| 114 |
found.setDeleted(false);
|
| 115 |
+
found.setUpdatedBy(currentUser);
|
| 116 |
auditLogService.log(
|
| 117 |
+
currentUser,
|
| 118 |
LogEntityType.FAQ,
|
| 119 |
found.getId(),
|
| 120 |
LogAction.RESTORE,
|
|
|
|
| 130 |
.toList();
|
| 131 |
}
|
| 132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
@Override
|
| 134 |
public long count() {
|
| 135 |
return faqRepository.count();
|
| 136 |
}
|
| 137 |
|
| 138 |
private User getCurrentUser() {
|
| 139 |
+
Long currentUserId = SecurityUtil.getCurrentUserId();
|
| 140 |
+
if (currentUserId == null) {
|
|
|
|
|
|
|
| 141 |
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 142 |
}
|
| 143 |
+
return userRepository.getReferenceById(currentUserId);
|
|
|
|
|
|
|
|
|
|
| 144 |
}
|
| 145 |
|
| 146 |
+
private Faq findFaq(Long id) {
|
| 147 |
+
return faqRepository.findById(id)
|
| 148 |
+
.orElseThrow(() -> new IllegalArgumentException("Không tìm thấy faq với ID: " + id));
|
| 149 |
+
}
|
| 150 |
}
|
src/main/java/com/darkfantasy/service/impl/GameCharacterServiceImpl.java
CHANGED
|
@@ -25,6 +25,7 @@ import lombok.RequiredArgsConstructor;
|
|
| 25 |
|
| 26 |
@Service
|
| 27 |
@RequiredArgsConstructor
|
|
|
|
| 28 |
public class GameCharacterServiceImpl implements GameCharacterService {
|
| 29 |
|
| 30 |
private final GameCharacterRepository gameCharacterRepository;
|
|
@@ -40,17 +41,18 @@ public class GameCharacterServiceImpl implements GameCharacterService {
|
|
| 40 |
@Override
|
| 41 |
public GameCharacterResponse createGameCharacter(
|
| 42 |
CreateGameCharacterRequest request) {
|
| 43 |
-
|
| 44 |
if (request == null) {
|
| 45 |
throw new IllegalArgumentException(
|
| 46 |
"Không thể tạo nhân vật với dữ liệu null");
|
| 47 |
}
|
| 48 |
|
| 49 |
GameCharacter character = request.toEntity();
|
| 50 |
-
character.setCreatedBy(
|
| 51 |
|
| 52 |
GameCharacter savedCharacter = gameCharacterRepository.save(character);
|
| 53 |
auditLogService.log(
|
|
|
|
| 54 |
LogEntityType.CHARACTER,
|
| 55 |
savedCharacter.getId(),
|
| 56 |
LogAction.CREATE,
|
|
@@ -62,6 +64,7 @@ public class GameCharacterServiceImpl implements GameCharacterService {
|
|
| 62 |
@Transactional
|
| 63 |
@Override
|
| 64 |
public GameCharacterResponse updateGameCharacter(UpdateGameCharacterRequest request) {
|
|
|
|
| 65 |
if (request == null) {
|
| 66 |
throw new IllegalArgumentException(
|
| 67 |
"Không thể thay đổi nhân vật với dữ liệu null");
|
|
@@ -72,11 +75,12 @@ public class GameCharacterServiceImpl implements GameCharacterService {
|
|
| 72 |
character.setDescription(request.getDescription());
|
| 73 |
character.setQuote(request.getQuote());
|
| 74 |
character.setPriority(request.getPriority());
|
| 75 |
-
character.setUpdatedBy(
|
| 76 |
if (request.getImage() != null) {
|
| 77 |
character.setImage(request.getImage());
|
| 78 |
}
|
| 79 |
auditLogService.log(
|
|
|
|
| 80 |
LogEntityType.CHARACTER,
|
| 81 |
character.getId(),
|
| 82 |
LogAction.UPDATE,
|
|
@@ -88,13 +92,15 @@ public class GameCharacterServiceImpl implements GameCharacterService {
|
|
| 88 |
@Transactional
|
| 89 |
@Override
|
| 90 |
public void deleteCharacter(Long id) {
|
|
|
|
| 91 |
if (id == null) {
|
| 92 |
throw new IllegalArgumentException("Không thể xóa nhân vật với ID null");
|
| 93 |
}
|
| 94 |
GameCharacter found = findGameCharacter(id);
|
| 95 |
found.setDeleted(true);
|
| 96 |
-
found.setUpdatedBy(
|
| 97 |
auditLogService.log(
|
|
|
|
| 98 |
LogEntityType.CHARACTER,
|
| 99 |
found.getId(),
|
| 100 |
LogAction.DELETE,
|
|
@@ -104,13 +110,15 @@ public class GameCharacterServiceImpl implements GameCharacterService {
|
|
| 104 |
@Transactional
|
| 105 |
@Override
|
| 106 |
public void restoreCharacter(Long id) {
|
|
|
|
| 107 |
if (id == null) {
|
| 108 |
throw new IllegalArgumentException("Không thể khôi phục nhân vật với ID null");
|
| 109 |
}
|
| 110 |
GameCharacter found = findGameCharacter(id);
|
| 111 |
found.setDeleted(false);
|
| 112 |
-
found.setUpdatedBy(
|
| 113 |
-
|
|
|
|
| 114 |
LogEntityType.CHARACTER,
|
| 115 |
found.getId(),
|
| 116 |
LogAction.RESTORE,
|
|
@@ -154,15 +162,10 @@ public class GameCharacterServiceImpl implements GameCharacterService {
|
|
| 154 |
}
|
| 155 |
|
| 156 |
private User getCurrentUser() {
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
if (currentUsername == null) {
|
| 161 |
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 162 |
}
|
| 163 |
-
|
| 164 |
-
return userRepository
|
| 165 |
-
.findUserByUsername(currentUsername)
|
| 166 |
-
.orElseThrow(() -> new IllegalArgumentException("Không tìm thấy người dùng"));
|
| 167 |
}
|
| 168 |
}
|
|
|
|
| 25 |
|
| 26 |
@Service
|
| 27 |
@RequiredArgsConstructor
|
| 28 |
+
@Transactional(readOnly = true)
|
| 29 |
public class GameCharacterServiceImpl implements GameCharacterService {
|
| 30 |
|
| 31 |
private final GameCharacterRepository gameCharacterRepository;
|
|
|
|
| 41 |
@Override
|
| 42 |
public GameCharacterResponse createGameCharacter(
|
| 43 |
CreateGameCharacterRequest request) {
|
| 44 |
+
User currentUser = getCurrentUser();
|
| 45 |
if (request == null) {
|
| 46 |
throw new IllegalArgumentException(
|
| 47 |
"Không thể tạo nhân vật với dữ liệu null");
|
| 48 |
}
|
| 49 |
|
| 50 |
GameCharacter character = request.toEntity();
|
| 51 |
+
character.setCreatedBy(currentUser);
|
| 52 |
|
| 53 |
GameCharacter savedCharacter = gameCharacterRepository.save(character);
|
| 54 |
auditLogService.log(
|
| 55 |
+
currentUser,
|
| 56 |
LogEntityType.CHARACTER,
|
| 57 |
savedCharacter.getId(),
|
| 58 |
LogAction.CREATE,
|
|
|
|
| 64 |
@Transactional
|
| 65 |
@Override
|
| 66 |
public GameCharacterResponse updateGameCharacter(UpdateGameCharacterRequest request) {
|
| 67 |
+
User currentUser = getCurrentUser();
|
| 68 |
if (request == null) {
|
| 69 |
throw new IllegalArgumentException(
|
| 70 |
"Không thể thay đổi nhân vật với dữ liệu null");
|
|
|
|
| 75 |
character.setDescription(request.getDescription());
|
| 76 |
character.setQuote(request.getQuote());
|
| 77 |
character.setPriority(request.getPriority());
|
| 78 |
+
character.setUpdatedBy(currentUser);
|
| 79 |
if (request.getImage() != null) {
|
| 80 |
character.setImage(request.getImage());
|
| 81 |
}
|
| 82 |
auditLogService.log(
|
| 83 |
+
currentUser,
|
| 84 |
LogEntityType.CHARACTER,
|
| 85 |
character.getId(),
|
| 86 |
LogAction.UPDATE,
|
|
|
|
| 92 |
@Transactional
|
| 93 |
@Override
|
| 94 |
public void deleteCharacter(Long id) {
|
| 95 |
+
User currentUser = getCurrentUser();
|
| 96 |
if (id == null) {
|
| 97 |
throw new IllegalArgumentException("Không thể xóa nhân vật với ID null");
|
| 98 |
}
|
| 99 |
GameCharacter found = findGameCharacter(id);
|
| 100 |
found.setDeleted(true);
|
| 101 |
+
found.setUpdatedBy(currentUser);
|
| 102 |
auditLogService.log(
|
| 103 |
+
currentUser,
|
| 104 |
LogEntityType.CHARACTER,
|
| 105 |
found.getId(),
|
| 106 |
LogAction.DELETE,
|
|
|
|
| 110 |
@Transactional
|
| 111 |
@Override
|
| 112 |
public void restoreCharacter(Long id) {
|
| 113 |
+
User currentUser = getCurrentUser();
|
| 114 |
if (id == null) {
|
| 115 |
throw new IllegalArgumentException("Không thể khôi phục nhân vật với ID null");
|
| 116 |
}
|
| 117 |
GameCharacter found = findGameCharacter(id);
|
| 118 |
found.setDeleted(false);
|
| 119 |
+
found.setUpdatedBy(currentUser);
|
| 120 |
+
auditLogService.log(
|
| 121 |
+
currentUser,
|
| 122 |
LogEntityType.CHARACTER,
|
| 123 |
found.getId(),
|
| 124 |
LogAction.RESTORE,
|
|
|
|
| 162 |
}
|
| 163 |
|
| 164 |
private User getCurrentUser() {
|
| 165 |
+
Long currentUserId = SecurityUtil.getCurrentUserId();
|
| 166 |
+
if (currentUserId == null) {
|
|
|
|
|
|
|
| 167 |
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 168 |
}
|
| 169 |
+
return userRepository.getReferenceById(currentUserId);
|
|
|
|
|
|
|
|
|
|
| 170 |
}
|
| 171 |
}
|
src/main/java/com/darkfantasy/service/impl/StoryServiceImpl.java
CHANGED
|
@@ -24,6 +24,7 @@ import lombok.RequiredArgsConstructor;
|
|
| 24 |
|
| 25 |
@Service
|
| 26 |
@RequiredArgsConstructor
|
|
|
|
| 27 |
public class StoryServiceImpl implements StoryService {
|
| 28 |
private final StoryRepository storyRepository;
|
| 29 |
private final UserRepository userRepository;
|
|
@@ -44,6 +45,7 @@ public class StoryServiceImpl implements StoryService {
|
|
| 44 |
@Transactional
|
| 45 |
@Override
|
| 46 |
public StoryResponse createStory(CreateStoryRequest request) {
|
|
|
|
| 47 |
if (request == null) {
|
| 48 |
throw new IllegalArgumentException(
|
| 49 |
"Không thể tạo câu chuyện với dữ liệu null");
|
|
@@ -51,10 +53,11 @@ public class StoryServiceImpl implements StoryService {
|
|
| 51 |
|
| 52 |
Story story = request.toEntity();
|
| 53 |
|
| 54 |
-
story.setCreatedBy(
|
| 55 |
|
| 56 |
Story savedStory = storyRepository.save(story);
|
| 57 |
auditLogService.log(
|
|
|
|
| 58 |
LogEntityType.STORY,
|
| 59 |
savedStory.getId(),
|
| 60 |
LogAction.CREATE,
|
|
@@ -65,6 +68,7 @@ public class StoryServiceImpl implements StoryService {
|
|
| 65 |
@Transactional
|
| 66 |
@Override
|
| 67 |
public StoryResponse updateStory(UpdateStoryRequest request) {
|
|
|
|
| 68 |
if (request == null) {
|
| 69 |
throw new IllegalArgumentException(
|
| 70 |
"Không thể thay đổi câu chuyện với dữ liệu null");
|
|
@@ -79,8 +83,9 @@ public class StoryServiceImpl implements StoryService {
|
|
| 79 |
story.setQuoteContent(request.getQuoteContent());
|
| 80 |
story.setQuoteAuthor(request.getQuoteAuthor());
|
| 81 |
story.setPriority(request.getPriority());
|
| 82 |
-
story.setUpdatedBy(
|
| 83 |
auditLogService.log(
|
|
|
|
| 84 |
LogEntityType.STORY,
|
| 85 |
story.getId(),
|
| 86 |
LogAction.UPDATE,
|
|
@@ -91,13 +96,15 @@ public class StoryServiceImpl implements StoryService {
|
|
| 91 |
@Transactional
|
| 92 |
@Override
|
| 93 |
public void deleteStory(Long id) {
|
|
|
|
| 94 |
if (id == null) {
|
| 95 |
throw new IllegalArgumentException("Không thể xóa câu chuyện với ID null");
|
| 96 |
}
|
| 97 |
Story found = findStory(id);
|
| 98 |
found.setDeleted(true);
|
| 99 |
-
found.setUpdatedBy(
|
| 100 |
auditLogService.log(
|
|
|
|
| 101 |
LogEntityType.STORY,
|
| 102 |
found.getId(),
|
| 103 |
LogAction.DELETE,
|
|
@@ -107,13 +114,15 @@ public class StoryServiceImpl implements StoryService {
|
|
| 107 |
@Transactional
|
| 108 |
@Override
|
| 109 |
public void restoreStory(Long id) {
|
|
|
|
| 110 |
if (id == null) {
|
| 111 |
throw new IllegalArgumentException("Không thể khôi phục câu chuyện với ID null");
|
| 112 |
}
|
| 113 |
Story found = findStory(id);
|
| 114 |
found.setDeleted(false);
|
| 115 |
-
found.setUpdatedBy(
|
| 116 |
auditLogService.log(
|
|
|
|
| 117 |
LogEntityType.STORY,
|
| 118 |
found.getId(),
|
| 119 |
LogAction.RESTORE,
|
|
@@ -123,7 +132,7 @@ public class StoryServiceImpl implements StoryService {
|
|
| 123 |
@Override
|
| 124 |
public StoryResponse getStoryDeletedFalseHighestPriority() {
|
| 125 |
Story story = storyRepository.findTopByDeletedFalseOrderByPriorityDesc();
|
| 126 |
-
if(story == null){
|
| 127 |
return null;
|
| 128 |
}
|
| 129 |
return StoryResponse.fromEntity(story);
|
|
@@ -149,15 +158,10 @@ public class StoryServiceImpl implements StoryService {
|
|
| 149 |
}
|
| 150 |
|
| 151 |
private User getCurrentUser() {
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
if (currentUsername == null) {
|
| 156 |
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 157 |
}
|
| 158 |
-
|
| 159 |
-
return userRepository
|
| 160 |
-
.findUserByUsername(currentUsername)
|
| 161 |
-
.orElseThrow(() -> new IllegalArgumentException("Không tìm thấy người dùng"));
|
| 162 |
}
|
| 163 |
}
|
|
|
|
| 24 |
|
| 25 |
@Service
|
| 26 |
@RequiredArgsConstructor
|
| 27 |
+
@Transactional(readOnly = true)
|
| 28 |
public class StoryServiceImpl implements StoryService {
|
| 29 |
private final StoryRepository storyRepository;
|
| 30 |
private final UserRepository userRepository;
|
|
|
|
| 45 |
@Transactional
|
| 46 |
@Override
|
| 47 |
public StoryResponse createStory(CreateStoryRequest request) {
|
| 48 |
+
User currentUser = getCurrentUser();
|
| 49 |
if (request == null) {
|
| 50 |
throw new IllegalArgumentException(
|
| 51 |
"Không thể tạo câu chuyện với dữ liệu null");
|
|
|
|
| 53 |
|
| 54 |
Story story = request.toEntity();
|
| 55 |
|
| 56 |
+
story.setCreatedBy(currentUser);
|
| 57 |
|
| 58 |
Story savedStory = storyRepository.save(story);
|
| 59 |
auditLogService.log(
|
| 60 |
+
currentUser,
|
| 61 |
LogEntityType.STORY,
|
| 62 |
savedStory.getId(),
|
| 63 |
LogAction.CREATE,
|
|
|
|
| 68 |
@Transactional
|
| 69 |
@Override
|
| 70 |
public StoryResponse updateStory(UpdateStoryRequest request) {
|
| 71 |
+
User currentUser = getCurrentUser();
|
| 72 |
if (request == null) {
|
| 73 |
throw new IllegalArgumentException(
|
| 74 |
"Không thể thay đổi câu chuyện với dữ liệu null");
|
|
|
|
| 83 |
story.setQuoteContent(request.getQuoteContent());
|
| 84 |
story.setQuoteAuthor(request.getQuoteAuthor());
|
| 85 |
story.setPriority(request.getPriority());
|
| 86 |
+
story.setUpdatedBy(currentUser);
|
| 87 |
auditLogService.log(
|
| 88 |
+
currentUser,
|
| 89 |
LogEntityType.STORY,
|
| 90 |
story.getId(),
|
| 91 |
LogAction.UPDATE,
|
|
|
|
| 96 |
@Transactional
|
| 97 |
@Override
|
| 98 |
public void deleteStory(Long id) {
|
| 99 |
+
User currentUser = getCurrentUser();
|
| 100 |
if (id == null) {
|
| 101 |
throw new IllegalArgumentException("Không thể xóa câu chuyện với ID null");
|
| 102 |
}
|
| 103 |
Story found = findStory(id);
|
| 104 |
found.setDeleted(true);
|
| 105 |
+
found.setUpdatedBy(currentUser);
|
| 106 |
auditLogService.log(
|
| 107 |
+
currentUser,
|
| 108 |
LogEntityType.STORY,
|
| 109 |
found.getId(),
|
| 110 |
LogAction.DELETE,
|
|
|
|
| 114 |
@Transactional
|
| 115 |
@Override
|
| 116 |
public void restoreStory(Long id) {
|
| 117 |
+
User currentUser = getCurrentUser();
|
| 118 |
if (id == null) {
|
| 119 |
throw new IllegalArgumentException("Không thể khôi phục câu chuyện với ID null");
|
| 120 |
}
|
| 121 |
Story found = findStory(id);
|
| 122 |
found.setDeleted(false);
|
| 123 |
+
found.setUpdatedBy(currentUser);
|
| 124 |
auditLogService.log(
|
| 125 |
+
currentUser,
|
| 126 |
LogEntityType.STORY,
|
| 127 |
found.getId(),
|
| 128 |
LogAction.RESTORE,
|
|
|
|
| 132 |
@Override
|
| 133 |
public StoryResponse getStoryDeletedFalseHighestPriority() {
|
| 134 |
Story story = storyRepository.findTopByDeletedFalseOrderByPriorityDesc();
|
| 135 |
+
if (story == null) {
|
| 136 |
return null;
|
| 137 |
}
|
| 138 |
return StoryResponse.fromEntity(story);
|
|
|
|
| 158 |
}
|
| 159 |
|
| 160 |
private User getCurrentUser() {
|
| 161 |
+
Long currentUserId = SecurityUtil.getCurrentUserId();
|
| 162 |
+
if (currentUserId == null) {
|
|
|
|
|
|
|
| 163 |
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 164 |
}
|
| 165 |
+
return userRepository.getReferenceById(currentUserId);
|
|
|
|
|
|
|
|
|
|
| 166 |
}
|
| 167 |
}
|
src/main/java/com/darkfantasy/service/impl/UserServiceImpl.java
CHANGED
|
@@ -3,11 +3,9 @@ package com.darkfantasy.service.impl;
|
|
| 3 |
import java.security.SecureRandom;
|
| 4 |
import java.time.Instant;
|
| 5 |
import java.util.Optional;
|
| 6 |
-
import java.util.Random;
|
| 7 |
|
| 8 |
import org.springframework.data.domain.Page;
|
| 9 |
import org.springframework.data.domain.Pageable;
|
| 10 |
-
import org.springframework.mail.SimpleMailMessage;
|
| 11 |
import org.springframework.mail.javamail.JavaMailSender;
|
| 12 |
import org.springframework.security.authentication.AuthenticationManager;
|
| 13 |
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
@@ -34,6 +32,7 @@ import com.darkfantasy.exception.custom.ResourceNotFoundException;
|
|
| 34 |
import com.darkfantasy.repository.PasswordResetTokenRepository;
|
| 35 |
import com.darkfantasy.repository.UserRepository;
|
| 36 |
import com.darkfantasy.service.AuditLogService;
|
|
|
|
| 37 |
import com.darkfantasy.service.UserService;
|
| 38 |
import com.darkfantasy.util.SecurityUtil;
|
| 39 |
|
|
@@ -41,13 +40,14 @@ import lombok.RequiredArgsConstructor;
|
|
| 41 |
|
| 42 |
@Service
|
| 43 |
@RequiredArgsConstructor
|
|
|
|
| 44 |
public class UserServiceImpl implements UserService {
|
| 45 |
private final UserRepository userRepository;
|
| 46 |
private final PasswordEncoder passwordEncoder;
|
| 47 |
private final AuditLogService auditLogService;
|
| 48 |
private final PasswordResetTokenRepository tokenRepository;
|
| 49 |
-
private final JavaMailSender mailSender;
|
| 50 |
private final AuthenticationManager authenticationManager;
|
|
|
|
| 51 |
|
| 52 |
@Transactional
|
| 53 |
@Override
|
|
@@ -116,24 +116,13 @@ public class UserServiceImpl implements UserService {
|
|
| 116 |
currentUser.setMustChangePassword(false);
|
| 117 |
|
| 118 |
auditLogService.log(
|
|
|
|
| 119 |
LogEntityType.USER,
|
| 120 |
currentUser.getId(),
|
| 121 |
LogAction.UPDATE,
|
| 122 |
"Đổi mật khẩu: " + currentUser.getUsername());
|
| 123 |
}
|
| 124 |
|
| 125 |
-
private Optional<User> findUser(String keyword) {
|
| 126 |
-
return userRepository.findUserByUsernameOrEmail(keyword, keyword);
|
| 127 |
-
}
|
| 128 |
-
|
| 129 |
-
private boolean existsByKeyword(String keyword) {
|
| 130 |
-
return userRepository.existsByUsernameOrEmail(keyword, keyword);
|
| 131 |
-
}
|
| 132 |
-
|
| 133 |
-
private User findUser(Long id) {
|
| 134 |
-
return id == null ? null : userRepository.getReferenceById(id);
|
| 135 |
-
}
|
| 136 |
-
|
| 137 |
@Override
|
| 138 |
public UserResponse findAccountById(Long id) {
|
| 139 |
if (id == null)
|
|
@@ -143,17 +132,6 @@ public class UserServiceImpl implements UserService {
|
|
| 143 |
return UserResponse.fromEntity(user);
|
| 144 |
}
|
| 145 |
|
| 146 |
-
private User getCurrentUser() {
|
| 147 |
-
String currentUsername = SecurityUtil.getCurrentUserName();
|
| 148 |
-
|
| 149 |
-
if (currentUsername == null) {
|
| 150 |
-
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 151 |
-
}
|
| 152 |
-
|
| 153 |
-
return userRepository.findUserByUsername(currentUsername)
|
| 154 |
-
.orElseThrow(() -> new IllegalArgumentException("Không tìm thấy người dùng"));
|
| 155 |
-
}
|
| 156 |
-
|
| 157 |
@Override
|
| 158 |
public Page<UserResponse> getAccounts(Pageable pageable) {
|
| 159 |
return userRepository.findAllByOrderByIdAsc(pageable)
|
|
@@ -163,11 +141,12 @@ public class UserServiceImpl implements UserService {
|
|
| 163 |
@Transactional
|
| 164 |
@Override
|
| 165 |
public void lockUser(Long id) {
|
| 166 |
-
User user =
|
| 167 |
if (user.getRole() == Role.ADMIN)
|
| 168 |
throw new IllegalArgumentException("Không có thẩm quyền");
|
| 169 |
user.setActive(false);
|
| 170 |
auditLogService.log(
|
|
|
|
| 171 |
LogEntityType.USER,
|
| 172 |
user.getId(),
|
| 173 |
LogAction.LOCK,
|
|
@@ -177,11 +156,12 @@ public class UserServiceImpl implements UserService {
|
|
| 177 |
@Transactional
|
| 178 |
@Override
|
| 179 |
public void unlockUser(Long id) {
|
| 180 |
-
User user =
|
| 181 |
if (user.getRole() == Role.ADMIN)
|
| 182 |
throw new IllegalArgumentException("Không có thẩm quyền");
|
| 183 |
user.setActive(true);
|
| 184 |
auditLogService.log(
|
|
|
|
| 185 |
LogEntityType.USER,
|
| 186 |
user.getId(),
|
| 187 |
LogAction.UNLOCK,
|
|
@@ -224,14 +204,9 @@ public class UserServiceImpl implements UserService {
|
|
| 224 |
.used(false)
|
| 225 |
.build();
|
| 226 |
tokenRepository.save(token);
|
| 227 |
-
|
| 228 |
-
message.setTo(user.getEmail());
|
| 229 |
-
message.setSubject("Mã OTP đ���t lại mật khẩu");
|
| 230 |
-
message.setText("Mã OTP của bạn là: " + otp + "\n\nMã có hiệu lực trong 5 phút.");
|
| 231 |
-
mailSender.send(message);
|
| 232 |
}
|
| 233 |
|
| 234 |
-
@Transactional(readOnly = true)
|
| 235 |
@Override
|
| 236 |
public void verifyOtp(VerifyOtpRequest request) {
|
| 237 |
PasswordResetToken token = tokenRepository
|
|
@@ -261,4 +236,40 @@ public class UserServiceImpl implements UserService {
|
|
| 261 |
user.setPassword(passwordEncoder.encode(request.getNewPassword()));
|
| 262 |
token.setUsed(true);
|
| 263 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
}
|
|
|
|
| 3 |
import java.security.SecureRandom;
|
| 4 |
import java.time.Instant;
|
| 5 |
import java.util.Optional;
|
|
|
|
| 6 |
|
| 7 |
import org.springframework.data.domain.Page;
|
| 8 |
import org.springframework.data.domain.Pageable;
|
|
|
|
| 9 |
import org.springframework.mail.javamail.JavaMailSender;
|
| 10 |
import org.springframework.security.authentication.AuthenticationManager;
|
| 11 |
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
|
|
| 32 |
import com.darkfantasy.repository.PasswordResetTokenRepository;
|
| 33 |
import com.darkfantasy.repository.UserRepository;
|
| 34 |
import com.darkfantasy.service.AuditLogService;
|
| 35 |
+
import com.darkfantasy.service.EmailService;
|
| 36 |
import com.darkfantasy.service.UserService;
|
| 37 |
import com.darkfantasy.util.SecurityUtil;
|
| 38 |
|
|
|
|
| 40 |
|
| 41 |
@Service
|
| 42 |
@RequiredArgsConstructor
|
| 43 |
+
@Transactional(readOnly = true)
|
| 44 |
public class UserServiceImpl implements UserService {
|
| 45 |
private final UserRepository userRepository;
|
| 46 |
private final PasswordEncoder passwordEncoder;
|
| 47 |
private final AuditLogService auditLogService;
|
| 48 |
private final PasswordResetTokenRepository tokenRepository;
|
|
|
|
| 49 |
private final AuthenticationManager authenticationManager;
|
| 50 |
+
private final EmailService emailService;
|
| 51 |
|
| 52 |
@Transactional
|
| 53 |
@Override
|
|
|
|
| 116 |
currentUser.setMustChangePassword(false);
|
| 117 |
|
| 118 |
auditLogService.log(
|
| 119 |
+
currentUser,
|
| 120 |
LogEntityType.USER,
|
| 121 |
currentUser.getId(),
|
| 122 |
LogAction.UPDATE,
|
| 123 |
"Đổi mật khẩu: " + currentUser.getUsername());
|
| 124 |
}
|
| 125 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
@Override
|
| 127 |
public UserResponse findAccountById(Long id) {
|
| 128 |
if (id == null)
|
|
|
|
| 132 |
return UserResponse.fromEntity(user);
|
| 133 |
}
|
| 134 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
@Override
|
| 136 |
public Page<UserResponse> getAccounts(Pageable pageable) {
|
| 137 |
return userRepository.findAllByOrderByIdAsc(pageable)
|
|
|
|
| 141 |
@Transactional
|
| 142 |
@Override
|
| 143 |
public void lockUser(Long id) {
|
| 144 |
+
User user = findUserById(id);
|
| 145 |
if (user.getRole() == Role.ADMIN)
|
| 146 |
throw new IllegalArgumentException("Không có thẩm quyền");
|
| 147 |
user.setActive(false);
|
| 148 |
auditLogService.log(
|
| 149 |
+
getCurrentUser(),
|
| 150 |
LogEntityType.USER,
|
| 151 |
user.getId(),
|
| 152 |
LogAction.LOCK,
|
|
|
|
| 156 |
@Transactional
|
| 157 |
@Override
|
| 158 |
public void unlockUser(Long id) {
|
| 159 |
+
User user = findUserById(id);
|
| 160 |
if (user.getRole() == Role.ADMIN)
|
| 161 |
throw new IllegalArgumentException("Không có thẩm quyền");
|
| 162 |
user.setActive(true);
|
| 163 |
auditLogService.log(
|
| 164 |
+
getCurrentUser(),
|
| 165 |
LogEntityType.USER,
|
| 166 |
user.getId(),
|
| 167 |
LogAction.UNLOCK,
|
|
|
|
| 204 |
.used(false)
|
| 205 |
.build();
|
| 206 |
tokenRepository.save(token);
|
| 207 |
+
emailService.sendPasswordResetEmail(user.getEmail(), otp);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 208 |
}
|
| 209 |
|
|
|
|
| 210 |
@Override
|
| 211 |
public void verifyOtp(VerifyOtpRequest request) {
|
| 212 |
PasswordResetToken token = tokenRepository
|
|
|
|
| 236 |
user.setPassword(passwordEncoder.encode(request.getNewPassword()));
|
| 237 |
token.setUsed(true);
|
| 238 |
}
|
| 239 |
+
|
| 240 |
+
@Override
|
| 241 |
+
public void logLogout() {
|
| 242 |
+
User currentUser = getCurrentUser();
|
| 243 |
+
auditLogService.log(
|
| 244 |
+
currentUser,
|
| 245 |
+
LogEntityType.USER,
|
| 246 |
+
currentUser.getId(),
|
| 247 |
+
LogAction.LOGOUT,
|
| 248 |
+
"Đăng xuất: " + currentUser.getUsername());
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
private Optional<User> findUser(String keyword) {
|
| 252 |
+
return userRepository.findUserByUsernameOrEmail(keyword, keyword);
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
private boolean existsByKeyword(String keyword) {
|
| 256 |
+
return userRepository.existsByUsernameOrEmail(keyword, keyword);
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
private User findUserById(Long id) {
|
| 260 |
+
if (id == null) {
|
| 261 |
+
throw new IllegalArgumentException("Id không thể là null");
|
| 262 |
+
}
|
| 263 |
+
return userRepository.findById(id)
|
| 264 |
+
.orElseThrow(() -> new ResourceNotFoundException("Không tìm thấy người dùng"));
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
private User getCurrentUser() {
|
| 268 |
+
Long currentUserId = SecurityUtil.getCurrentUserId();
|
| 269 |
+
if (currentUserId == null) {
|
| 270 |
+
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 271 |
+
}
|
| 272 |
+
return userRepository.getReferenceById(currentUserId);
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
}
|
src/main/java/com/darkfantasy/service/impl/WorldServiceImpl.java
CHANGED
|
@@ -2,6 +2,8 @@ package com.darkfantasy.service.impl;
|
|
| 2 |
|
| 3 |
import java.util.List;
|
| 4 |
|
|
|
|
|
|
|
| 5 |
import org.springframework.data.domain.Page;
|
| 6 |
import org.springframework.data.domain.Pageable;
|
| 7 |
import org.springframework.stereotype.Service;
|
|
@@ -24,6 +26,7 @@ import lombok.RequiredArgsConstructor;
|
|
| 24 |
|
| 25 |
@Service
|
| 26 |
@RequiredArgsConstructor
|
|
|
|
| 27 |
public class WorldServiceImpl implements WorldService {
|
| 28 |
|
| 29 |
private final WorldRepository worldRepository;
|
|
@@ -42,28 +45,33 @@ public class WorldServiceImpl implements WorldService {
|
|
| 42 |
return WorldResponse.fromEntity(findWorld(id));
|
| 43 |
}
|
| 44 |
|
|
|
|
| 45 |
@Transactional
|
| 46 |
@Override
|
| 47 |
public WorldResponse createWorld(CreateWorldRequest request) {
|
|
|
|
| 48 |
if (request == null) {
|
| 49 |
throw new IllegalArgumentException(
|
| 50 |
"Không thể tạo thế giới với dữ liệu null");
|
| 51 |
}
|
| 52 |
|
| 53 |
World world = request.toEntity();
|
| 54 |
-
world.setCreatedBy(
|
| 55 |
World savedWorld = worldRepository.save(world);
|
| 56 |
auditLogService.log(
|
|
|
|
| 57 |
LogEntityType.WORLD,
|
| 58 |
savedWorld.getId(),
|
| 59 |
LogAction.CREATE,
|
| 60 |
"Thêm thế giới: " + savedWorld.getTitle());
|
| 61 |
-
return WorldResponse.fromEntity(
|
| 62 |
}
|
| 63 |
|
|
|
|
| 64 |
@Transactional
|
| 65 |
@Override
|
| 66 |
public WorldResponse updateWorld(UpdateWorldRequest request) {
|
|
|
|
| 67 |
if (request == null) {
|
| 68 |
throw new IllegalArgumentException(
|
| 69 |
"Không thể thay đổi thế giới với dữ liệu null");
|
|
@@ -76,8 +84,9 @@ public class WorldServiceImpl implements WorldService {
|
|
| 76 |
if (request.getImage() != null) {
|
| 77 |
world.setImage(request.getImage());
|
| 78 |
}
|
| 79 |
-
world.setUpdatedBy(
|
| 80 |
auditLogService.log(
|
|
|
|
| 81 |
LogEntityType.WORLD,
|
| 82 |
world.getId(),
|
| 83 |
LogAction.UPDATE,
|
|
@@ -85,32 +94,38 @@ public class WorldServiceImpl implements WorldService {
|
|
| 85 |
return WorldResponse.fromEntity(world);
|
| 86 |
}
|
| 87 |
|
|
|
|
| 88 |
@Transactional
|
| 89 |
@Override
|
| 90 |
public void deleteWorld(Long id) {
|
|
|
|
| 91 |
if (id == null) {
|
| 92 |
throw new IllegalArgumentException("Không thể xóa thế giới với ID null");
|
| 93 |
}
|
| 94 |
World found = findWorld(id);
|
| 95 |
found.setDeleted(true);
|
| 96 |
-
found.
|
| 97 |
auditLogService.log(
|
|
|
|
| 98 |
LogEntityType.WORLD,
|
| 99 |
found.getId(),
|
| 100 |
LogAction.DELETE,
|
| 101 |
"Xóa thế giới: " + found.getTitle());
|
| 102 |
}
|
| 103 |
|
|
|
|
| 104 |
@Transactional
|
| 105 |
@Override
|
| 106 |
public void restoreWorld(Long id) {
|
|
|
|
| 107 |
if (id == null) {
|
| 108 |
throw new IllegalArgumentException("Không thể khôi phục thế giới với ID null");
|
| 109 |
}
|
| 110 |
World found = findWorld(id);
|
| 111 |
found.setDeleted(false);
|
| 112 |
-
found.setUpdatedBy(
|
| 113 |
auditLogService.log(
|
|
|
|
| 114 |
LogEntityType.WORLD,
|
| 115 |
found.getId(),
|
| 116 |
LogAction.RESTORE,
|
|
@@ -129,6 +144,7 @@ public class WorldServiceImpl implements WorldService {
|
|
| 129 |
return WorldResponse.fromEntity(world);
|
| 130 |
}
|
| 131 |
|
|
|
|
| 132 |
@Override
|
| 133 |
public List<WorldResponse> getWorldsDeletedFalse() {
|
| 134 |
return worldRepository
|
|
@@ -138,27 +154,22 @@ public class WorldServiceImpl implements WorldService {
|
|
| 138 |
.toList();
|
| 139 |
}
|
| 140 |
|
| 141 |
-
private World findWorld(Long id) {
|
| 142 |
-
return worldRepository.findById(id)
|
| 143 |
-
.orElseThrow(() -> new IllegalArgumentException("Không tìm thấy thế giới với ID: " + id));
|
| 144 |
-
}
|
| 145 |
-
|
| 146 |
@Override
|
| 147 |
public long count() {
|
| 148 |
return worldRepository.count();
|
| 149 |
}
|
| 150 |
|
| 151 |
private User getCurrentUser() {
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
if (currentUsername == null) {
|
| 156 |
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 157 |
}
|
|
|
|
|
|
|
| 158 |
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
.orElseThrow(() -> new IllegalArgumentException("Không tìm thấy
|
| 162 |
}
|
| 163 |
|
| 164 |
}
|
|
|
|
| 2 |
|
| 3 |
import java.util.List;
|
| 4 |
|
| 5 |
+
import org.springframework.cache.annotation.CacheEvict;
|
| 6 |
+
import org.springframework.cache.annotation.Cacheable;
|
| 7 |
import org.springframework.data.domain.Page;
|
| 8 |
import org.springframework.data.domain.Pageable;
|
| 9 |
import org.springframework.stereotype.Service;
|
|
|
|
| 26 |
|
| 27 |
@Service
|
| 28 |
@RequiredArgsConstructor
|
| 29 |
+
@Transactional(readOnly = true)
|
| 30 |
public class WorldServiceImpl implements WorldService {
|
| 31 |
|
| 32 |
private final WorldRepository worldRepository;
|
|
|
|
| 45 |
return WorldResponse.fromEntity(findWorld(id));
|
| 46 |
}
|
| 47 |
|
| 48 |
+
@CacheEvict(value = "publicWorlds", allEntries = true)
|
| 49 |
@Transactional
|
| 50 |
@Override
|
| 51 |
public WorldResponse createWorld(CreateWorldRequest request) {
|
| 52 |
+
User currentUser = getCurrentUser();
|
| 53 |
if (request == null) {
|
| 54 |
throw new IllegalArgumentException(
|
| 55 |
"Không thể tạo thế giới với dữ liệu null");
|
| 56 |
}
|
| 57 |
|
| 58 |
World world = request.toEntity();
|
| 59 |
+
world.setCreatedBy(currentUser);
|
| 60 |
World savedWorld = worldRepository.save(world);
|
| 61 |
auditLogService.log(
|
| 62 |
+
currentUser,
|
| 63 |
LogEntityType.WORLD,
|
| 64 |
savedWorld.getId(),
|
| 65 |
LogAction.CREATE,
|
| 66 |
"Thêm thế giới: " + savedWorld.getTitle());
|
| 67 |
+
return WorldResponse.fromEntity(savedWorld);
|
| 68 |
}
|
| 69 |
|
| 70 |
+
@CacheEvict(value = "publicWorlds", allEntries = true)
|
| 71 |
@Transactional
|
| 72 |
@Override
|
| 73 |
public WorldResponse updateWorld(UpdateWorldRequest request) {
|
| 74 |
+
User currentUser = getCurrentUser();
|
| 75 |
if (request == null) {
|
| 76 |
throw new IllegalArgumentException(
|
| 77 |
"Không thể thay đổi thế giới với dữ liệu null");
|
|
|
|
| 84 |
if (request.getImage() != null) {
|
| 85 |
world.setImage(request.getImage());
|
| 86 |
}
|
| 87 |
+
world.setUpdatedBy(currentUser);
|
| 88 |
auditLogService.log(
|
| 89 |
+
currentUser,
|
| 90 |
LogEntityType.WORLD,
|
| 91 |
world.getId(),
|
| 92 |
LogAction.UPDATE,
|
|
|
|
| 94 |
return WorldResponse.fromEntity(world);
|
| 95 |
}
|
| 96 |
|
| 97 |
+
@CacheEvict(value = "publicWorlds", allEntries = true)
|
| 98 |
@Transactional
|
| 99 |
@Override
|
| 100 |
public void deleteWorld(Long id) {
|
| 101 |
+
User currentUser = getCurrentUser();
|
| 102 |
if (id == null) {
|
| 103 |
throw new IllegalArgumentException("Không thể xóa thế giới với ID null");
|
| 104 |
}
|
| 105 |
World found = findWorld(id);
|
| 106 |
found.setDeleted(true);
|
| 107 |
+
found.setUpdatedBy(currentUser);
|
| 108 |
auditLogService.log(
|
| 109 |
+
currentUser,
|
| 110 |
LogEntityType.WORLD,
|
| 111 |
found.getId(),
|
| 112 |
LogAction.DELETE,
|
| 113 |
"Xóa thế giới: " + found.getTitle());
|
| 114 |
}
|
| 115 |
|
| 116 |
+
@CacheEvict(value = "publicWorlds", allEntries = true)
|
| 117 |
@Transactional
|
| 118 |
@Override
|
| 119 |
public void restoreWorld(Long id) {
|
| 120 |
+
User currentUser = getCurrentUser();
|
| 121 |
if (id == null) {
|
| 122 |
throw new IllegalArgumentException("Không thể khôi phục thế giới với ID null");
|
| 123 |
}
|
| 124 |
World found = findWorld(id);
|
| 125 |
found.setDeleted(false);
|
| 126 |
+
found.setUpdatedBy(currentUser);
|
| 127 |
auditLogService.log(
|
| 128 |
+
currentUser,
|
| 129 |
LogEntityType.WORLD,
|
| 130 |
found.getId(),
|
| 131 |
LogAction.RESTORE,
|
|
|
|
| 144 |
return WorldResponse.fromEntity(world);
|
| 145 |
}
|
| 146 |
|
| 147 |
+
@Cacheable(value = "publicWorlds")
|
| 148 |
@Override
|
| 149 |
public List<WorldResponse> getWorldsDeletedFalse() {
|
| 150 |
return worldRepository
|
|
|
|
| 154 |
.toList();
|
| 155 |
}
|
| 156 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
@Override
|
| 158 |
public long count() {
|
| 159 |
return worldRepository.count();
|
| 160 |
}
|
| 161 |
|
| 162 |
private User getCurrentUser() {
|
| 163 |
+
Long currentUserId = SecurityUtil.getCurrentUserId();
|
| 164 |
+
if (currentUserId == null) {
|
|
|
|
|
|
|
| 165 |
throw new IllegalStateException("Không tìm thấy người dùng hiện tại");
|
| 166 |
}
|
| 167 |
+
return userRepository.getReferenceById(currentUserId);
|
| 168 |
+
}
|
| 169 |
|
| 170 |
+
private World findWorld(Long id) {
|
| 171 |
+
return worldRepository.findById(id)
|
| 172 |
+
.orElseThrow(() -> new IllegalArgumentException("Không tìm thấy thế giới với ID: " + id));
|
| 173 |
}
|
| 174 |
|
| 175 |
}
|
src/main/java/com/darkfantasy/util/SecurityUtil.java
CHANGED
|
@@ -2,46 +2,38 @@ package com.darkfantasy.util;
|
|
| 2 |
|
| 3 |
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
| 4 |
import org.springframework.security.core.Authentication;
|
| 5 |
-
import org.springframework.security.core.GrantedAuthority;
|
| 6 |
import org.springframework.security.core.context.SecurityContextHolder;
|
| 7 |
|
| 8 |
import com.darkfantasy.entity.enums.Role;
|
|
|
|
| 9 |
|
| 10 |
import lombok.experimental.UtilityClass;
|
| 11 |
|
| 12 |
@UtilityClass
|
| 13 |
public class SecurityUtil {
|
| 14 |
-
public
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|| !authentication.isAuthenticated()
|
| 18 |
-
|| authentication instanceof AnonymousAuthenticationToken) {
|
| 19 |
-
return null;
|
| 20 |
}
|
| 21 |
-
return authentication.getName();
|
| 22 |
-
}
|
| 23 |
|
| 24 |
-
|
|
|
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
| 29 |
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
|
| 34 |
-
|
| 35 |
-
|
|
|
|
| 36 |
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
.stream()
|
| 40 |
-
.map(GrantedAuthority::getAuthority)
|
| 41 |
-
.filter(authority -> authority.startsWith("ROLE_"))
|
| 42 |
-
.findFirst()
|
| 43 |
-
.map(authority -> Role.valueOf(
|
| 44 |
-
authority.replace("ROLE_", "")))
|
| 45 |
-
.orElse(null);
|
| 46 |
}
|
| 47 |
}
|
|
|
|
| 2 |
|
| 3 |
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
| 4 |
import org.springframework.security.core.Authentication;
|
|
|
|
| 5 |
import org.springframework.security.core.context.SecurityContextHolder;
|
| 6 |
|
| 7 |
import com.darkfantasy.entity.enums.Role;
|
| 8 |
+
import com.darkfantasy.security.CustomUserDetails;
|
| 9 |
|
| 10 |
import lombok.experimental.UtilityClass;
|
| 11 |
|
| 12 |
@UtilityClass
|
| 13 |
public class SecurityUtil {
|
| 14 |
+
public CustomUserDetails getCurrentUser() {
|
| 15 |
+
if (!isAuthenticated()) {
|
| 16 |
+
throw new IllegalStateException("Người dùng chưa đăng nhập");
|
|
|
|
|
|
|
|
|
|
| 17 |
}
|
|
|
|
|
|
|
| 18 |
|
| 19 |
+
return (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
| 20 |
+
}
|
| 21 |
|
| 22 |
+
public boolean isAuthenticated() {
|
| 23 |
+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
| 24 |
+
return authentication != null && authentication.isAuthenticated()
|
| 25 |
+
&& !(authentication instanceof AnonymousAuthenticationToken);
|
| 26 |
+
}
|
| 27 |
|
| 28 |
+
public Role getCurrentUserRole() {
|
| 29 |
+
return Role.valueOf(getCurrentUser().getRole());
|
| 30 |
+
}
|
| 31 |
|
| 32 |
+
public String getCurrentUserName() {
|
| 33 |
+
return getCurrentUser().getUsername();
|
| 34 |
+
}
|
| 35 |
|
| 36 |
+
public Long getCurrentUserId() {
|
| 37 |
+
return getCurrentUser().getId();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
}
|
| 39 |
}
|
src/main/resources/application.properties
CHANGED
|
@@ -26,7 +26,7 @@ server.servlet.jsp.init-parameters.development=true
|
|
| 26 |
spring.datasource.url=${SPRING_DATASOURCE_URL}
|
| 27 |
# spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
|
| 28 |
# spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
|
| 29 |
-
|
| 30 |
# Hibernate Dialect
|
| 31 |
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
|
| 32 |
|
|
@@ -38,8 +38,16 @@ spring.mail.password=${SPRING_MAIL_PASSWORD}
|
|
| 38 |
|
| 39 |
# JPA configuration
|
| 40 |
spring.jpa.hibernate.ddl-auto=update
|
| 41 |
-
spring.jpa.show-sql=true
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
|
| 45 |
spring.mail.properties.mail.smtp.auth=true
|
|
@@ -56,3 +64,27 @@ spring.cache.type=none
|
|
| 56 |
# spring.cache.type=caffeine
|
| 57 |
# spring.cache.caffeine.spec=maximumSize=500,expireAfterWrite=5m
|
| 58 |
# spring.cache.cache-names=articles,characters,worlds,stories,faqs,contributors
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
spring.datasource.url=${SPRING_DATASOURCE_URL}
|
| 27 |
# spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
|
| 28 |
# spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
|
| 29 |
+
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
|
| 30 |
# Hibernate Dialect
|
| 31 |
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
|
| 32 |
|
|
|
|
| 38 |
|
| 39 |
# JPA configuration
|
| 40 |
spring.jpa.hibernate.ddl-auto=update
|
|
|
|
| 41 |
|
| 42 |
+
# Bật tính năng in câu lệnh SQL ra console
|
| 43 |
+
#Nên bật khi code, khi deploy thì tắt
|
| 44 |
+
spring.jpa.show-sql=false
|
| 45 |
+
|
| 46 |
+
# Format SQL cho dễ đọc (xuống dòng, thụt lề), nếu không nó sẽ in thành 1 hàng ngang rất rối
|
| 47 |
+
spring.jpa.properties.hibernate.format_sql=true
|
| 48 |
+
|
| 49 |
+
# Làm nổi bật các từ khóa SQL (SELECT, FROM, JOIN) bằng màu sắc
|
| 50 |
+
spring.jpa.properties.hibernate.highlight_sql=true
|
| 51 |
|
| 52 |
|
| 53 |
spring.mail.properties.mail.smtp.auth=true
|
|
|
|
| 64 |
# spring.cache.type=caffeine
|
| 65 |
# spring.cache.caffeine.spec=maximumSize=500,expireAfterWrite=5m
|
| 66 |
# spring.cache.cache-names=articles,characters,worlds,stories,faqs,contributors
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
# 1. TỐI ƯU CONNECTION POOL (HikariCP)
|
| 70 |
+
# Số lượng kết nối tối đa giữ với Database (Tránh mở quá nhiều gây sập DB)
|
| 71 |
+
spring.datasource.hikari.maximum-pool-size=20
|
| 72 |
+
spring.datasource.hikari.minimum-idle=5
|
| 73 |
+
# Thời gian chờ tối đa nếu hết kết nối (tính bằng ms)
|
| 74 |
+
spring.datasource.hikari.connection-timeout=30000
|
| 75 |
+
|
| 76 |
+
# 2. BẬT BATCH PROCESSING (Đóng gói câu SQL)
|
| 77 |
+
# Thay vì bắn 100 câu INSERT rời rạc, Hibernate sẽ gộp lại bắn 1 lần
|
| 78 |
+
spring.jpa.properties.hibernate.jdbc.batch_size=50
|
| 79 |
+
spring.jpa.properties.hibernate.order_inserts=true
|
| 80 |
+
spring.jpa.properties.hibernate.order_updates=true
|
| 81 |
+
|
| 82 |
+
# 3. TẮT OPEN-IN-VIEW (Quan trọng)
|
| 83 |
+
# Chặn việc Hibernate tự ý giữ kết nối Database trong lúc Controller đang trả về JSON (Anti-pattern)
|
| 84 |
+
spring.jpa.open-in-view=false
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
spring.config.import=optional:file:.env[.properties]
|
| 90 |
+
|