Spaces:
Sleeping
Sleeping
Commit ·
c2446b7
0
Parent(s):
Deploy to HF with Git LFS for all media files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .PVS-Studio/State/WindowState.json +23 -0
- .PVS-Studio/Temp/.PVS-Studio/.gitignore +2 -0
- .PVS-Studio/nonAnalyzedProjects.log +1 -0
- .PVS-Studio/project.json +329 -0
- .gitattributes +7 -0
- .gitignore +33 -0
- .mvn/wrapper/maven-wrapper.properties +3 -0
- DockerFile +13 -0
- README.md +1 -0
- TEST_CASES.md +104 -0
- mvnw +295 -0
- mvnw.cmd +189 -0
- pom.xml +130 -0
- src/main/java/com/darkfantasy/DarkFantasyApplication.java +19 -0
- src/main/java/com/darkfantasy/annotation/ChangePasswordMatch.java +23 -0
- src/main/java/com/darkfantasy/annotation/RegisterPasswordMatch.java +23 -0
- src/main/java/com/darkfantasy/annotation/ResetPasswordMatch.java +23 -0
- src/main/java/com/darkfantasy/config/SchedulingConfig.java +5 -0
- src/main/java/com/darkfantasy/config/WebConfig.java +27 -0
- src/main/java/com/darkfantasy/constant/CmsMenu.java +16 -0
- src/main/java/com/darkfantasy/constant/Routes.java +18 -0
- src/main/java/com/darkfantasy/controller/AdminController.java +109 -0
- src/main/java/com/darkfantasy/controller/ArticleController.java +195 -0
- src/main/java/com/darkfantasy/controller/ContactMessageController.java +62 -0
- src/main/java/com/darkfantasy/controller/ContributorController.java +181 -0
- src/main/java/com/darkfantasy/controller/DashboardController.java +46 -0
- src/main/java/com/darkfantasy/controller/FaqController.java +161 -0
- src/main/java/com/darkfantasy/controller/GameCharacterController.java +163 -0
- src/main/java/com/darkfantasy/controller/ImageController.java +42 -0
- src/main/java/com/darkfantasy/controller/InternalController.java +26 -0
- src/main/java/com/darkfantasy/controller/PageController.java +206 -0
- src/main/java/com/darkfantasy/controller/StoryController.java +171 -0
- src/main/java/com/darkfantasy/controller/UserController.java +321 -0
- src/main/java/com/darkfantasy/controller/WorldController.java +162 -0
- src/main/java/com/darkfantasy/dto/article/ArticleResponse.java +58 -0
- src/main/java/com/darkfantasy/dto/article/CreateArticleRequest.java +40 -0
- src/main/java/com/darkfantasy/dto/article/UpdateArticleRequest.java +32 -0
- src/main/java/com/darkfantasy/dto/contact/ContactMessageResponse.java +42 -0
- src/main/java/com/darkfantasy/dto/contact/CreateContactMessageRequest.java +49 -0
- src/main/java/com/darkfantasy/dto/contributor/ContributorResponse.java +41 -0
- src/main/java/com/darkfantasy/dto/contributor/CreateContributorRequest.java +44 -0
- src/main/java/com/darkfantasy/dto/contributor/UpdateContributorRequest.java +34 -0
- src/main/java/com/darkfantasy/dto/faq/CreateFaqRequest.java +41 -0
- src/main/java/com/darkfantasy/dto/faq/FaqResponse.java +30 -0
- src/main/java/com/darkfantasy/dto/faq/UpdateFaqRequest.java +29 -0
- src/main/java/com/darkfantasy/dto/gamecharacter/CreateGameCharacterRequest.java +52 -0
- src/main/java/com/darkfantasy/dto/gamecharacter/GameCharacterResponse.java +36 -0
- src/main/java/com/darkfantasy/dto/gamecharacter/UpdateGameCharacterRequest.java +48 -0
- src/main/java/com/darkfantasy/dto/log/AuditLogResponse.java +37 -0
- src/main/java/com/darkfantasy/dto/story/CreateStoryRequest.java +50 -0
.PVS-Studio/State/WindowState.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"isDataChanged": false,
|
| 3 |
+
"filters": {
|
| 4 |
+
"code": "",
|
| 5 |
+
"cwe": "",
|
| 6 |
+
"sastId": "",
|
| 7 |
+
"message": "",
|
| 8 |
+
"files": "",
|
| 9 |
+
"level": [
|
| 10 |
+
0,
|
| 11 |
+
1,
|
| 12 |
+
2
|
| 13 |
+
],
|
| 14 |
+
"isColumnFiltersVisible": false,
|
| 15 |
+
"isLevelFiltersVisible": true,
|
| 16 |
+
"isGroupFiltersVisible": true,
|
| 17 |
+
"type": [
|
| 18 |
+
"General",
|
| 19 |
+
"Optimization"
|
| 20 |
+
]
|
| 21 |
+
},
|
| 22 |
+
"loadableProcessIds": []
|
| 23 |
+
}
|
.PVS-Studio/Temp/.PVS-Studio/.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
logs/
|
| 2 |
+
models/
|
.PVS-Studio/nonAnalyzedProjects.log
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
[]
|
.PVS-Studio/project.json
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"modules": [
|
| 3 |
+
{
|
| 4 |
+
"name": "DarkMoon1",
|
| 5 |
+
"path": "f:\\DarkMoon1\\",
|
| 6 |
+
"sources": [
|
| 7 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\DarkFantasyApplication.java",
|
| 8 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\annotation\\ChangePasswordMatch.java",
|
| 9 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\annotation\\RegisterPasswordMatch.java",
|
| 10 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\annotation\\ResetPasswordMatch.java",
|
| 11 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\config\\SchedulingConfig.java",
|
| 12 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\config\\WebConfig.java",
|
| 13 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\constant\\CmsMenu.java",
|
| 14 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\constant\\Routes.java",
|
| 15 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\AdminController.java",
|
| 16 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\ArticleController.java",
|
| 17 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\ContactMessageController.java",
|
| 18 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\DashboardController.java",
|
| 19 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\FaqController.java",
|
| 20 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\GameCharacterController.java",
|
| 21 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\ImageController.java",
|
| 22 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\InternalController.java",
|
| 23 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\PageController.java",
|
| 24 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\StoryController.java",
|
| 25 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\UserController.java",
|
| 26 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\WorldController.java",
|
| 27 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\article\\ArticleResponse.java",
|
| 28 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\article\\CreateArticleRequest.java",
|
| 29 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\article\\UpdateArticleRequest.java",
|
| 30 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\contact\\ContactMessageResponse.java",
|
| 31 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\contact\\CreateContactMessageRequest.java",
|
| 32 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\faq\\CreateFaqRequest.java",
|
| 33 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\faq\\FaqResponse.java",
|
| 34 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\faq\\UpdateFaqRequest.java",
|
| 35 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\gamecharacter\\CreateGameCharacterRequest.java",
|
| 36 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\gamecharacter\\GameCharacterResponse.java",
|
| 37 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\gamecharacter\\UpdateGameCharacterRequest.java",
|
| 38 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\log\\AuditLogResponse.java",
|
| 39 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\story\\CreateStoryRequest.java",
|
| 40 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\story\\StoryResponse.java",
|
| 41 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\story\\UpdateStoryRequest.java",
|
| 42 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\ChangePasswordRequest.java",
|
| 43 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\ForgotPasswordRequest.java",
|
| 44 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\LoginRequest.java",
|
| 45 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\RegisterRequest.java",
|
| 46 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\ResetPasswordRequest.java",
|
| 47 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\UserResponse.java",
|
| 48 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\VerifyOtpRequest.java",
|
| 49 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\world\\CreateWorldRequest.java",
|
| 50 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\world\\UpdateWorldRequest.java",
|
| 51 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\world\\WorldResponse.java",
|
| 52 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\Article.java",
|
| 53 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\AuditLog.java",
|
| 54 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\ContactMessage.java",
|
| 55 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\Faq.java",
|
| 56 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\GameCharacter.java",
|
| 57 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\PasswordResetToken.java",
|
| 58 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\Story.java",
|
| 59 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\User.java",
|
| 60 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\World.java",
|
| 61 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\converter\\ArticleTypeConverter.java",
|
| 62 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\converter\\ContactCategoryConverter.java",
|
| 63 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\converter\\ContactPlatformConverter.java",
|
| 64 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\converter\\LogActionConverter.java",
|
| 65 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\converter\\LogEntityTypeConverter.java",
|
| 66 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\converter\\RoleConverter.java",
|
| 67 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\enums\\ArticleType.java",
|
| 68 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\enums\\ContactCategory.java",
|
| 69 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\enums\\ContactPlatform.java",
|
| 70 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\enums\\LogAction.java",
|
| 71 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\enums\\LogEntityType.java",
|
| 72 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\enums\\Role.java",
|
| 73 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\exception\\GlobalExceptionHandler.java",
|
| 74 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\ArticleRepository.java",
|
| 75 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\AuditLogRepository.java",
|
| 76 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\ContactMessageRepository.java",
|
| 77 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\FaqRepository.java",
|
| 78 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\GameCharacterRepository.java",
|
| 79 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\PasswordResetTokenRepository.java",
|
| 80 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\StoryRepository.java",
|
| 81 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\UserRepository.java",
|
| 82 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\WorldRepository.java",
|
| 83 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\security\\ForceChangePasswordInterceptor.java",
|
| 84 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\security\\SecurityConfig.java",
|
| 85 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\ArticleService.java",
|
| 86 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\AuditLogService.java",
|
| 87 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\ContactMessageService.java",
|
| 88 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\CustomUserDetailsService.java",
|
| 89 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\FaqService.java",
|
| 90 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\FileStorageService.java",
|
| 91 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\GameCharacterService.java",
|
| 92 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\StoryService.java",
|
| 93 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\UserService.java",
|
| 94 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\WorldService.java",
|
| 95 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\ArticleServiceImpl.java",
|
| 96 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\AuditLogServiceImpl.java",
|
| 97 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\ContactMessageServiceImpl.java",
|
| 98 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\FaqServiceImpl.java",
|
| 99 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\FileStorageServiceImpl.java",
|
| 100 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\GameCharacterServiceImpl.java",
|
| 101 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\StoryServiceImpl.java",
|
| 102 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\UserServiceImpl.java",
|
| 103 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\WorldServiceImpl.java",
|
| 104 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\util\\SecurityUtil.java",
|
| 105 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\util\\TimeUtil.java",
|
| 106 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\validator\\PasswordChangeMatchValidator.java",
|
| 107 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\validator\\PasswordRegisterMatchValidator.java",
|
| 108 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\validator\\ResetPasswordMatchValidator.java",
|
| 109 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\DarkFantasyApplication.java",
|
| 110 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\annotation\\ChangePasswordMatch.java",
|
| 111 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\annotation\\RegisterPasswordMatch.java",
|
| 112 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\annotation\\ResetPasswordMatch.java",
|
| 113 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\config\\SchedulingConfig.java",
|
| 114 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\config\\WebConfig.java",
|
| 115 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\constant\\CmsMenu.java",
|
| 116 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\constant\\Routes.java",
|
| 117 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\AdminController.java",
|
| 118 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\ArticleController.java",
|
| 119 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\ContactMessageController.java",
|
| 120 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\DashboardController.java",
|
| 121 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\FaqController.java",
|
| 122 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\GameCharacterController.java",
|
| 123 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\ImageController.java",
|
| 124 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\InternalController.java",
|
| 125 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\PageController.java",
|
| 126 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\StoryController.java",
|
| 127 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\UserController.java",
|
| 128 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\controller\\WorldController.java",
|
| 129 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\article\\ArticleResponse.java",
|
| 130 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\article\\CreateArticleRequest.java",
|
| 131 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\article\\UpdateArticleRequest.java",
|
| 132 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\contact\\ContactMessageResponse.java",
|
| 133 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\contact\\CreateContactMessageRequest.java",
|
| 134 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\faq\\CreateFaqRequest.java",
|
| 135 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\faq\\FaqResponse.java",
|
| 136 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\faq\\UpdateFaqRequest.java",
|
| 137 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\gamecharacter\\CreateGameCharacterRequest.java",
|
| 138 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\gamecharacter\\GameCharacterResponse.java",
|
| 139 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\gamecharacter\\UpdateGameCharacterRequest.java",
|
| 140 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\log\\AuditLogResponse.java",
|
| 141 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\story\\CreateStoryRequest.java",
|
| 142 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\story\\StoryResponse.java",
|
| 143 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\story\\UpdateStoryRequest.java",
|
| 144 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\ChangePasswordRequest.java",
|
| 145 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\ForgotPasswordRequest.java",
|
| 146 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\LoginRequest.java",
|
| 147 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\RegisterRequest.java",
|
| 148 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\ResetPasswordRequest.java",
|
| 149 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\UserResponse.java",
|
| 150 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\user\\VerifyOtpRequest.java",
|
| 151 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\world\\CreateWorldRequest.java",
|
| 152 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\world\\UpdateWorldRequest.java",
|
| 153 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\dto\\world\\WorldResponse.java",
|
| 154 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\Article.java",
|
| 155 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\AuditLog.java",
|
| 156 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\ContactMessage.java",
|
| 157 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\Faq.java",
|
| 158 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\GameCharacter.java",
|
| 159 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\PasswordResetToken.java",
|
| 160 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\Story.java",
|
| 161 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\User.java",
|
| 162 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\World.java",
|
| 163 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\converter\\ArticleTypeConverter.java",
|
| 164 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\converter\\ContactCategoryConverter.java",
|
| 165 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\converter\\ContactPlatformConverter.java",
|
| 166 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\converter\\LogActionConverter.java",
|
| 167 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\converter\\LogEntityTypeConverter.java",
|
| 168 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\converter\\RoleConverter.java",
|
| 169 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\enums\\ArticleType.java",
|
| 170 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\enums\\ContactCategory.java",
|
| 171 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\enums\\ContactPlatform.java",
|
| 172 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\enums\\LogAction.java",
|
| 173 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\enums\\LogEntityType.java",
|
| 174 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\entity\\enums\\Role.java",
|
| 175 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\exception\\GlobalExceptionHandler.java",
|
| 176 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\ArticleRepository.java",
|
| 177 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\AuditLogRepository.java",
|
| 178 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\ContactMessageRepository.java",
|
| 179 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\FaqRepository.java",
|
| 180 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\GameCharacterRepository.java",
|
| 181 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\PasswordResetTokenRepository.java",
|
| 182 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\StoryRepository.java",
|
| 183 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\UserRepository.java",
|
| 184 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\repository\\WorldRepository.java",
|
| 185 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\security\\ForceChangePasswordInterceptor.java",
|
| 186 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\security\\SecurityConfig.java",
|
| 187 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\ArticleService.java",
|
| 188 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\AuditLogService.java",
|
| 189 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\ContactMessageService.java",
|
| 190 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\CustomUserDetailsService.java",
|
| 191 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\FaqService.java",
|
| 192 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\FileStorageService.java",
|
| 193 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\GameCharacterService.java",
|
| 194 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\StoryService.java",
|
| 195 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\UserService.java",
|
| 196 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\WorldService.java",
|
| 197 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\ArticleServiceImpl.java",
|
| 198 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\AuditLogServiceImpl.java",
|
| 199 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\ContactMessageServiceImpl.java",
|
| 200 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\FaqServiceImpl.java",
|
| 201 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\FileStorageServiceImpl.java",
|
| 202 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\GameCharacterServiceImpl.java",
|
| 203 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\StoryServiceImpl.java",
|
| 204 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\UserServiceImpl.java",
|
| 205 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\service\\impl\\WorldServiceImpl.java",
|
| 206 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\util\\SecurityUtil.java",
|
| 207 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\util\\TimeUtil.java",
|
| 208 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\validator\\PasswordChangeMatchValidator.java",
|
| 209 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\validator\\PasswordRegisterMatchValidator.java",
|
| 210 |
+
"f:\\DarkMoon1\\src\\main\\java\\com\\darkfantasy\\validator\\ResetPasswordMatchValidator.java",
|
| 211 |
+
"f:\\DarkMoon1\\target\\"
|
| 212 |
+
],
|
| 213 |
+
"testSources": [],
|
| 214 |
+
"generatedSources": [
|
| 215 |
+
"f:\\DarkMoon1\\target\\"
|
| 216 |
+
],
|
| 217 |
+
"classpath": [
|
| 218 |
+
"F:\\DarkMoon1\\target\\classes",
|
| 219 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-starter-mail\\3.2.5\\spring-boot-starter-mail-3.2.5.jar",
|
| 220 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-starter\\3.2.5\\spring-boot-starter-3.2.5.jar",
|
| 221 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-starter-logging\\3.2.5\\spring-boot-starter-logging-3.2.5.jar",
|
| 222 |
+
"C:\\Users\\Admin\\.m2\\repository\\ch\\qos\\logback\\logback-classic\\1.4.14\\logback-classic-1.4.14.jar",
|
| 223 |
+
"C:\\Users\\Admin\\.m2\\repository\\ch\\qos\\logback\\logback-core\\1.4.14\\logback-core-1.4.14.jar",
|
| 224 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\apache\\logging\\log4j\\log4j-to-slf4j\\2.21.1\\log4j-to-slf4j-2.21.1.jar",
|
| 225 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\apache\\logging\\log4j\\log4j-api\\2.21.1\\log4j-api-2.21.1.jar",
|
| 226 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\slf4j\\jul-to-slf4j\\2.0.13\\jul-to-slf4j-2.0.13.jar",
|
| 227 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-core\\6.1.6\\spring-core-6.1.6.jar",
|
| 228 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-jcl\\6.1.6\\spring-jcl-6.1.6.jar",
|
| 229 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\yaml\\snakeyaml\\2.2\\snakeyaml-2.2.jar",
|
| 230 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-context-support\\6.1.6\\spring-context-support-6.1.6.jar",
|
| 231 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-beans\\6.1.6\\spring-beans-6.1.6.jar",
|
| 232 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-context\\6.1.6\\spring-context-6.1.6.jar",
|
| 233 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\eclipse\\angus\\jakarta.mail\\2.0.3\\jakarta.mail-2.0.3.jar",
|
| 234 |
+
"C:\\Users\\Admin\\.m2\\repository\\jakarta\\activation\\jakarta.activation-api\\2.1.3\\jakarta.activation-api-2.1.3.jar",
|
| 235 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\eclipse\\angus\\angus-activation\\2.0.2\\angus-activation-2.0.2.jar",
|
| 236 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\thymeleaf\\extras\\thymeleaf-extras-springsecurity6\\3.1.2.RELEASE\\thymeleaf-extras-springsecurity6-3.1.2.RELEASE.jar",
|
| 237 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\thymeleaf\\thymeleaf-spring6\\3.1.2.RELEASE\\thymeleaf-spring6-3.1.2.RELEASE.jar",
|
| 238 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\thymeleaf\\thymeleaf\\3.1.2.RELEASE\\thymeleaf-3.1.2.RELEASE.jar",
|
| 239 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\attoparser\\attoparser\\2.0.7.RELEASE\\attoparser-2.0.7.RELEASE.jar",
|
| 240 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\unbescape\\unbescape\\1.1.6.RELEASE\\unbescape-1.1.6.RELEASE.jar",
|
| 241 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\slf4j\\slf4j-api\\2.0.13\\slf4j-api-2.0.13.jar",
|
| 242 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-starter-thymeleaf\\3.2.5\\spring-boot-starter-thymeleaf-3.2.5.jar",
|
| 243 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\mariadb\\jdbc\\mariadb-java-client\\3.3.3\\mariadb-java-client-3.3.3.jar",
|
| 244 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\github\\waffle\\waffle-jna\\3.3.0\\waffle-jna-3.3.0.jar",
|
| 245 |
+
"C:\\Users\\Admin\\.m2\\repository\\net\\java\\dev\\jna\\jna\\5.13.0\\jna-5.13.0.jar",
|
| 246 |
+
"C:\\Users\\Admin\\.m2\\repository\\net\\java\\dev\\jna\\jna-platform\\5.13.0\\jna-platform-5.13.0.jar",
|
| 247 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\slf4j\\jcl-over-slf4j\\2.0.13\\jcl-over-slf4j-2.0.13.jar",
|
| 248 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\github\\ben-manes\\caffeine\\caffeine\\3.1.8\\caffeine-3.1.8.jar",
|
| 249 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\google\\errorprone\\error_prone_annotations\\2.21.1\\error_prone_annotations-2.21.1.jar",
|
| 250 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\checkerframework\\checker-qual\\3.32.0\\checker-qual-3.32.0.jar",
|
| 251 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-starter-validation\\3.2.5\\spring-boot-starter-validation-3.2.5.jar",
|
| 252 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\apache\\tomcat\\embed\\tomcat-embed-el\\10.1.20\\tomcat-embed-el-10.1.20.jar",
|
| 253 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\hibernate\\validator\\hibernate-validator\\8.0.1.Final\\hibernate-validator-8.0.1.Final.jar",
|
| 254 |
+
"C:\\Users\\Admin\\.m2\\repository\\jakarta\\validation\\jakarta.validation-api\\3.0.2\\jakarta.validation-api-3.0.2.jar",
|
| 255 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\jboss\\logging\\jboss-logging\\3.5.3.Final\\jboss-logging-3.5.3.Final.jar",
|
| 256 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\fasterxml\\classmate\\1.6.0\\classmate-1.6.0.jar",
|
| 257 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-starter-data-jpa\\3.2.5\\spring-boot-starter-data-jpa-3.2.5.jar",
|
| 258 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-starter-aop\\3.2.5\\spring-boot-starter-aop-3.2.5.jar",
|
| 259 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-aop\\6.1.6\\spring-aop-6.1.6.jar",
|
| 260 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\aspectj\\aspectjweaver\\1.9.22\\aspectjweaver-1.9.22.jar",
|
| 261 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-starter-jdbc\\3.2.5\\spring-boot-starter-jdbc-3.2.5.jar",
|
| 262 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\zaxxer\\HikariCP\\5.0.1\\HikariCP-5.0.1.jar",
|
| 263 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-jdbc\\6.1.6\\spring-jdbc-6.1.6.jar",
|
| 264 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\hibernate\\orm\\hibernate-core\\6.4.4.Final\\hibernate-core-6.4.4.Final.jar",
|
| 265 |
+
"C:\\Users\\Admin\\.m2\\repository\\jakarta\\persistence\\jakarta.persistence-api\\3.1.0\\jakarta.persistence-api-3.1.0.jar",
|
| 266 |
+
"C:\\Users\\Admin\\.m2\\repository\\jakarta\\transaction\\jakarta.transaction-api\\2.0.1\\jakarta.transaction-api-2.0.1.jar",
|
| 267 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\hibernate\\common\\hibernate-commons-annotations\\6.0.6.Final\\hibernate-commons-annotations-6.0.6.Final.jar",
|
| 268 |
+
"C:\\Users\\Admin\\.m2\\repository\\io\\smallrye\\jandex\\3.1.2\\jandex-3.1.2.jar",
|
| 269 |
+
"C:\\Users\\Admin\\.m2\\repository\\net\\bytebuddy\\byte-buddy\\1.14.13\\byte-buddy-1.14.13.jar",
|
| 270 |
+
"C:\\Users\\Admin\\.m2\\repository\\jakarta\\xml\\bind\\jakarta.xml.bind-api\\4.0.2\\jakarta.xml.bind-api-4.0.2.jar",
|
| 271 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\glassfish\\jaxb\\jaxb-runtime\\4.0.5\\jaxb-runtime-4.0.5.jar",
|
| 272 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\glassfish\\jaxb\\jaxb-core\\4.0.5\\jaxb-core-4.0.5.jar",
|
| 273 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\glassfish\\jaxb\\txw2\\4.0.5\\txw2-4.0.5.jar",
|
| 274 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\sun\\istack\\istack-commons-runtime\\4.1.2\\istack-commons-runtime-4.1.2.jar",
|
| 275 |
+
"C:\\Users\\Admin\\.m2\\repository\\jakarta\\inject\\jakarta.inject-api\\2.0.1\\jakarta.inject-api-2.0.1.jar",
|
| 276 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\antlr\\antlr4-runtime\\4.13.0\\antlr4-runtime-4.13.0.jar",
|
| 277 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\data\\spring-data-jpa\\3.2.5\\spring-data-jpa-3.2.5.jar",
|
| 278 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\data\\spring-data-commons\\3.2.5\\spring-data-commons-3.2.5.jar",
|
| 279 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-orm\\6.1.6\\spring-orm-6.1.6.jar",
|
| 280 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-tx\\6.1.6\\spring-tx-6.1.6.jar",
|
| 281 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-aspects\\6.1.6\\spring-aspects-6.1.6.jar",
|
| 282 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\projectlombok\\lombok\\1.18.32\\lombok-1.18.32.jar",
|
| 283 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-starter-oauth2-client\\3.2.5\\spring-boot-starter-oauth2-client-3.2.5.jar",
|
| 284 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\security\\spring-security-config\\6.2.4\\spring-security-config-6.2.4.jar",
|
| 285 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\security\\spring-security-core\\6.2.4\\spring-security-core-6.2.4.jar",
|
| 286 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\security\\spring-security-crypto\\6.2.4\\spring-security-crypto-6.2.4.jar",
|
| 287 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-expression\\6.1.6\\spring-expression-6.1.6.jar",
|
| 288 |
+
"C:\\Users\\Admin\\.m2\\repository\\io\\micrometer\\micrometer-observation\\1.12.5\\micrometer-observation-1.12.5.jar",
|
| 289 |
+
"C:\\Users\\Admin\\.m2\\repository\\io\\micrometer\\micrometer-commons\\1.12.5\\micrometer-commons-1.12.5.jar",
|
| 290 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\security\\spring-security-oauth2-client\\6.2.4\\spring-security-oauth2-client-6.2.4.jar",
|
| 291 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\security\\spring-security-oauth2-core\\6.2.4\\spring-security-oauth2-core-6.2.4.jar",
|
| 292 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\security\\spring-security-web\\6.2.4\\spring-security-web-6.2.4.jar",
|
| 293 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\nimbusds\\oauth2-oidc-sdk\\9.43.3\\oauth2-oidc-sdk-9.43.3.jar",
|
| 294 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\github\\stephenc\\jcip\\jcip-annotations\\1.0-1\\jcip-annotations-1.0-1.jar",
|
| 295 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\nimbusds\\content-type\\2.2\\content-type-2.2.jar",
|
| 296 |
+
"C:\\Users\\Admin\\.m2\\repository\\net\\minidev\\json-smart\\2.5.1\\json-smart-2.5.1.jar",
|
| 297 |
+
"C:\\Users\\Admin\\.m2\\repository\\net\\minidev\\accessors-smart\\2.5.1\\accessors-smart-2.5.1.jar",
|
| 298 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\ow2\\asm\\asm\\9.6\\asm-9.6.jar",
|
| 299 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\nimbusds\\lang-tag\\1.7\\lang-tag-1.7.jar",
|
| 300 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\security\\spring-security-oauth2-jose\\6.2.4\\spring-security-oauth2-jose-6.2.4.jar",
|
| 301 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\nimbusds\\nimbus-jose-jwt\\9.24.4\\nimbus-jose-jwt-9.24.4.jar",
|
| 302 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-starter-web\\3.2.5\\spring-boot-starter-web-3.2.5.jar",
|
| 303 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-starter-json\\3.2.5\\spring-boot-starter-json-3.2.5.jar",
|
| 304 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\fasterxml\\jackson\\core\\jackson-databind\\2.15.4\\jackson-databind-2.15.4.jar",
|
| 305 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\fasterxml\\jackson\\core\\jackson-annotations\\2.15.4\\jackson-annotations-2.15.4.jar",
|
| 306 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\fasterxml\\jackson\\core\\jackson-core\\2.15.4\\jackson-core-2.15.4.jar",
|
| 307 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\fasterxml\\jackson\\datatype\\jackson-datatype-jdk8\\2.15.4\\jackson-datatype-jdk8-2.15.4.jar",
|
| 308 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\fasterxml\\jackson\\datatype\\jackson-datatype-jsr310\\2.15.4\\jackson-datatype-jsr310-2.15.4.jar",
|
| 309 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\fasterxml\\jackson\\module\\jackson-module-parameter-names\\2.15.4\\jackson-module-parameter-names-2.15.4.jar",
|
| 310 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-web\\6.1.6\\spring-web-6.1.6.jar",
|
| 311 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\spring-webmvc\\6.1.6\\spring-webmvc-6.1.6.jar",
|
| 312 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-starter-tomcat\\3.2.5\\spring-boot-starter-tomcat-3.2.5.jar",
|
| 313 |
+
"C:\\Users\\Admin\\.m2\\repository\\jakarta\\annotation\\jakarta.annotation-api\\2.1.1\\jakarta.annotation-api-2.1.1.jar",
|
| 314 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\apache\\tomcat\\embed\\tomcat-embed-core\\10.1.20\\tomcat-embed-core-10.1.20.jar",
|
| 315 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\apache\\tomcat\\embed\\tomcat-embed-websocket\\10.1.20\\tomcat-embed-websocket-10.1.20.jar",
|
| 316 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-devtools\\3.2.5\\spring-boot-devtools-3.2.5.jar",
|
| 317 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot\\3.2.5\\spring-boot-3.2.5.jar",
|
| 318 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\springframework\\boot\\spring-boot-autoconfigure\\3.2.5\\spring-boot-autoconfigure-3.2.5.jar",
|
| 319 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\apache\\tomcat\\embed\\tomcat-embed-jasper\\10.1.20\\tomcat-embed-jasper-10.1.20.jar",
|
| 320 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\eclipse\\jdt\\ecj\\3.33.0\\ecj-3.33.0.jar",
|
| 321 |
+
"C:\\Users\\Admin\\.m2\\repository\\jakarta\\servlet\\jsp\\jstl\\jakarta.servlet.jsp.jstl-api\\3.0.0\\jakarta.servlet.jsp.jstl-api-3.0.0.jar",
|
| 322 |
+
"C:\\Users\\Admin\\.m2\\repository\\jakarta\\servlet\\jakarta.servlet-api\\6.0.0\\jakarta.servlet-api-6.0.0.jar",
|
| 323 |
+
"C:\\Users\\Admin\\.m2\\repository\\jakarta\\el\\jakarta.el-api\\5.0.0\\jakarta.el-api-5.0.0.jar",
|
| 324 |
+
"C:\\Users\\Admin\\.m2\\repository\\org\\glassfish\\web\\jakarta.servlet.jsp.jstl\\3.0.1\\jakarta.servlet.jsp.jstl-3.0.1.jar",
|
| 325 |
+
"C:\\Users\\Admin\\.m2\\repository\\com\\mysql\\mysql-connector-j\\8.3.0\\mysql-connector-j-8.3.0.jar"
|
| 326 |
+
]
|
| 327 |
+
}
|
| 328 |
+
]
|
| 329 |
+
}
|
.gitattributes
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/mvnw text eol=lf
|
| 2 |
+
*.cmd text eol=crlf
|
| 3 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.jpg filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.webp filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
HELP.md
|
| 2 |
+
target/
|
| 3 |
+
.mvn/wrapper/maven-wrapper.jar
|
| 4 |
+
!**/src/main/**/target/
|
| 5 |
+
!**/src/test/**/target/
|
| 6 |
+
|
| 7 |
+
### STS ###
|
| 8 |
+
.apt_generated
|
| 9 |
+
.classpath
|
| 10 |
+
.factorypath
|
| 11 |
+
.project
|
| 12 |
+
.settings
|
| 13 |
+
.springBeans
|
| 14 |
+
.sts4-cache
|
| 15 |
+
|
| 16 |
+
### IntelliJ IDEA ###
|
| 17 |
+
.idea
|
| 18 |
+
*.iws
|
| 19 |
+
*.iml
|
| 20 |
+
*.ipr
|
| 21 |
+
|
| 22 |
+
### NetBeans ###
|
| 23 |
+
/nbproject/private/
|
| 24 |
+
/nbbuild/
|
| 25 |
+
/dist/
|
| 26 |
+
/nbdist/
|
| 27 |
+
/.nb-gradle/
|
| 28 |
+
build/
|
| 29 |
+
!**/src/main/**/build/
|
| 30 |
+
!**/src/test/**/build/
|
| 31 |
+
|
| 32 |
+
### VS Code ###
|
| 33 |
+
.vscode/
|
.mvn/wrapper/maven-wrapper.properties
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
wrapperVersion=3.3.4
|
| 2 |
+
distributionType=only-script
|
| 3 |
+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.15/apache-maven-3.9.15-bin.zip
|
DockerFile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ---Build code ---
|
| 2 |
+
FROM maven:3.9.6-eclipse-temurin-21-alpine AS build
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
COPY . .
|
| 5 |
+
# Build run
|
| 6 |
+
RUN mvn clean package -DskipTests
|
| 7 |
+
|
| 8 |
+
FROM eclipse-temurin:21-jdk-alpine
|
| 9 |
+
WORKDIR /app
|
| 10 |
+
COPY --from=build /app/target/*.war app.war
|
| 11 |
+
|
| 12 |
+
EXPOSE 8080
|
| 13 |
+
ENTRYPOINT ["java","-jar","app.war"]
|
README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# DarkMoon
|
TEST_CASES.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Test Cases - DarkMoon / Moon Blight
|
| 2 |
+
|
| 3 |
+
| Test Case ID | Mô tả | Pre-condition | Steps | Expected Result | Actual Result | Status |
|
| 4 |
+
|---|---|---|---|---|---|---|
|
| 5 |
+
| TC-PUB-001 | Kiểm tra trang chủ hiển thị đúng dữ liệu nổi bật | Ứng dụng chạy, có dữ liệu article/character/story/world chưa xóa | 1. Mở `/` 2. Quan sát banner, latest articles, characters, top story, top world | Trang chủ load thành công, hiển thị tối đa 3 bài viết mới, 4 nhân vật, story/world ưu tiên cao nhất | | Chưa chạy |
|
| 6 |
+
| TC-PUB-002 | Kiểm tra trang chủ khi chưa có dữ liệu nổi bật | Database không có dữ liệu public hoặc toàn bộ dữ liệu đã soft-delete | 1. Mở `/` 2. Quan sát các khu vực dữ liệu động | Trang không lỗi 500, các khu vực trống/ẩn hợp lý | | Chưa chạy |
|
| 7 |
+
| TC-PUB-003 | Kiểm tra trang Story public | Có ít nhất 2 story chưa xóa với priority khác nhau | 1. Mở `/story` 2. Quan sát danh sách story | Chỉ hiển thị story chưa xóa, sắp xếp priority giảm dần | | Chưa chạy |
|
| 8 |
+
| TC-PUB-004 | Kiểm tra trang Characters public | Có nhân vật chưa xóa và nhân vật đã xóa | 1. Mở `/characters` 2. Quan sát danh sách | Chỉ hiển thị nhân vật chưa xóa, sắp xếp priority giảm dần | | Chưa chạy |
|
| 9 |
+
| TC-PUB-005 | Kiểm tra trang World public | Có world chưa xóa và world đã xóa | 1. Mở `/world` 2. Quan sát danh sách | Chỉ hiển thị world chưa xóa, sắp xếp priority giảm dần | | Chưa chạy |
|
| 10 |
+
| TC-PUB-006 | Kiểm tra trang News public mặc định | Có ít nhất 7 bài viết chưa xóa | 1. Mở `/news` 2. Quan sát số bài viết và nút load thêm | Hiển thị 6 bài viết, `hasMore` đúng, có nextLimit = 12 | | Chưa chạy |
|
| 11 |
+
| TC-PUB-007 | Kiểm tra trang News với tham số limit | Có ít nhất 12 bài viết chưa xóa | 1. Mở `/news?limit=12` 2. Đếm số bài viết | Hiển thị tối đa 12 bài viết, nextLimit = 18 nếu còn dữ liệu | | Chưa chạy |
|
| 12 |
+
| TC-PUB-008 | Kiểm tra bài viết quan trọng nổi bật trong News | Có bài viết type IMPORTANT chưa xóa mới nhất | 1. Mở `/news` 2. Kiểm tra khu vực featured article | Featured article là bài IMPORTANT mới nhất chưa xóa | | Chưa chạy |
|
| 13 |
+
| TC-PUB-009 | Kiểm tra chi tiết tin tức hợp lệ | Có bài viết chưa xóa với ID hợp lệ | 1. Mở `/news/{id}` 2. Quan sát nội dung và bài liên quan | Hiển thị đúng bài viết, danh sách liên quan không chứa chính bài đang xem | | Chưa chạy |
|
| 14 |
+
| TC-PUB-010 | Kiểm tra chi tiết tin tức ID không tồn tại | Không có article với ID test | 1. Mở `/news/999999` | Hệ thống xử lý lỗi hợp lý, không lộ stack trace cho người dùng | | Chưa chạy |
|
| 15 |
+
| TC-CON-001 | Kiểm tra trang Support hiển thị form và FAQ | Có FAQ chưa xóa | 1. Mở `/support` 2. Quan sát form liên hệ và FAQ | Trang hiển thị categories, platforms và FAQ chưa xóa | | Chưa chạy |
|
| 16 |
+
| TC-CON-002 | Gửi contact message hợp lệ | Người dùng ở trang `/support` | 1. Nhập tên, email hợp lệ, category, platform, message 2. Submit | Lưu contact message với processed = false, redirect `/support`, hiển thị thông báo thành công | | Chưa chạy |
|
| 17 |
+
| TC-CON-003 | Gửi contact thiếu tên | Người dùng ở trang `/support` | 1. Để trống tên 2. Nhập các trường còn lại hợp lệ 3. Submit | Không lưu dữ liệu, ở lại trang support, hiển thị lỗi kiểm tra thông tin | | Chưa chạy |
|
| 18 |
+
| TC-CON-004 | Gửi contact email sai định dạng | Người dùng ở trang `/support` | 1. Nhập email `abc` 2. Nhập các trường khác hợp lệ 3. Submit | Không lưu dữ liệu, hiển thị lỗi email không hợp lệ | | Chưa chạy |
|
| 19 |
+
| TC-CON-005 | Gửi contact thiếu category | Người dùng ở trang `/support` | 1. Không chọn category 2. Nhập các trường khác hợp lệ 3. Submit | Không lưu dữ liệu, hiển thị lỗi chọn danh mục | | Chưa chạy |
|
| 20 |
+
| TC-CON-007 | Gửi contact thiếu nội dung | Người dùng ở trang `/support` | 1. Để trống message 2. Nhập các trường khác hợp lệ 3. Submit | Không lưu dữ liệu, hiển thị lỗi nhập nội dung | | Chưa chạy |
|
| 21 |
+
| TC-CON-008 | Gửi contact message vượt quá 5000 ký tự | Người dùng ở trang `/support` | 1. Nhập message 5001 ký tự 2. Submit | Không lưu dữ liệu, hiển thị lỗi độ dài nội dung | | Chưa chạy |
|
| 22 |
+
| TC-CON-009 | Xem danh sách contact trong CMS | Có tài khoản STAFF đã đăng nhập, có contact messages | 1. Mở `/contact/blightmoon/list` | Hiển thị danh sách contact phân trang size 20 | | Chưa chạy |
|
| 23 |
+
| TC-CON-010 | Kiểm tra phân quyền contact CMS khi chưa đăng nhập | Chưa đăng nhập | 1. Mở `/contact/blightmoon/list` | Kỳ vọng bảo mật: bị chặn hoặc chuyển login. Nếu truy cập được là lỗi cấu hình route | | Chưa chạy |
|
| 24 |
+
| TC-AUTH-002 | Đăng nhập STAFF hợp lệ | Có tài khoản STAFF active, mật khẩu đúng | 1. Nhập username/email và password 2. Submit login | Đăng nhập thành công, tạo session, redirect `/dashboard/moonblight`, ghi audit LOGIN | | Chưa chạy |
|
| 25 |
+
| TC-AUTH-003 | Đăng nhập ADMIN hợp lệ | Có tài khoản ADMIN active, mật khẩu đúng | 1. Nhập credentials admin 2. Submit login | Đăng nhập thành công, redirect `/admin/moonblight/dashboard`, ghi audit LOGIN | | Chưa chạy |
|
| 26 |
+
| TC-AUTH-004 | Đăng nhập sai mật khẩu | Có tài khoản hợp lệ | 1. Nhập username/email đúng 2. Nhập password sai 3. Submit | Ở lại login, hiển thị `Tên đăng nhập hoặc mật khẩu không đúng.` | | Chưa chạy |
|
| 27 |
+
| TC-AUTH-005 | Đăng nhập thiếu username/email | Chưa đăng nhập | 1. Để trống login 2. Nhập password hợp lệ 3. Submit | Validation lỗi, không gọi authenticate, ở lại login | | Chưa chạy |
|
| 28 |
+
| TC-AUTH-007 | Đăng nhập mật khẩu dưới 8 ký tự | Chưa đăng nhập | 1. Nhập login 2. Nhập password 7 ký tự 3. Submit | Validation lỗi mật khẩu tối thiểu 8 ký tự | | Chưa chạy |
|
| 29 |
+
| TC-AUTH-008 | Đăng nhập tài khoản bị khóa | Có STAFF isActive = false | 1. Nhập credentials tài khoản bị khóa 2. Submit | Đăng nhập thất bại hoặc bị Spring Security từ chối, không vào dashboard | | Chưa chạy |
|
| 30 |
+
| TC-AUTH-010 | Đăng ký tài khoản STAFF hợp lệ | Username/email chưa tồn tại | 1. Nhập username 3-30 ký tự chữ/số, email hợp lệ, password >= 8, rePassword khớp, fullName 2. Submit | Tạo user role STAFF, isActive = false, password BCrypt, redirect login, thông báo thành công | | Chưa chạy |
|
| 31 |
+
| TC-AUTH-011 | Đăng ký username đã tồn tại | Database có username test | 1. Nhập username đã tồn tại 2. Nhập thông tin khác hợp lệ 3. Submit | Không tạo user, hiển thị `Tên đăng nhập đã tồn tại!`, xóa password trên form | | Chưa chạy |
|
| 32 |
+
| TC-AUTH-012 | Đăng ký email đã tồn tại | Database có email test | 1. Nhập email đã tồn tại 2. Nhập thông tin khác hợp lệ 3. Submit | Không tạo user, hiển thị `Email này đã được đăng ký!`, xóa password trên form | | Chưa chạy |
|
| 33 |
+
| TC-AUTH-017 | Đăng ký mật khẩu và xác nhận không khớp | Chưa đăng nhập | 1. Nhập password và rePassword khác nhau 2. Submit | Validation lỗi password confirmation, không tạo user | | Chưa chạy |
|
| 34 |
+
| TC-AUTH-018 | Đăng xuất khi đã đăng nhập | User đã đăng nhập | 1. POST `/user/moonblight/logout` | Session bị clear, redirect login, ghi audit LOGOUT | | Chưa chạy |
|
| 35 |
+
| TC-AUTH-020 | Gửi OTP cho email tồn tại | Có user với email hợp lệ | 1. Nhập email tồn tại 2. Submit forgot-password | Tạo OTP 6 chữ số, hết hạn sau 5 phút, gửi email, session `OTP_EMAIL`, redirect verify-otp | | Chưa chạy |
|
| 36 |
+
| TC-AUTH-021 | Gửi OTP cho email không tồn tại | Email chưa có trong DB | 1. Nhập email chưa đăng ký 2. Submit | Ở lại forgot-password, hiển thị `Email không tồn tại` | | Chưa chạy |
|
| 37 |
+
| TC-AUTH-022 | Verify OTP hợp lệ | Đã gửi OTP, OTP chưa hết hạn | 1. Mở verify-otp 2. Nhập OTP đúng 3. Submit | Session lưu RESET_EMAIL và RESET_OTP, redirect reset-password | | Chưa chạy |
|
| 38 |
+
| TC-AUTH-023 | Verify OTP sai | Đã gửi OTP | 1. Nhập OTP sai 2. Submit | Ở lại verify-otp, hiển thị `OTP không hợp lệ` | | Chưa chạy |
|
| 39 |
+
| TC-AUTH-024 | Verify OTP hết hạn | OTP đã quá 5 phút | 1. Nhập OTP hết hạn 2. Submit | Ở lại verify-otp, hiển thị `OTP đã hết hạn` | | Chưa chạy |
|
| 40 |
+
| TC-AUTH-025 | Reset password hợp lệ sau OTP | Session có RESET_EMAIL và RESET_OTP hợp lệ | 1. Mở reset-password 2. Nhập newPassword >= 8 và confirm khớp 3. Submit | Password được mã hóa mới, OTP marked used, session reset bị xóa, redirect login | | Chưa chạy |
|
| 41 |
+
| TC-AUTH-027 | Reset password xác nhận không khớp | Session OTP hợp lệ | 1. Nhập newPassword và confirmPassword khác nhau 2. Submit | Validation lỗi, ở lại reset-password | | Chưa chạy |
|
| 42 |
+
| TC-AUTH-028 | Reset password dưới 8 ký tự | Session OTP hợp lệ | 1. Nhập newPassword 7 ký tự 2. Submit | Validation lỗi min 8, không đổi mật khẩu | | Chưa chạy |
|
| 43 |
+
| TC-AUTH-029 | Đổi mật khẩu current user hợp lệ | User đã đăng nhập | 1. Mở `/user/moonblight/change-password` 2. Nhập oldPassword đúng, newPassword >= 8, confirm khớp 3. Submit | Password đổi thành công, mustChangePassword = false, redirect theo role, ghi audit UPDATE | | Chưa chạy |
|
| 44 |
+
| TC-AUTH-031 | Đổi mật khẩu xác nhận không khớp | User đã đăng nhập | 1. Nhập oldPassword đúng 2. Nhập newPassword và confirm khác nhau 3. Submit | Validation/lỗi xác nhận không khớp, không đổi mật khẩu | | Chưa chạy |
|
| 45 |
+
| TC-SEC-001 | Chặn truy cập dashboard STAFF khi chưa đăng nhập | Chưa đăng nhập | 1. Mở `/dashboard/moonblight` | Không cho xem dashboard, chuyển tới trang login hoặc trả 403 theo cấu hình Spring Security | | Chưa chạy |
|
| 46 |
+
| TC-SEC-002 | Chặn truy cập CMS Article khi chưa đăng nhập | Chưa đăng nhập | 1. Mở `/article/moonblight/list` | Không cho xem danh sách article CMS | | Chưa chạy |
|
| 47 |
+
| TC-SEC-003 | Chặn STAFF truy cập admin dashboard | Đăng nhập STAFF active | 1. Mở `/admin/moonblight/dashboard` | Bị từ chối 403 hoặc chuyển trang lỗi, không thấy dữ liệu admin | | Chưa chạy |
|
| 48 |
+
| TC-SEC-004 | Cho ADMIN truy cập admin dashboard | Đăng nhập ADMIN active | 1. Mở `/admin/moonblight/dashboard` | Hiển thị dashboard admin với totalUsers, activeUsers, inActiveUsers | | Chưa chạy |
|
| 49 |
+
| TC-SEC-005 | Cho STAFF truy cập CMS content | Đăng nhập STAFF active | 1. Mở `/article/moonblight/list` 2. Mở `/story/moonblight/list` | STAFF truy cập được các trang CMS content | | Chưa chạy |
|
| 50 |
+
| TC-SEC-007 | Force change password redirect | User đăng nhập có mustChangePassword = true | 1. Mở `/dashboard/moonblight` | Bị redirect tới `/user/moonblight/change-password` | | Chưa chạy |
|
| 51 |
+
| TC-SEC-008 | Force change password cho phép mở trang change-password | User đăng nhập có mustChangePassword = true | 1. Mở `/user/moonblight/change-password` | Trang đổi mật khẩu hiển thị, không bị redirect vòng lặp | | Chưa chạy |
|
| 52 |
+
| TC-SEC-010 | Kiểm tra API hash password internal public | Chưa đăng nhập | 1. Gọi `/api/moonblight/internal/hash?password=12345678` | Kỳ vọng bảo mật: API nội bộ không nên public. Nếu trả BCrypt khi chưa đăng nhập là rủi ro cần ghi nhận | | Chưa chạy |
|
| 53 |
+
| TC-ADM-001 | Xem danh sách tài khoản | Đăng nhập ADMIN | 1. Mở `/admin/moonblight/list` | Hiển thị accounts phân trang size 10, sắp xếp id tăng dần | | Chưa chạy |
|
| 54 |
+
| TC-ADM-002 | Xem chi tiết tài khoản hợp lệ | Đăng nhập ADMIN, có user ID hợp lệ | 1. Mở `/admin/moonblight/account/{id}` | Hiển thị đúng thông tin account | | Chưa chạy |
|
| 55 |
+
| TC-ADM-004 | Approve tài khoản STAFF chờ duyệt | Đăng nhập ADMIN, STAFF isActive = false | 1. POST `/admin/moonblight/account/{id}/approve` | User isActive = true, redirect detail, thông báo duyệt thành công, ghi audit UNLOCK | | Chưa chạy |
|
| 56 |
+
| TC-ADM-006 | Khóa tài khoản STAFF | Đăng nhập ADMIN, STAFF active | 1. POST `/admin/moonblight/account/{id}/lock` | User isActive = false, thông báo khóa thành công, ghi audit LOCK | | Chưa chạy |
|
| 57 |
+
| TC-ADM-007 | Mở khóa tài khoản STAFF | Đăng nhập ADMIN, STAFF inactive | 1. POST `/admin/moonblight/account/{id}/unlock` | User isActive = true, thông báo mở khóa thành công, ghi audit UNLOCK | | Chưa chạy |
|
| 58 |
+
| TC-ADM-008 | Không cho khóa tài khoản ADMIN | Đăng nhập ADMIN, target user role ADMIN | 1. POST `/admin/moonblight/account/{adminId}/lock` | Không đổi trạng thái admin, hiển thị lỗi `Không có thẩm quyền` hoặc xử lý lỗi phù hợp | | Chưa chạy |
|
| 59 |
+
| TC-ADM-009 | Xem audit log | Đăng nhập ADMIN, có audit logs | 1. Mở `/admin/moonblight/log` | Hiển thị logs size 20, sort createdAt giảm dần, activeMenu audit-log | | Chưa chạy |
|
| 60 |
+
| TC-ADM-010 | Dashboard admin thống kê user | Đăng nhập ADMIN, DB có active/inactive users | 1. Mở `/admin/moonblight/dashboard` | totalUsers = activeUsers + inActiveUsers, số liệu đúng DB | | Chưa chạy |
|
| 61 |
+
| TC-DASH-001 | Dashboard STAFF hiển thị thống kê content | Đăng nhập STAFF active | 1. Mở `/dashboard/moonblight` | Hiển thị count article/story/world/character/faq và contact chưa xử lý | | Chưa chạy |
|
| 62 |
+
| TC-ART-001 | Xem danh sách bài viết CMS | Đăng nhập STAFF, có articles | 1. Mở `/article/moonblight/list` | Hiển thị tất cả article kể cả đã xóa, phân trang size 10 | | Chưa chạy |
|
| 63 |
+
| TC-ART-003 | Tạo bài viết hợp lệ không thumbnail | Đăng nhập STAFF | 1. Nhập title, content, type 2. Không chọn thumbnail 3. Submit | Tạo article, createdBy là current user, redirect list, flash success, ghi audit CREATE | | Chưa chạy |
|
| 64 |
+
| TC-ART-004 | Tạo bài viết hợp lệ có thumbnail | Đăng nhập STAFF, có file ảnh hợp lệ | 1. Chọn thumbnail 2. Nhập thông tin hợp lệ 3. Submit | File lưu vào `uploads/articles`, thumbnailUrl được set, tạo article thành công | | Chưa chạy |
|
| 65 |
+
| TC-ART-005 | Tạo bài viết thiếu title | Đăng nhập STAFF | 1. Để trống title 2. Nhập content/type 3. Submit | Không tạo article, ở lại form, hiển thị lỗi title không được trống | | Chưa chạy |
|
| 66 |
+
| TC-ART-006 | Tạo bài viết title trên 255 ký tự | Đăng nhập STAFF | 1. Nhập title 256 ký tự 2. Nhập content/type 3. Submit | Không tạo article, hiển thị lỗi tiêu đề quá dài | | Chưa chạy |
|
| 67 |
+
| TC-ART-007 | Tạo bài viết thiếu content | Đăng nhập STAFF | 1. Nhập title/type 2. Để trống content 3. Submit | Không tạo article, hiển thị lỗi nội dung không được trống | | Chưa chạy |
|
| 68 |
+
| TC-ART-008 | Tạo bài viết thiếu type | Đăng nhập STAFF | 1. Nhập title/content 2. Không chọn type 3. Submit | Không tạo article, hiển thị lỗi loại bài viết không được trống | | Chưa chạy |
|
| 69 |
+
| TC-ART-009 | Sửa bài viết hợp lệ không đổi thumbnail | Đăng nhập STAFF, có article | 1. Mở `/article/moonblight/{id}/edit` 2. Sửa title/content/type 3. Không chọn thumbnail 4. Submit | Article cập nhật, thumbnailUrl cũ được giữ, updatedBy set, audit UPDATE | | Chưa chạy |
|
| 70 |
+
| TC-ART-011 | Soft-delete bài viết | Đăng nhập STAFF, có article chưa xóa | 1. POST `/article/moonblight/{id}/delete` | Article deleted = true, public news không còn hiển thị, CMS vẫn thấy, audit DELETE | | Chưa chạy |
|
| 71 |
+
| TC-ART-012 | Restore bài viết | Đăng nhập STAFF, có article deleted = true | 1. POST `/article/moonblight/{id}/restore` | Article deleted = false, public news hiển thị lại, audit RESTORE | | Chưa chạy |
|
| 72 |
+
| TC-STO-001 | Xem danh sách story CMS | Đăng nhập STAFF | 1. Mở `/story/moonblight/list` | Hiển thị stories phân trang size 10, activeMenu STORY | | Chưa chạy |
|
| 73 |
+
| TC-STO-002 | Tạo story hợp lệ có ảnh | Đăng nhập STAFF, có file ảnh | 1. Mở create 2. Nhập tag, title, content, quoteContent, quoteAuthor, priority 3. Chọn image 4. Submit | Tạo story thành công, lưu ảnh `uploads/stories`, audit CREATE | | Chưa chạy |
|
| 74 |
+
| TC-STO-005 | Tạo story priority nhỏ hơn 0 | Đăng nhập STAFF | 1. Nhập priority = -1 2. Submit | Không tạo story, hiển thị lỗi priority 0-100 | | Chưa chạy |
|
| 75 |
+
| TC-STO-006 | Tạo story priority lớn hơn 100 | Đăng nhập STAFF | 1. Nhập priority = 101 2. Submit | Không tạo story, hiển thị lỗi priority 0-100 | | Chưa chạy |
|
| 76 |
+
| TC-STO-007 | Sửa story không đổi ảnh | Đăng nhập STAFF, có story | 1. Mở edit 2. Sửa title/content/priority 3. Không chọn image 4. Submit | Story cập nhật, giữ image cũ, audit UPDATE | | Chưa chạy |
|
| 77 |
+
| TC-STO-008 | Soft-delete story | Đăng nhập STAFF, có story chưa xóa | 1. POST `/story/moonblight/{id}/delete` | Story deleted = true, public `/story` không hiển thị, audit DELETE | | Chưa chạy |
|
| 78 |
+
| TC-STO-009 | Restore story | Đăng nhập STAFF, có story deleted = true | 1. POST `/story/moonblight/{id}/restore` | Story deleted = false, public `/story` hiển thị lại, audit RESTORE | | Chưa chạy |
|
| 79 |
+
| TC-WLD-001 | Xem danh sách world CMS | Đăng nhập STAFF | 1. Mở `/world/moonblight/list` | Hiển thị worlds phân trang size 10, activeMenu WORLD | | Chưa chạy |
|
| 80 |
+
| TC-WLD-002 | Tạo world hợp lệ có ảnh | Đăng nhập STAFF, có file ảnh | 1. Mở create 2. Nhập tag, title, content, priority 3. Chọn image 4. Submit | Tạo world thành công, lưu ảnh `uploads/worlds`, audit CREATE | | Chưa chạy |
|
| 81 |
+
| TC-WLD-003 | Tạo world thiếu title | Đăng nhập STAFF | 1. Để trống title 2. Nhập các trường khác hợp lệ 3. Submit | Không tạo world, hiển thị lỗi tiêu đề không được trống | | Chưa chạy |
|
| 82 |
+
| TC-WLD-005 | Tạo world thiếu content | Đăng nhập STAFF | 1. Để trống content 2. Submit form | Không tạo world, hiển thị lỗi nội dung không được trống | | Chưa chạy |
|
| 83 |
+
| TC-WLD-007 | Sửa world hợp lệ không đổi ảnh | Đăng nhập STAFF, có world | 1. Mở edit 2. Sửa tag/title/content/priority 3. Không chọn image 4. Submit | World cập nhật, giữ image cũ, audit UPDATE | | Chưa chạy |
|
| 84 |
+
| TC-WLD-008 | Soft-delete world | Đăng nhập STAFF, có world chưa xóa | 1. POST `/world/moonblight/{id}/delete` | World deleted = true, public `/world` không hiển thị, audit DELETE | | Chưa chạy |
|
| 85 |
+
| TC-WLD-009 | Restore world | Đăng nhập STAFF, có world deleted = true | 1. POST `/world/moonblight/{id}/restore` | World deleted = false, public `/world` hiển thị lại, audit RESTORE | | Chưa chạy |
|
| 86 |
+
| TC-CHR-001 | Xem danh sách character CMS | Đăng nhập STAFF | 1. Mở `/character/moonblight/list` | Hiển thị characters phân trang size 10, activeMenu CHARACTER | | Chưa chạy |
|
| 87 |
+
| TC-CHR-002 | Tạo character hợp lệ có ảnh | Đăng nhập STAFF, có file ảnh | 1. Mở create 2. Nhập name, race, description, quote, priority 3. Chọn image 4. Submit | Tạo character thành công, lưu ảnh `uploads/characters`, audit CREATE | | Chưa chạy |
|
| 88 |
+
| TC-CHR-003 | Tạo character thiếu name | Đăng nhập STAFF | 1. Để trống name 2. Submit form hợp lệ còn lại | Không tạo character, hiển thị lỗi tên nhân v��t không được trống | | Chưa chạy |
|
| 89 |
+
| TC-CHR-005 | Tạo character thiếu race | Đăng nhập STAFF | 1. Để trống race 2. Submit | Không tạo character, hiển thị lỗi chủng tộc không được trống | | Chưa chạy |
|
| 90 |
+
| TC-CHR-008 | Tạo character priority ngoài biên | Đăng nhập STAFF | 1. Nhập priority = 101 hoặc -1 2. Submit | Không tạo character, hiển thị lỗi priority 0-100 | | Chưa chạy |
|
| 91 |
+
| TC-CHR-009 | Sửa character không đổi ảnh | Đăng nhập STAFF, có character | 1. Mở edit 2. Sửa name/race/description/priority 3. Không chọn image 4. Submit | Character cập nhật, giữ image cũ, audit UPDATE | | Chưa chạy |
|
| 92 |
+
| TC-CHR-010 | Soft-delete character | Đăng nhập STAFF, có character chưa xóa | 1. POST `/character/moonblight/{id}/delete` | Character deleted = true, public `/characters` không hiển thị, audit DELETE | | Chưa chạy |
|
| 93 |
+
| TC-CHR-011 | Restore character | Đăng nhập STAFF, có character deleted = true | 1. POST `/character/moonblight/{id}/restore` | Character deleted = false, public `/characters` hiển thị lại, audit RESTORE | | Chưa chạy |
|
| 94 |
+
| TC-FAQ-001 | Xem danh sách FAQ CMS | Đăng nhập STAFF | 1. Mở `/faq/moonblight/list` | Hiển thị FAQs phân trang size 10, activeMenu FAQ | | Chưa chạy |
|
| 95 |
+
| TC-FAQ-002 | Tạo FAQ hợp lệ | Đăng nhập STAFF | 1. Mở create 2. Nhập title, content, priority 3. Submit | Tạo FAQ thành công, redirect list, audit CREATE | | Chưa chạy |
|
| 96 |
+
| TC-FAQ-003 | Tạo FAQ thiếu title | Đăng nhập STAFF | 1. Để trống title 2. Nhập content/priority 3. Submit | Không tạo FAQ, hiển thị lỗi tiêu đề không được trống | | Chưa chạy |
|
| 97 |
+
| TC-FAQ-006 | Sửa FAQ hợp lệ | Đăng nhập STAFF, có FAQ | 1. Mở `/faq/moonblight/{id}/edit` 2. Sửa title/content/priority 3. Submit | FAQ cập nhật, redirect list, audit UPDATE | | Chưa chạy |
|
| 98 |
+
| TC-FAQ-007 | Soft-delete FAQ | Đăng nhập STAFF, có FAQ chưa xóa | 1. POST `/faq/moonblight/{id}/delete` | FAQ deleted = true, public support không hiển thị FAQ đó, audit DELETE | | Chưa chạy |
|
| 99 |
+
| TC-FAQ-008 | Restore FAQ | Đăng nhập STAFF, có FAQ deleted = true | 1. POST `/faq/moonblight/{id}/restore` | FAQ deleted = false, public support hiển thị lại, audit RESTORE | | Chưa chạy |
|
| 100 |
+
| TC-UPL-001 | Upload ảnh editor hợp lệ | Đăng nhập STAFF hoặc user được phép truy cập editor, có file ảnh | 1. POST `/api/upload` với multipart field `image` | Trả JSON `{url: ...}`, file lưu trong `uploads/editor` | | Chưa chạy |
|
| 101 |
+
| TC-UPL-002 | Upload ảnh editor file rỗng | Có quyền gọi API upload | 1. POST `/api/upload` với file rỗng | Trả HTTP 400 và JSON error `File upload không hợp lệ` | | Chưa chạy |
|
| 102 |
+
| TC-AUD-003 | Đánh dấu contact đã xử lý | Đăng nhập STAFF, có contact chưa xử lý | 1. POST `/contact/blightmoon/{id}/process` | Contact processed = true, redirect list, flash success, audit PROCESS | | Chưa chạy |
|
| 103 |
+
| TC-AUD-004 | Đánh dấu contact chưa xử lý | Đăng nhập STAFF, có contact đã xử lý | 1. POST `/contact/blightmoon/{id}/unprocess` | Contact processed = false, redirect list, flash success | | Chưa chạy |
|
| 104 |
+
| TC-AUD-005 | Xem chi tiết contact | Đăng nhập STAFF, có contact ID hợp lệ | 1. Mở `/contact/blightmoon/{id}` | Hiển thị đúng visitorName, visitorEmail, category, platform, message, processed | | Chưa chạy |
|
mvnw
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/sh
|
| 2 |
+
# ----------------------------------------------------------------------------
|
| 3 |
+
# Licensed to the Apache Software Foundation (ASF) under one
|
| 4 |
+
# or more contributor license agreements. See the NOTICE file
|
| 5 |
+
# distributed with this work for additional information
|
| 6 |
+
# regarding copyright ownership. The ASF licenses this file
|
| 7 |
+
# to you under the Apache License, Version 2.0 (the
|
| 8 |
+
# "License"); you may not use this file except in compliance
|
| 9 |
+
# with the License. You may obtain a copy of the License at
|
| 10 |
+
#
|
| 11 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 12 |
+
#
|
| 13 |
+
# Unless required by applicable law or agreed to in writing,
|
| 14 |
+
# software distributed under the License is distributed on an
|
| 15 |
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
| 16 |
+
# KIND, either express or implied. See the License for the
|
| 17 |
+
# specific language governing permissions and limitations
|
| 18 |
+
# under the License.
|
| 19 |
+
# ----------------------------------------------------------------------------
|
| 20 |
+
|
| 21 |
+
# ----------------------------------------------------------------------------
|
| 22 |
+
# Apache Maven Wrapper startup batch script, version 3.3.4
|
| 23 |
+
#
|
| 24 |
+
# Optional ENV vars
|
| 25 |
+
# -----------------
|
| 26 |
+
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
| 27 |
+
# MVNW_REPOURL - repo url base for downloading maven distribution
|
| 28 |
+
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
| 29 |
+
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
| 30 |
+
# ----------------------------------------------------------------------------
|
| 31 |
+
|
| 32 |
+
set -euf
|
| 33 |
+
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
| 34 |
+
|
| 35 |
+
# OS specific support.
|
| 36 |
+
native_path() { printf %s\\n "$1"; }
|
| 37 |
+
case "$(uname)" in
|
| 38 |
+
CYGWIN* | MINGW*)
|
| 39 |
+
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
| 40 |
+
native_path() { cygpath --path --windows "$1"; }
|
| 41 |
+
;;
|
| 42 |
+
esac
|
| 43 |
+
|
| 44 |
+
# set JAVACMD and JAVACCMD
|
| 45 |
+
set_java_home() {
|
| 46 |
+
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
| 47 |
+
if [ -n "${JAVA_HOME-}" ]; then
|
| 48 |
+
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
| 49 |
+
# IBM's JDK on AIX uses strange locations for the executables
|
| 50 |
+
JAVACMD="$JAVA_HOME/jre/sh/java"
|
| 51 |
+
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
| 52 |
+
else
|
| 53 |
+
JAVACMD="$JAVA_HOME/bin/java"
|
| 54 |
+
JAVACCMD="$JAVA_HOME/bin/javac"
|
| 55 |
+
|
| 56 |
+
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
| 57 |
+
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
| 58 |
+
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
| 59 |
+
return 1
|
| 60 |
+
fi
|
| 61 |
+
fi
|
| 62 |
+
else
|
| 63 |
+
JAVACMD="$(
|
| 64 |
+
'set' +e
|
| 65 |
+
'unset' -f command 2>/dev/null
|
| 66 |
+
'command' -v java
|
| 67 |
+
)" || :
|
| 68 |
+
JAVACCMD="$(
|
| 69 |
+
'set' +e
|
| 70 |
+
'unset' -f command 2>/dev/null
|
| 71 |
+
'command' -v javac
|
| 72 |
+
)" || :
|
| 73 |
+
|
| 74 |
+
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
| 75 |
+
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
| 76 |
+
return 1
|
| 77 |
+
fi
|
| 78 |
+
fi
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
# hash string like Java String::hashCode
|
| 82 |
+
hash_string() {
|
| 83 |
+
str="${1:-}" h=0
|
| 84 |
+
while [ -n "$str" ]; do
|
| 85 |
+
char="${str%"${str#?}"}"
|
| 86 |
+
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
| 87 |
+
str="${str#?}"
|
| 88 |
+
done
|
| 89 |
+
printf %x\\n $h
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
verbose() { :; }
|
| 93 |
+
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
| 94 |
+
|
| 95 |
+
die() {
|
| 96 |
+
printf %s\\n "$1" >&2
|
| 97 |
+
exit 1
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
trim() {
|
| 101 |
+
# MWRAPPER-139:
|
| 102 |
+
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
| 103 |
+
# Needed for removing poorly interpreted newline sequences when running in more
|
| 104 |
+
# exotic environments such as mingw bash on Windows.
|
| 105 |
+
printf "%s" "${1}" | tr -d '[:space:]'
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
scriptDir="$(dirname "$0")"
|
| 109 |
+
scriptName="$(basename "$0")"
|
| 110 |
+
|
| 111 |
+
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
| 112 |
+
while IFS="=" read -r key value; do
|
| 113 |
+
case "${key-}" in
|
| 114 |
+
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
| 115 |
+
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
| 116 |
+
esac
|
| 117 |
+
done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
| 118 |
+
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
| 119 |
+
|
| 120 |
+
case "${distributionUrl##*/}" in
|
| 121 |
+
maven-mvnd-*bin.*)
|
| 122 |
+
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
| 123 |
+
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
| 124 |
+
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
| 125 |
+
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
| 126 |
+
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
| 127 |
+
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
| 128 |
+
*)
|
| 129 |
+
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
| 130 |
+
distributionPlatform=linux-amd64
|
| 131 |
+
;;
|
| 132 |
+
esac
|
| 133 |
+
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
| 134 |
+
;;
|
| 135 |
+
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
| 136 |
+
*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
| 137 |
+
esac
|
| 138 |
+
|
| 139 |
+
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
| 140 |
+
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
| 141 |
+
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
| 142 |
+
distributionUrlName="${distributionUrl##*/}"
|
| 143 |
+
distributionUrlNameMain="${distributionUrlName%.*}"
|
| 144 |
+
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
| 145 |
+
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
|
| 146 |
+
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
| 147 |
+
|
| 148 |
+
exec_maven() {
|
| 149 |
+
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
| 150 |
+
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
if [ -d "$MAVEN_HOME" ]; then
|
| 154 |
+
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
| 155 |
+
exec_maven "$@"
|
| 156 |
+
fi
|
| 157 |
+
|
| 158 |
+
case "${distributionUrl-}" in
|
| 159 |
+
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
| 160 |
+
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
| 161 |
+
esac
|
| 162 |
+
|
| 163 |
+
# prepare tmp dir
|
| 164 |
+
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
| 165 |
+
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
| 166 |
+
trap clean HUP INT TERM EXIT
|
| 167 |
+
else
|
| 168 |
+
die "cannot create temp dir"
|
| 169 |
+
fi
|
| 170 |
+
|
| 171 |
+
mkdir -p -- "${MAVEN_HOME%/*}"
|
| 172 |
+
|
| 173 |
+
# Download and Install Apache Maven
|
| 174 |
+
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
| 175 |
+
verbose "Downloading from: $distributionUrl"
|
| 176 |
+
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
| 177 |
+
|
| 178 |
+
# select .zip or .tar.gz
|
| 179 |
+
if ! command -v unzip >/dev/null; then
|
| 180 |
+
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
| 181 |
+
distributionUrlName="${distributionUrl##*/}"
|
| 182 |
+
fi
|
| 183 |
+
|
| 184 |
+
# verbose opt
|
| 185 |
+
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
| 186 |
+
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
| 187 |
+
|
| 188 |
+
# normalize http auth
|
| 189 |
+
case "${MVNW_PASSWORD:+has-password}" in
|
| 190 |
+
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
| 191 |
+
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
| 192 |
+
esac
|
| 193 |
+
|
| 194 |
+
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
| 195 |
+
verbose "Found wget ... using wget"
|
| 196 |
+
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
| 197 |
+
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
| 198 |
+
verbose "Found curl ... using curl"
|
| 199 |
+
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
| 200 |
+
elif set_java_home; then
|
| 201 |
+
verbose "Falling back to use Java to download"
|
| 202 |
+
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
| 203 |
+
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
| 204 |
+
cat >"$javaSource" <<-END
|
| 205 |
+
public class Downloader extends java.net.Authenticator
|
| 206 |
+
{
|
| 207 |
+
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
| 208 |
+
{
|
| 209 |
+
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
| 210 |
+
}
|
| 211 |
+
public static void main( String[] args ) throws Exception
|
| 212 |
+
{
|
| 213 |
+
setDefault( new Downloader() );
|
| 214 |
+
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
| 215 |
+
}
|
| 216 |
+
}
|
| 217 |
+
END
|
| 218 |
+
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
| 219 |
+
verbose " - Compiling Downloader.java ..."
|
| 220 |
+
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
| 221 |
+
verbose " - Running Downloader.java ..."
|
| 222 |
+
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
| 223 |
+
fi
|
| 224 |
+
|
| 225 |
+
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
| 226 |
+
if [ -n "${distributionSha256Sum-}" ]; then
|
| 227 |
+
distributionSha256Result=false
|
| 228 |
+
if [ "$MVN_CMD" = mvnd.sh ]; then
|
| 229 |
+
echo "Checksum validation is not supported for maven-mvnd." >&2
|
| 230 |
+
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
| 231 |
+
exit 1
|
| 232 |
+
elif command -v sha256sum >/dev/null; then
|
| 233 |
+
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
|
| 234 |
+
distributionSha256Result=true
|
| 235 |
+
fi
|
| 236 |
+
elif command -v shasum >/dev/null; then
|
| 237 |
+
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
| 238 |
+
distributionSha256Result=true
|
| 239 |
+
fi
|
| 240 |
+
else
|
| 241 |
+
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
| 242 |
+
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
| 243 |
+
exit 1
|
| 244 |
+
fi
|
| 245 |
+
if [ $distributionSha256Result = false ]; then
|
| 246 |
+
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
| 247 |
+
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
| 248 |
+
exit 1
|
| 249 |
+
fi
|
| 250 |
+
fi
|
| 251 |
+
|
| 252 |
+
# unzip and move
|
| 253 |
+
if command -v unzip >/dev/null; then
|
| 254 |
+
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
| 255 |
+
else
|
| 256 |
+
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
| 257 |
+
fi
|
| 258 |
+
|
| 259 |
+
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
| 260 |
+
actualDistributionDir=""
|
| 261 |
+
|
| 262 |
+
# First try the expected directory name (for regular distributions)
|
| 263 |
+
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
|
| 264 |
+
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
|
| 265 |
+
actualDistributionDir="$distributionUrlNameMain"
|
| 266 |
+
fi
|
| 267 |
+
fi
|
| 268 |
+
|
| 269 |
+
# If not found, search for any directory with the Maven executable (for snapshots)
|
| 270 |
+
if [ -z "$actualDistributionDir" ]; then
|
| 271 |
+
# enable globbing to iterate over items
|
| 272 |
+
set +f
|
| 273 |
+
for dir in "$TMP_DOWNLOAD_DIR"/*; do
|
| 274 |
+
if [ -d "$dir" ]; then
|
| 275 |
+
if [ -f "$dir/bin/$MVN_CMD" ]; then
|
| 276 |
+
actualDistributionDir="$(basename "$dir")"
|
| 277 |
+
break
|
| 278 |
+
fi
|
| 279 |
+
fi
|
| 280 |
+
done
|
| 281 |
+
set -f
|
| 282 |
+
fi
|
| 283 |
+
|
| 284 |
+
if [ -z "$actualDistributionDir" ]; then
|
| 285 |
+
verbose "Contents of $TMP_DOWNLOAD_DIR:"
|
| 286 |
+
verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
|
| 287 |
+
die "Could not find Maven distribution directory in extracted archive"
|
| 288 |
+
fi
|
| 289 |
+
|
| 290 |
+
verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
| 291 |
+
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
|
| 292 |
+
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
| 293 |
+
|
| 294 |
+
clean || :
|
| 295 |
+
exec_maven "$@"
|
mvnw.cmd
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<# : batch portion
|
| 2 |
+
@REM ----------------------------------------------------------------------------
|
| 3 |
+
@REM Licensed to the Apache Software Foundation (ASF) under one
|
| 4 |
+
@REM or more contributor license agreements. See the NOTICE file
|
| 5 |
+
@REM distributed with this work for additional information
|
| 6 |
+
@REM regarding copyright ownership. The ASF licenses this file
|
| 7 |
+
@REM to you under the Apache License, Version 2.0 (the
|
| 8 |
+
@REM "License"); you may not use this file except in compliance
|
| 9 |
+
@REM with the License. You may obtain a copy of the License at
|
| 10 |
+
@REM
|
| 11 |
+
@REM http://www.apache.org/licenses/LICENSE-2.0
|
| 12 |
+
@REM
|
| 13 |
+
@REM Unless required by applicable law or agreed to in writing,
|
| 14 |
+
@REM software distributed under the License is distributed on an
|
| 15 |
+
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
| 16 |
+
@REM KIND, either express or implied. See the License for the
|
| 17 |
+
@REM specific language governing permissions and limitations
|
| 18 |
+
@REM under the License.
|
| 19 |
+
@REM ----------------------------------------------------------------------------
|
| 20 |
+
|
| 21 |
+
@REM ----------------------------------------------------------------------------
|
| 22 |
+
@REM Apache Maven Wrapper startup batch script, version 3.3.4
|
| 23 |
+
@REM
|
| 24 |
+
@REM Optional ENV vars
|
| 25 |
+
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
| 26 |
+
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
| 27 |
+
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
| 28 |
+
@REM ----------------------------------------------------------------------------
|
| 29 |
+
|
| 30 |
+
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
| 31 |
+
@SET __MVNW_CMD__=
|
| 32 |
+
@SET __MVNW_ERROR__=
|
| 33 |
+
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
| 34 |
+
@SET PSModulePath=
|
| 35 |
+
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
| 36 |
+
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
| 37 |
+
)
|
| 38 |
+
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
| 39 |
+
@SET __MVNW_PSMODULEP_SAVE=
|
| 40 |
+
@SET __MVNW_ARG0_NAME__=
|
| 41 |
+
@SET MVNW_USERNAME=
|
| 42 |
+
@SET MVNW_PASSWORD=
|
| 43 |
+
@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
|
| 44 |
+
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
| 45 |
+
@GOTO :EOF
|
| 46 |
+
: end batch / begin powershell #>
|
| 47 |
+
|
| 48 |
+
$ErrorActionPreference = "Stop"
|
| 49 |
+
if ($env:MVNW_VERBOSE -eq "true") {
|
| 50 |
+
$VerbosePreference = "Continue"
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
| 54 |
+
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
| 55 |
+
if (!$distributionUrl) {
|
| 56 |
+
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
| 60 |
+
"maven-mvnd-*" {
|
| 61 |
+
$USE_MVND = $true
|
| 62 |
+
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
| 63 |
+
$MVN_CMD = "mvnd.cmd"
|
| 64 |
+
break
|
| 65 |
+
}
|
| 66 |
+
default {
|
| 67 |
+
$USE_MVND = $false
|
| 68 |
+
$MVN_CMD = $script -replace '^mvnw','mvn'
|
| 69 |
+
break
|
| 70 |
+
}
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
| 74 |
+
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
| 75 |
+
if ($env:MVNW_REPOURL) {
|
| 76 |
+
$MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
| 77 |
+
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
|
| 78 |
+
}
|
| 79 |
+
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
| 80 |
+
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
| 81 |
+
|
| 82 |
+
$MAVEN_M2_PATH = "$HOME/.m2"
|
| 83 |
+
if ($env:MAVEN_USER_HOME) {
|
| 84 |
+
$MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
|
| 88 |
+
New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
$MAVEN_WRAPPER_DISTS = $null
|
| 92 |
+
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
|
| 93 |
+
$MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
|
| 94 |
+
} else {
|
| 95 |
+
$MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
|
| 99 |
+
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
| 100 |
+
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
| 101 |
+
|
| 102 |
+
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
| 103 |
+
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
| 104 |
+
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
| 105 |
+
exit $?
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
| 109 |
+
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
# prepare tmp dir
|
| 113 |
+
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
| 114 |
+
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
| 115 |
+
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
| 116 |
+
trap {
|
| 117 |
+
if ($TMP_DOWNLOAD_DIR.Exists) {
|
| 118 |
+
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
| 119 |
+
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
| 120 |
+
}
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
| 124 |
+
|
| 125 |
+
# Download and Install Apache Maven
|
| 126 |
+
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
| 127 |
+
Write-Verbose "Downloading from: $distributionUrl"
|
| 128 |
+
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
| 129 |
+
|
| 130 |
+
$webclient = New-Object System.Net.WebClient
|
| 131 |
+
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
| 132 |
+
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
| 133 |
+
}
|
| 134 |
+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
| 135 |
+
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
| 136 |
+
|
| 137 |
+
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
| 138 |
+
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
| 139 |
+
if ($distributionSha256Sum) {
|
| 140 |
+
if ($USE_MVND) {
|
| 141 |
+
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
| 142 |
+
}
|
| 143 |
+
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
| 144 |
+
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
| 145 |
+
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
| 146 |
+
}
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
# unzip and move
|
| 150 |
+
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
| 151 |
+
|
| 152 |
+
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
| 153 |
+
$actualDistributionDir = ""
|
| 154 |
+
|
| 155 |
+
# First try the expected directory name (for regular distributions)
|
| 156 |
+
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
|
| 157 |
+
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
|
| 158 |
+
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
|
| 159 |
+
$actualDistributionDir = $distributionUrlNameMain
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
# If not found, search for any directory with the Maven executable (for snapshots)
|
| 163 |
+
if (!$actualDistributionDir) {
|
| 164 |
+
Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
|
| 165 |
+
$testPath = Join-Path $_.FullName "bin/$MVN_CMD"
|
| 166 |
+
if (Test-Path -Path $testPath -PathType Leaf) {
|
| 167 |
+
$actualDistributionDir = $_.Name
|
| 168 |
+
}
|
| 169 |
+
}
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
if (!$actualDistributionDir) {
|
| 173 |
+
Write-Error "Could not find Maven distribution directory in extracted archive"
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
| 177 |
+
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
|
| 178 |
+
try {
|
| 179 |
+
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
| 180 |
+
} catch {
|
| 181 |
+
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
| 182 |
+
Write-Error "fail to move MAVEN_HOME"
|
| 183 |
+
}
|
| 184 |
+
} finally {
|
| 185 |
+
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
| 186 |
+
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
pom.xml
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
| 3 |
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
| 4 |
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
| 5 |
+
<modelVersion>4.0.0</modelVersion>
|
| 6 |
+
|
| 7 |
+
<parent>
|
| 8 |
+
<groupId>org.springframework.boot</groupId>
|
| 9 |
+
<artifactId>spring-boot-starter-parent</artifactId>
|
| 10 |
+
<version>3.2.5</version>
|
| 11 |
+
<relativePath />
|
| 12 |
+
</parent>
|
| 13 |
+
|
| 14 |
+
<groupId>com.darkfantasy</groupId>
|
| 15 |
+
<artifactId>darkfantasy-landing</artifactId>
|
| 16 |
+
<version>1.0.0</version>
|
| 17 |
+
<packaging>war</packaging>
|
| 18 |
+
<name>Dark Fantasy Landing Page</name>
|
| 19 |
+
<description>Gothic Horror Dark Fantasy Landing Page - Spring Boot + JSP</description>
|
| 20 |
+
|
| 21 |
+
<properties>
|
| 22 |
+
<java.version>21</java.version>
|
| 23 |
+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
| 24 |
+
</properties>
|
| 25 |
+
|
| 26 |
+
<dependencies>
|
| 27 |
+
<dependency>
|
| 28 |
+
<groupId>org.springframework.boot</groupId>
|
| 29 |
+
<artifactId>spring-boot-starter-mail</artifactId>
|
| 30 |
+
</dependency>
|
| 31 |
+
<dependency>
|
| 32 |
+
<groupId>org.thymeleaf.extras</groupId>
|
| 33 |
+
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
|
| 34 |
+
</dependency>
|
| 35 |
+
<dependency>
|
| 36 |
+
<groupId>org.springframework.boot</groupId>
|
| 37 |
+
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
| 38 |
+
</dependency>
|
| 39 |
+
<dependency>
|
| 40 |
+
<groupId>org.mariadb.jdbc</groupId>
|
| 41 |
+
<artifactId>mariadb-java-client</artifactId>
|
| 42 |
+
</dependency>
|
| 43 |
+
<dependency>
|
| 44 |
+
<groupId>org.springframework.boot</groupId>
|
| 45 |
+
<artifactId>spring-boot-starter-validation</artifactId>
|
| 46 |
+
</dependency>
|
| 47 |
+
<dependency>
|
| 48 |
+
<groupId>org.springframework.boot</groupId>
|
| 49 |
+
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
| 50 |
+
</dependency>
|
| 51 |
+
<dependency>
|
| 52 |
+
<groupId>org.projectlombok</groupId>
|
| 53 |
+
<artifactId>lombok</artifactId>
|
| 54 |
+
<!-- <version>1.18.32</version> -->
|
| 55 |
+
<optional>true</optional>
|
| 56 |
+
</dependency>
|
| 57 |
+
<!-- OAuth2 Google Login -->
|
| 58 |
+
<dependency>
|
| 59 |
+
<groupId>org.springframework.boot</groupId>
|
| 60 |
+
<artifactId>spring-boot-starter-oauth2-client</artifactId>
|
| 61 |
+
</dependency>
|
| 62 |
+
<!-- Spring Boot Web -->
|
| 63 |
+
<dependency>
|
| 64 |
+
<groupId>org.springframework.boot</groupId>
|
| 65 |
+
<artifactId>spring-boot-starter-web</artifactId>
|
| 66 |
+
</dependency>
|
| 67 |
+
|
| 68 |
+
<!-- Embedded Tomcat (not provided so java -jar works) -->
|
| 69 |
+
<dependency>
|
| 70 |
+
<groupId>org.springframework.boot</groupId>
|
| 71 |
+
<artifactId>spring-boot-starter-tomcat</artifactId>
|
| 72 |
+
</dependency>
|
| 73 |
+
<dependency>
|
| 74 |
+
<groupId>org.springframework.boot</groupId>
|
| 75 |
+
<artifactId>spring-boot-devtools</artifactId>
|
| 76 |
+
<scope>runtime</scope>
|
| 77 |
+
<optional>true</optional>
|
| 78 |
+
</dependency>
|
| 79 |
+
<!-- JSP support -->
|
| 80 |
+
<dependency>
|
| 81 |
+
<groupId>org.apache.tomcat.embed</groupId>
|
| 82 |
+
<artifactId>tomcat-embed-jasper</artifactId>
|
| 83 |
+
</dependency>
|
| 84 |
+
|
| 85 |
+
<!-- JSTL -->
|
| 86 |
+
<dependency>
|
| 87 |
+
<groupId>jakarta.servlet.jsp.jstl</groupId>
|
| 88 |
+
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
|
| 89 |
+
</dependency>
|
| 90 |
+
<dependency>
|
| 91 |
+
<groupId>org.glassfish.web</groupId>
|
| 92 |
+
<artifactId>jakarta.servlet.jsp.jstl</artifactId>
|
| 93 |
+
</dependency>
|
| 94 |
+
<dependency>
|
| 95 |
+
<groupId>com.mysql</groupId>
|
| 96 |
+
<artifactId>mysql-connector-j</artifactId>
|
| 97 |
+
<scope>runtime</scope>
|
| 98 |
+
</dependency>
|
| 99 |
+
|
| 100 |
+
</dependencies>
|
| 101 |
+
|
| 102 |
+
<build>
|
| 103 |
+
<plugins>
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
<plugin>
|
| 107 |
+
<groupId>org.springframework.boot</groupId>
|
| 108 |
+
<artifactId>spring-boot-maven-plugin</artifactId>
|
| 109 |
+
</plugin>
|
| 110 |
+
<plugin>
|
| 111 |
+
<groupId>org.apache.maven.plugins</groupId>
|
| 112 |
+
<artifactId>maven-compiler-plugin</artifactId>
|
| 113 |
+
<version>3.11.0</version>
|
| 114 |
+
|
| 115 |
+
<configuration>
|
| 116 |
+
<release>21</release>
|
| 117 |
+
|
| 118 |
+
<annotationProcessorPaths>
|
| 119 |
+
<path>
|
| 120 |
+
<groupId>org.projectlombok</groupId>
|
| 121 |
+
<artifactId>lombok</artifactId>
|
| 122 |
+
<version>1.18.32</version>
|
| 123 |
+
</path>
|
| 124 |
+
</annotationProcessorPaths>
|
| 125 |
+
|
| 126 |
+
</configuration>
|
| 127 |
+
</plugin>
|
| 128 |
+
</plugins>
|
| 129 |
+
</build>
|
| 130 |
+
</project>
|
src/main/java/com/darkfantasy/DarkFantasyApplication.java
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy;
|
| 2 |
+
|
| 3 |
+
import org.springframework.boot.SpringApplication;
|
| 4 |
+
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
| 5 |
+
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
| 6 |
+
import org.springframework.boot.builder.SpringApplicationBuilder;
|
| 7 |
+
|
| 8 |
+
@SpringBootApplication
|
| 9 |
+
public class DarkFantasyApplication extends SpringBootServletInitializer {
|
| 10 |
+
|
| 11 |
+
@Override
|
| 12 |
+
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
|
| 13 |
+
return application.sources(DarkFantasyApplication.class);
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
public static void main(String[] args) {
|
| 17 |
+
SpringApplication.run(DarkFantasyApplication.class, args);
|
| 18 |
+
}
|
| 19 |
+
}
|
src/main/java/com/darkfantasy/annotation/ChangePasswordMatch.java
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.annotation;
|
| 2 |
+
|
| 3 |
+
import java.lang.annotation.ElementType;
|
| 4 |
+
import java.lang.annotation.Retention;
|
| 5 |
+
import java.lang.annotation.RetentionPolicy;
|
| 6 |
+
import java.lang.annotation.Target;
|
| 7 |
+
|
| 8 |
+
import com.darkfantasy.validator.PasswordChangeMatchValidator;
|
| 9 |
+
|
| 10 |
+
import jakarta.validation.Constraint;
|
| 11 |
+
import jakarta.validation.Payload;
|
| 12 |
+
|
| 13 |
+
@Target(ElementType.TYPE)
|
| 14 |
+
@Retention(RetentionPolicy.RUNTIME)
|
| 15 |
+
@Constraint(validatedBy = PasswordChangeMatchValidator.class)
|
| 16 |
+
public @interface ChangePasswordMatch {
|
| 17 |
+
|
| 18 |
+
String message() default "Mật khẩu xác nhận không khớp";
|
| 19 |
+
|
| 20 |
+
Class<?>[] groups() default {};
|
| 21 |
+
|
| 22 |
+
Class<? extends Payload>[] payload() default {};
|
| 23 |
+
}
|
src/main/java/com/darkfantasy/annotation/RegisterPasswordMatch.java
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.annotation;
|
| 2 |
+
|
| 3 |
+
import java.lang.annotation.ElementType;
|
| 4 |
+
import java.lang.annotation.Retention;
|
| 5 |
+
import java.lang.annotation.RetentionPolicy;
|
| 6 |
+
import java.lang.annotation.Target;
|
| 7 |
+
|
| 8 |
+
import com.darkfantasy.validator.PasswordRegisterMatchValidator;
|
| 9 |
+
|
| 10 |
+
import jakarta.validation.Constraint;
|
| 11 |
+
import jakarta.validation.Payload;
|
| 12 |
+
|
| 13 |
+
@Target(ElementType.TYPE)
|
| 14 |
+
@Retention(RetentionPolicy.RUNTIME)
|
| 15 |
+
@Constraint(validatedBy = PasswordRegisterMatchValidator.class)
|
| 16 |
+
public @interface RegisterPasswordMatch {
|
| 17 |
+
|
| 18 |
+
String message() default "Mật khẩu xác nhận không khớp";
|
| 19 |
+
|
| 20 |
+
Class<?>[] groups() default {};
|
| 21 |
+
|
| 22 |
+
Class<? extends Payload>[] payload() default {};
|
| 23 |
+
}
|
src/main/java/com/darkfantasy/annotation/ResetPasswordMatch.java
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.annotation;
|
| 2 |
+
|
| 3 |
+
import java.lang.annotation.ElementType;
|
| 4 |
+
import java.lang.annotation.Retention;
|
| 5 |
+
import java.lang.annotation.RetentionPolicy;
|
| 6 |
+
import java.lang.annotation.Target;
|
| 7 |
+
|
| 8 |
+
import com.darkfantasy.validator.ResetPasswordMatchValidator;
|
| 9 |
+
|
| 10 |
+
import jakarta.validation.Constraint;
|
| 11 |
+
import jakarta.validation.Payload;
|
| 12 |
+
|
| 13 |
+
@Target(ElementType.TYPE)
|
| 14 |
+
@Retention(RetentionPolicy.RUNTIME)
|
| 15 |
+
@Constraint(validatedBy = ResetPasswordMatchValidator.class)
|
| 16 |
+
public @interface ResetPasswordMatch {
|
| 17 |
+
|
| 18 |
+
String message() default "Mật khẩu xác nhận không khớp";
|
| 19 |
+
|
| 20 |
+
Class<?>[] groups() default {};
|
| 21 |
+
|
| 22 |
+
Class<? extends Payload>[] payload() default {};
|
| 23 |
+
}
|
src/main/java/com/darkfantasy/config/SchedulingConfig.java
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// package com.darkfantasy.config;
|
| 2 |
+
|
| 3 |
+
// public class SchedulingConfig {
|
| 4 |
+
|
| 5 |
+
// }
|
src/main/java/com/darkfantasy/config/WebConfig.java
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.config;
|
| 2 |
+
|
| 3 |
+
import org.springframework.context.annotation.Configuration;
|
| 4 |
+
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
| 5 |
+
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
| 6 |
+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
| 7 |
+
|
| 8 |
+
import com.darkfantasy.security.ForceChangePasswordInterceptor;
|
| 9 |
+
|
| 10 |
+
import lombok.RequiredArgsConstructor;
|
| 11 |
+
|
| 12 |
+
@Configuration
|
| 13 |
+
@RequiredArgsConstructor
|
| 14 |
+
public class WebConfig implements WebMvcConfigurer {
|
| 15 |
+
private final ForceChangePasswordInterceptor interceptor;
|
| 16 |
+
|
| 17 |
+
@Override
|
| 18 |
+
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
| 19 |
+
registry.addResourceHandler("/uploads/**")
|
| 20 |
+
.addResourceLocations("file:uploads/");
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
@Override
|
| 24 |
+
public void addInterceptors(InterceptorRegistry registry) {
|
| 25 |
+
registry.addInterceptor(interceptor);
|
| 26 |
+
}
|
| 27 |
+
}
|
src/main/java/com/darkfantasy/constant/CmsMenu.java
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.constant;
|
| 2 |
+
|
| 3 |
+
import lombok.AccessLevel;
|
| 4 |
+
import lombok.NoArgsConstructor;
|
| 5 |
+
|
| 6 |
+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
| 7 |
+
public final class CmsMenu {
|
| 8 |
+
public static final String DASHBOARD = "dashboard";
|
| 9 |
+
public static final String ARTICLE = "article";
|
| 10 |
+
public static final String STORY = "story";
|
| 11 |
+
public static final String WORLD = "world";
|
| 12 |
+
public static final String CHARACTER = "character";
|
| 13 |
+
public static final String FAQ = "faq";
|
| 14 |
+
public static final String CONTRIBUTOR = "contributor";
|
| 15 |
+
public static final String CONTACT = "contact";
|
| 16 |
+
}
|
src/main/java/com/darkfantasy/constant/Routes.java
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.constant;
|
| 2 |
+
|
| 3 |
+
import lombok.AccessLevel;
|
| 4 |
+
import lombok.NoArgsConstructor;
|
| 5 |
+
|
| 6 |
+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
| 7 |
+
public final class Routes {
|
| 8 |
+
public static final String USER = "/user/moonblight";
|
| 9 |
+
public static final String ADMIN = "/admin/moonblight";
|
| 10 |
+
public static final String DASHBOARD = "/dashboard/moonblight";
|
| 11 |
+
public static final String ARTICLE = "/article/moonblight";
|
| 12 |
+
public static final String CHARACTER = "/character/moonblight";
|
| 13 |
+
public static final String WORLD = "/world/moonblight";
|
| 14 |
+
public static final String STORY = "/story/moonblight";
|
| 15 |
+
public static final String FAQ = "/faq/moonblight";
|
| 16 |
+
public static final String CONTRIBUTOR = "/contributor/moonblight";
|
| 17 |
+
public static final String CONTACT = "/contact/moonblight";
|
| 18 |
+
}
|
src/main/java/com/darkfantasy/controller/AdminController.java
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import org.springframework.data.domain.Pageable;
|
| 4 |
+
import org.springframework.data.domain.Sort.Direction;
|
| 5 |
+
import org.springframework.data.web.PageableDefault;
|
| 6 |
+
import org.springframework.stereotype.Controller;
|
| 7 |
+
import org.springframework.ui.Model;
|
| 8 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 9 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
| 10 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 11 |
+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
| 12 |
+
|
| 13 |
+
import lombok.RequiredArgsConstructor;
|
| 14 |
+
|
| 15 |
+
import com.darkfantasy.constant.Routes;
|
| 16 |
+
import com.darkfantasy.dto.user.UserResponse;
|
| 17 |
+
import com.darkfantasy.service.AuditLogService;
|
| 18 |
+
import com.darkfantasy.service.UserService;
|
| 19 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 20 |
+
|
| 21 |
+
@Controller
|
| 22 |
+
@RequiredArgsConstructor
|
| 23 |
+
@RequestMapping(Routes.ADMIN + "/")
|
| 24 |
+
public class AdminController {
|
| 25 |
+
private final AuditLogService auditLogService;
|
| 26 |
+
private final UserService userService;
|
| 27 |
+
|
| 28 |
+
@GetMapping("dashboard")
|
| 29 |
+
public String toDashboard(Model model) {
|
| 30 |
+
model.addAttribute("totalUsers", userService.count());
|
| 31 |
+
model.addAttribute("activeUsers", userService.countActiveUsers());
|
| 32 |
+
model.addAttribute("inActiveUsers", userService.countInactiveUsers());
|
| 33 |
+
return "cms/admin/admin-dashboard";
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
@GetMapping("list")
|
| 37 |
+
public String toAccountList(
|
| 38 |
+
@PageableDefault(size = 10) Pageable pageable,
|
| 39 |
+
Model model) {
|
| 40 |
+
model.addAttribute("accounts", userService.getAccounts(pageable));
|
| 41 |
+
return "cms/admin/account-list";
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
@GetMapping("account/{id}")
|
| 45 |
+
public String toDetailAccount(
|
| 46 |
+
@PathVariable("id") Long id,
|
| 47 |
+
Model model) {
|
| 48 |
+
|
| 49 |
+
UserResponse response = userService.findAccountById(id);
|
| 50 |
+
|
| 51 |
+
model.addAttribute("account", response);
|
| 52 |
+
return "cms/admin/account-detail";
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
@GetMapping("log")
|
| 56 |
+
public String toLogList(
|
| 57 |
+
@PageableDefault(size = 20, sort = "createdAt", direction = Direction.DESC) Pageable pageable,
|
| 58 |
+
Model model) {
|
| 59 |
+
|
| 60 |
+
model.addAttribute("logs", auditLogService.getLogs(pageable));
|
| 61 |
+
model.addAttribute("activeMenu", "audit-log");
|
| 62 |
+
return "cms/admin/audit-log";
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
@PostMapping("account/{id}/approve")
|
| 66 |
+
public String approveAccount(
|
| 67 |
+
@PathVariable("id") Long id,
|
| 68 |
+
RedirectAttributes redirectAttributes) {
|
| 69 |
+
|
| 70 |
+
userService.unlockUser(id);
|
| 71 |
+
|
| 72 |
+
redirectAttributes.addFlashAttribute("successMessage", "Duyệt tài khoản thành công");
|
| 73 |
+
return "redirect:" + Routes.ADMIN + "/account/" + id;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
@PostMapping("account/{id}/reject")
|
| 77 |
+
public String rejectAccount(
|
| 78 |
+
@PathVariable("id") Long id,
|
| 79 |
+
RedirectAttributes redirectAttributes) {
|
| 80 |
+
|
| 81 |
+
userService.lockUser(id);
|
| 82 |
+
|
| 83 |
+
redirectAttributes.addFlashAttribute("successMessage", "Từ chối tài khoản thành công");
|
| 84 |
+
return "redirect:" + Routes.ADMIN + "/account/" + id;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
@PostMapping("account/{id}/lock")
|
| 88 |
+
public String lockAccount(
|
| 89 |
+
@PathVariable("id") Long id,
|
| 90 |
+
RedirectAttributes redirectAttributes) {
|
| 91 |
+
|
| 92 |
+
userService.lockUser(id);
|
| 93 |
+
|
| 94 |
+
redirectAttributes.addFlashAttribute("successMessage", "Khóa tài khoản thành công");
|
| 95 |
+
return "redirect:" + Routes.ADMIN + "/account/" + id;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
@PostMapping("account/{id}/unlock")
|
| 99 |
+
public String unlockAccount(
|
| 100 |
+
@PathVariable("id") Long id,
|
| 101 |
+
RedirectAttributes redirectAttributes) {
|
| 102 |
+
|
| 103 |
+
userService.unlockUser(id);
|
| 104 |
+
|
| 105 |
+
redirectAttributes.addFlashAttribute("successMessage", "Mở khóa tài khoản thành công");
|
| 106 |
+
return "redirect:" + Routes.ADMIN + "/account/" + id;
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
}
|
src/main/java/com/darkfantasy/controller/ArticleController.java
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import org.springframework.data.domain.Pageable;
|
| 4 |
+
import org.springframework.data.web.PageableDefault;
|
| 5 |
+
import org.springframework.stereotype.Controller;
|
| 6 |
+
import org.springframework.ui.Model;
|
| 7 |
+
import org.springframework.validation.BindingResult;
|
| 8 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 9 |
+
import org.springframework.web.bind.annotation.ModelAttribute;
|
| 10 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
| 11 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 12 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 13 |
+
import org.springframework.web.bind.annotation.RequestParam;
|
| 14 |
+
import org.springframework.web.multipart.MultipartFile;
|
| 15 |
+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
| 16 |
+
|
| 17 |
+
import com.darkfantasy.constant.CmsMenu;
|
| 18 |
+
import com.darkfantasy.constant.Routes;
|
| 19 |
+
import com.darkfantasy.dto.article.ArticleResponse;
|
| 20 |
+
import com.darkfantasy.dto.article.CreateArticleRequest;
|
| 21 |
+
import com.darkfantasy.dto.article.UpdateArticleRequest;
|
| 22 |
+
import com.darkfantasy.entity.enums.ArticleType;
|
| 23 |
+
import com.darkfantasy.service.ArticleService;
|
| 24 |
+
import com.darkfantasy.service.FileStorageService;
|
| 25 |
+
|
| 26 |
+
import jakarta.validation.Valid;
|
| 27 |
+
import lombok.RequiredArgsConstructor;
|
| 28 |
+
|
| 29 |
+
@Controller
|
| 30 |
+
@RequestMapping(Routes.ARTICLE + "/")
|
| 31 |
+
@RequiredArgsConstructor
|
| 32 |
+
public class ArticleController {
|
| 33 |
+
|
| 34 |
+
private final ArticleService articleService;
|
| 35 |
+
private final FileStorageService fileStorageService;
|
| 36 |
+
|
| 37 |
+
@ModelAttribute("articleTypes")
|
| 38 |
+
public ArticleType[] articleTypes() {
|
| 39 |
+
return ArticleType.values();
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
@GetMapping({ "/list", "/list/" })
|
| 43 |
+
public String articleList(
|
| 44 |
+
@PageableDefault(size = 10) Pageable pageable,
|
| 45 |
+
Model model) {
|
| 46 |
+
model.addAttribute(
|
| 47 |
+
"articles",
|
| 48 |
+
articleService.getArticles(pageable));
|
| 49 |
+
model.addAttribute("activeMenu", CmsMenu.ARTICLE);
|
| 50 |
+
return "cms/article/article-list";
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
@GetMapping({ "/create", "/create/" })
|
| 54 |
+
public String toCreatePage(Model model) {
|
| 55 |
+
model.addAttribute("createArticleRequest", new CreateArticleRequest());
|
| 56 |
+
return "cms/article/article-create";
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
@PostMapping("/create")
|
| 60 |
+
public String createArticle(
|
| 61 |
+
@Valid @ModelAttribute("createArticleRequest") CreateArticleRequest request,
|
| 62 |
+
BindingResult result,
|
| 63 |
+
@RequestParam("thumbnail") MultipartFile thumbnailFile,
|
| 64 |
+
Model model,
|
| 65 |
+
RedirectAttributes redirectAttributes) {
|
| 66 |
+
|
| 67 |
+
if (result.hasErrors()) {
|
| 68 |
+
return "cms/article/article-create";
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
try {
|
| 72 |
+
|
| 73 |
+
if (!thumbnailFile.isEmpty()) {
|
| 74 |
+
request.setThumbnailUrl(fileStorageService.saveFile(thumbnailFile, "articles"));
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
ArticleResponse response = articleService.createArticle(request);
|
| 78 |
+
|
| 79 |
+
redirectAttributes.addFlashAttribute(
|
| 80 |
+
"successMessage",
|
| 81 |
+
"Tạo bài viết thành công: "
|
| 82 |
+
+ response.getTitle());
|
| 83 |
+
|
| 84 |
+
return "redirect:"+ Routes.ARTICLE +"/list";
|
| 85 |
+
|
| 86 |
+
} catch (Exception e) {
|
| 87 |
+
|
| 88 |
+
model.addAttribute(
|
| 89 |
+
"errorMessage",
|
| 90 |
+
e.getMessage());
|
| 91 |
+
|
| 92 |
+
return "cms/article/article-create";
|
| 93 |
+
}
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
@PostMapping("{id}/delete")
|
| 97 |
+
public String deleteArticle(
|
| 98 |
+
@PathVariable("id") Long id,
|
| 99 |
+
RedirectAttributes redirectAttributes) {
|
| 100 |
+
|
| 101 |
+
try {
|
| 102 |
+
articleService.deleteArticle(id);
|
| 103 |
+
|
| 104 |
+
redirectAttributes.addFlashAttribute(
|
| 105 |
+
"successMessage",
|
| 106 |
+
"Xóa bài viết thành công");
|
| 107 |
+
|
| 108 |
+
} catch (Exception e) {
|
| 109 |
+
redirectAttributes.addFlashAttribute(
|
| 110 |
+
"errorMessage",
|
| 111 |
+
e.getMessage());
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
return "redirect:"+ Routes.ARTICLE +"/list";
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
@PostMapping("{id}/restore")
|
| 118 |
+
public String restoreArticle(
|
| 119 |
+
@PathVariable("id") Long id,
|
| 120 |
+
RedirectAttributes redirectAttributes) {
|
| 121 |
+
try {
|
| 122 |
+
articleService.restoreArticle(id);
|
| 123 |
+
redirectAttributes.addFlashAttribute(
|
| 124 |
+
"successMessage",
|
| 125 |
+
"Khôi phục bài viết thành công");
|
| 126 |
+
} catch (Exception e) {
|
| 127 |
+
redirectAttributes.addFlashAttribute(
|
| 128 |
+
"errorMessage",
|
| 129 |
+
e.getMessage());
|
| 130 |
+
}
|
| 131 |
+
return "redirect:"+ Routes.ARTICLE +"/list";
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
@GetMapping({ "/{id}/edit", "/{id}/edit/" })
|
| 135 |
+
public String editArticlePage(
|
| 136 |
+
@PathVariable Long id,
|
| 137 |
+
Model model) {
|
| 138 |
+
|
| 139 |
+
ArticleResponse article = articleService.getArticleById(id);
|
| 140 |
+
|
| 141 |
+
model.addAttribute("article", article);
|
| 142 |
+
|
| 143 |
+
return "cms/article/article-edit";
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
@PostMapping("/{id}/edit")
|
| 147 |
+
public String editArticle(
|
| 148 |
+
@PathVariable Long id,
|
| 149 |
+
@Valid @ModelAttribute("article") UpdateArticleRequest request,
|
| 150 |
+
BindingResult result,
|
| 151 |
+
@RequestParam("thumbnail") MultipartFile thumbnailFile,
|
| 152 |
+
Model model) {
|
| 153 |
+
|
| 154 |
+
if (result.hasErrors()) {
|
| 155 |
+
ArticleResponse oldArticle = articleService.getArticleById(id);
|
| 156 |
+
|
| 157 |
+
request.setThumbnailUrl(
|
| 158 |
+
oldArticle.getThumbnailUrl());
|
| 159 |
+
|
| 160 |
+
model.addAttribute(
|
| 161 |
+
"articleTypes",
|
| 162 |
+
ArticleType.values());
|
| 163 |
+
|
| 164 |
+
return "cms/article/article-edit";
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
request.setId(id);
|
| 168 |
+
|
| 169 |
+
try {
|
| 170 |
+
|
| 171 |
+
if (!thumbnailFile.isEmpty()) {
|
| 172 |
+
|
| 173 |
+
request.setThumbnailUrl(
|
| 174 |
+
fileStorageService.saveFile(thumbnailFile, "articles"));
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
articleService.updateArticle(request);
|
| 178 |
+
|
| 179 |
+
return "redirect:"+ Routes.ARTICLE +"/list";
|
| 180 |
+
|
| 181 |
+
} catch (Exception e) {
|
| 182 |
+
|
| 183 |
+
model.addAttribute(
|
| 184 |
+
"articleTypes",
|
| 185 |
+
ArticleType.values());
|
| 186 |
+
|
| 187 |
+
model.addAttribute(
|
| 188 |
+
"errorMessage",
|
| 189 |
+
e.getMessage());
|
| 190 |
+
|
| 191 |
+
return "cms/article/article-edit";
|
| 192 |
+
}
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
}
|
src/main/java/com/darkfantasy/controller/ContactMessageController.java
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import org.springframework.data.domain.Pageable;
|
| 4 |
+
import org.springframework.data.web.PageableDefault;
|
| 5 |
+
import org.springframework.stereotype.Controller;
|
| 6 |
+
import org.springframework.ui.Model;
|
| 7 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 8 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
| 9 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 10 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 11 |
+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
| 12 |
+
|
| 13 |
+
import com.darkfantasy.constant.CmsMenu;
|
| 14 |
+
import com.darkfantasy.constant.Routes;
|
| 15 |
+
import com.darkfantasy.service.ContactMessageService;
|
| 16 |
+
|
| 17 |
+
import lombok.RequiredArgsConstructor;
|
| 18 |
+
|
| 19 |
+
@Controller
|
| 20 |
+
@RequiredArgsConstructor
|
| 21 |
+
@RequestMapping(Routes.CONTACT + "/")
|
| 22 |
+
public class ContactMessageController {
|
| 23 |
+
private final ContactMessageService contactMessageService;
|
| 24 |
+
@GetMapping("list")
|
| 25 |
+
public String toList(
|
| 26 |
+
@PageableDefault(size = 20) Pageable pageable,
|
| 27 |
+
Model model) {
|
| 28 |
+
model.addAttribute("messages", contactMessageService.getMessages(pageable));
|
| 29 |
+
model.addAttribute("activeMenu", CmsMenu.CONTACT);
|
| 30 |
+
|
| 31 |
+
return "cms/contact/contact-list";
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
@GetMapping("{id}")
|
| 35 |
+
public String detail(
|
| 36 |
+
@PathVariable Long id,
|
| 37 |
+
Model model) {
|
| 38 |
+
|
| 39 |
+
model.addAttribute("message", contactMessageService.getMessageById(id));
|
| 40 |
+
model.addAttribute("activeMenu", CmsMenu.CONTACT);
|
| 41 |
+
return "cms/contact/contact-detail";
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
@PostMapping("{id}/process")
|
| 45 |
+
public String process(@PathVariable Long id, RedirectAttributes redirectAttributes) {
|
| 46 |
+
contactMessageService.markAsProcessed(id);
|
| 47 |
+
redirectAttributes.addFlashAttribute("successMessage", "Đã đánh dấu xử lý.");
|
| 48 |
+
return "redirect:" + Routes.CONTACT + "/list";
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
@PostMapping("{id}/unprocess")
|
| 52 |
+
public String unprocess(
|
| 53 |
+
@PathVariable Long id,
|
| 54 |
+
RedirectAttributes redirectAttributes) {
|
| 55 |
+
|
| 56 |
+
contactMessageService.markAsUnprocessed(id);
|
| 57 |
+
|
| 58 |
+
redirectAttributes.addFlashAttribute("successMessage", "Đã đánh dấu chưa xử lý.");
|
| 59 |
+
|
| 60 |
+
return "redirect:" + Routes.CONTACT + "/list";
|
| 61 |
+
}
|
| 62 |
+
}
|
src/main/java/com/darkfantasy/controller/ContributorController.java
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import org.springframework.data.domain.Pageable;
|
| 4 |
+
import org.springframework.data.web.PageableDefault;
|
| 5 |
+
import org.springframework.stereotype.Controller;
|
| 6 |
+
import org.springframework.ui.Model;
|
| 7 |
+
import org.springframework.validation.BindingResult;
|
| 8 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 9 |
+
import org.springframework.web.bind.annotation.ModelAttribute;
|
| 10 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
| 11 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 12 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 13 |
+
import org.springframework.web.bind.annotation.RequestParam;
|
| 14 |
+
import org.springframework.web.multipart.MultipartFile;
|
| 15 |
+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
| 16 |
+
|
| 17 |
+
import com.darkfantasy.constant.CmsMenu;
|
| 18 |
+
import com.darkfantasy.constant.Routes;
|
| 19 |
+
import com.darkfantasy.dto.contributor.ContributorResponse;
|
| 20 |
+
import com.darkfantasy.dto.contributor.CreateContributorRequest;
|
| 21 |
+
import com.darkfantasy.dto.contributor.UpdateContributorRequest;
|
| 22 |
+
import com.darkfantasy.service.ContributorService;
|
| 23 |
+
import com.darkfantasy.service.FileStorageService;
|
| 24 |
+
|
| 25 |
+
import jakarta.validation.Valid;
|
| 26 |
+
import lombok.RequiredArgsConstructor;
|
| 27 |
+
|
| 28 |
+
@Controller
|
| 29 |
+
@RequestMapping(Routes.CONTRIBUTOR + "/")
|
| 30 |
+
@RequiredArgsConstructor
|
| 31 |
+
public class ContributorController {
|
| 32 |
+
|
| 33 |
+
private final ContributorService contributorService;
|
| 34 |
+
private final FileStorageService fileStorageService;
|
| 35 |
+
|
| 36 |
+
@GetMapping({ "/list", "/list/" })
|
| 37 |
+
public String contributorList(
|
| 38 |
+
@PageableDefault(size = 10) Pageable pageable,
|
| 39 |
+
Model model) {
|
| 40 |
+
model.addAttribute(
|
| 41 |
+
"contributors",
|
| 42 |
+
contributorService.getContributors(pageable));
|
| 43 |
+
model.addAttribute("activeMenu", CmsMenu.CONTRIBUTOR);
|
| 44 |
+
return "cms/contributor/contributor-list";
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
@GetMapping({ "/create", "/create/" })
|
| 48 |
+
public String toCreatePage(Model model) {
|
| 49 |
+
model.addAttribute("createContributorRequest", new CreateContributorRequest());
|
| 50 |
+
return "cms/contributor/contributor-create";
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
@PostMapping("/create")
|
| 54 |
+
public String createContributor(
|
| 55 |
+
@Valid @ModelAttribute("createContributorRequest") CreateContributorRequest request,
|
| 56 |
+
BindingResult result,
|
| 57 |
+
@RequestParam("thumbnail") MultipartFile thumbnailFile,
|
| 58 |
+
Model model,
|
| 59 |
+
RedirectAttributes redirectAttributes) {
|
| 60 |
+
|
| 61 |
+
if (result.hasErrors()) {
|
| 62 |
+
return "cms/contributor/contributor-create";
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
try {
|
| 66 |
+
|
| 67 |
+
if (!thumbnailFile.isEmpty()) {
|
| 68 |
+
request.setAvatar(fileStorageService.saveFile(thumbnailFile, "contributors"));
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
ContributorResponse response = contributorService.createContributor(request);
|
| 72 |
+
|
| 73 |
+
redirectAttributes.addFlashAttribute(
|
| 74 |
+
"successMessage",
|
| 75 |
+
"Tạo người đóng góp thành công: "
|
| 76 |
+
+ response.getName());
|
| 77 |
+
|
| 78 |
+
return "redirect:" + Routes.CONTRIBUTOR + "/list";
|
| 79 |
+
|
| 80 |
+
} catch (Exception e) {
|
| 81 |
+
|
| 82 |
+
model.addAttribute(
|
| 83 |
+
"errorMessage",
|
| 84 |
+
e.getMessage());
|
| 85 |
+
|
| 86 |
+
return "cms/contributor/contributor-create";
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
@PostMapping("{id}/delete")
|
| 91 |
+
public String deleteContributor(
|
| 92 |
+
@PathVariable("id") Long id,
|
| 93 |
+
RedirectAttributes redirectAttributes) {
|
| 94 |
+
|
| 95 |
+
try {
|
| 96 |
+
contributorService.deleteContributor(id);
|
| 97 |
+
|
| 98 |
+
redirectAttributes.addFlashAttribute(
|
| 99 |
+
"successMessage",
|
| 100 |
+
"Xóa người đóng góp thành công");
|
| 101 |
+
|
| 102 |
+
} catch (Exception e) {
|
| 103 |
+
redirectAttributes.addFlashAttribute(
|
| 104 |
+
"errorMessage",
|
| 105 |
+
e.getMessage());
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
return "redirect:" + Routes.CONTRIBUTOR + "/list";
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
@PostMapping("{id}/restore")
|
| 112 |
+
public String restoreContributor(
|
| 113 |
+
@PathVariable("id") Long id,
|
| 114 |
+
RedirectAttributes redirectAttributes) {
|
| 115 |
+
try {
|
| 116 |
+
contributorService.restoreContributor(id);
|
| 117 |
+
redirectAttributes.addFlashAttribute(
|
| 118 |
+
"successMessage",
|
| 119 |
+
"Khôi phục người đóng góp thành công");
|
| 120 |
+
} catch (Exception e) {
|
| 121 |
+
redirectAttributes.addFlashAttribute(
|
| 122 |
+
"errorMessage",
|
| 123 |
+
e.getMessage());
|
| 124 |
+
}
|
| 125 |
+
return "redirect:" + Routes.CONTRIBUTOR + "/list";
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
@GetMapping({ "/{id}/edit", "/{id}/edit/" })
|
| 129 |
+
public String editContributorPage(
|
| 130 |
+
@PathVariable Long id,
|
| 131 |
+
Model model) {
|
| 132 |
+
|
| 133 |
+
ContributorResponse contributor = contributorService.getContributorById(id);
|
| 134 |
+
|
| 135 |
+
model.addAttribute("contributor", contributor);
|
| 136 |
+
|
| 137 |
+
return "cms/contributor/contributor-edit";
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
@PostMapping("/{id}/edit")
|
| 141 |
+
public String editContributor(
|
| 142 |
+
@PathVariable Long id,
|
| 143 |
+
@Valid @ModelAttribute("contributor") UpdateContributorRequest request,
|
| 144 |
+
BindingResult result,
|
| 145 |
+
@RequestParam("thumbnail") MultipartFile thumbnailFile,
|
| 146 |
+
Model model) {
|
| 147 |
+
|
| 148 |
+
if (result.hasErrors()) {
|
| 149 |
+
ContributorResponse oldContributor = contributorService.getContributorById(id);
|
| 150 |
+
|
| 151 |
+
request.setAvatar(
|
| 152 |
+
oldContributor.getAvatar());
|
| 153 |
+
|
| 154 |
+
return "cms/contributor/contributor-edit";
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
request.setId(id);
|
| 158 |
+
|
| 159 |
+
try {
|
| 160 |
+
|
| 161 |
+
if (!thumbnailFile.isEmpty()) {
|
| 162 |
+
|
| 163 |
+
request.setAvatar(
|
| 164 |
+
fileStorageService.saveFile(thumbnailFile, "contributors"));
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
contributorService.updateContributor(request);
|
| 168 |
+
|
| 169 |
+
return "redirect:" + Routes.CONTRIBUTOR + "/list";
|
| 170 |
+
|
| 171 |
+
} catch (Exception e) {
|
| 172 |
+
|
| 173 |
+
model.addAttribute(
|
| 174 |
+
"errorMessage",
|
| 175 |
+
e.getMessage());
|
| 176 |
+
|
| 177 |
+
return "cms/contributor/contributor-edit";
|
| 178 |
+
}
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
}
|
src/main/java/com/darkfantasy/controller/DashboardController.java
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import org.springframework.stereotype.Controller;
|
| 4 |
+
import org.springframework.ui.Model;
|
| 5 |
+
import org.springframework.util.RouteMatcher.Route;
|
| 6 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 7 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 8 |
+
|
| 9 |
+
import com.darkfantasy.constant.CmsMenu;
|
| 10 |
+
import com.darkfantasy.constant.Routes;
|
| 11 |
+
import com.darkfantasy.service.ArticleService;
|
| 12 |
+
import com.darkfantasy.service.ContactMessageService;
|
| 13 |
+
import com.darkfantasy.service.ContributorService;
|
| 14 |
+
import com.darkfantasy.service.FaqService;
|
| 15 |
+
import com.darkfantasy.service.GameCharacterService;
|
| 16 |
+
import com.darkfantasy.service.StoryService;
|
| 17 |
+
import com.darkfantasy.service.WorldService;
|
| 18 |
+
|
| 19 |
+
import lombok.RequiredArgsConstructor;
|
| 20 |
+
|
| 21 |
+
@Controller
|
| 22 |
+
@RequiredArgsConstructor
|
| 23 |
+
@RequestMapping(Routes.DASHBOARD)
|
| 24 |
+
|
| 25 |
+
public class DashboardController {
|
| 26 |
+
private final ArticleService articleService;
|
| 27 |
+
private final GameCharacterService gameCharacterService;
|
| 28 |
+
private final WorldService worldService;
|
| 29 |
+
private final StoryService storyService;
|
| 30 |
+
private final FaqService faqService;
|
| 31 |
+
private final ContributorService contributorService;
|
| 32 |
+
private final ContactMessageService contactMessageService;
|
| 33 |
+
|
| 34 |
+
@GetMapping({ "", "/" })
|
| 35 |
+
public String toDashboard(Model model) {
|
| 36 |
+
model.addAttribute("activeMenu", CmsMenu.DASHBOARD);
|
| 37 |
+
model.addAttribute("articleCount", articleService.count());
|
| 38 |
+
model.addAttribute("storyCount", storyService.count());
|
| 39 |
+
model.addAttribute("worldCount", worldService.count());
|
| 40 |
+
model.addAttribute("characterCount", gameCharacterService.count());
|
| 41 |
+
model.addAttribute("faqCount", faqService.count());
|
| 42 |
+
model.addAttribute("contributorCount", contributorService.count());
|
| 43 |
+
model.addAttribute("unprocessedContactCount", contactMessageService.countByProcessedFalse());
|
| 44 |
+
return "cms/dashboard";
|
| 45 |
+
}
|
| 46 |
+
}
|
src/main/java/com/darkfantasy/controller/FaqController.java
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import org.springframework.data.domain.Pageable;
|
| 4 |
+
import org.springframework.data.web.PageableDefault;
|
| 5 |
+
import org.springframework.stereotype.Controller;
|
| 6 |
+
import org.springframework.ui.Model;
|
| 7 |
+
import org.springframework.util.RouteMatcher.Route;
|
| 8 |
+
import org.springframework.validation.BindingResult;
|
| 9 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 10 |
+
import org.springframework.web.bind.annotation.ModelAttribute;
|
| 11 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
| 12 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 13 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 14 |
+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
| 15 |
+
|
| 16 |
+
import com.darkfantasy.constant.CmsMenu;
|
| 17 |
+
import com.darkfantasy.constant.Routes;
|
| 18 |
+
import com.darkfantasy.dto.faq.CreateFaqRequest;
|
| 19 |
+
import com.darkfantasy.dto.faq.FaqResponse;
|
| 20 |
+
import com.darkfantasy.dto.faq.UpdateFaqRequest;
|
| 21 |
+
import com.darkfantasy.service.FaqService;
|
| 22 |
+
|
| 23 |
+
import jakarta.validation.Valid;
|
| 24 |
+
import lombok.RequiredArgsConstructor;
|
| 25 |
+
|
| 26 |
+
@Controller
|
| 27 |
+
@RequiredArgsConstructor
|
| 28 |
+
@RequestMapping(Routes.FAQ + "/")
|
| 29 |
+
public class FaqController {
|
| 30 |
+
private final FaqService faqService;
|
| 31 |
+
|
| 32 |
+
@GetMapping({ "list", "list/" })
|
| 33 |
+
public String toList(
|
| 34 |
+
@PageableDefault(size = 10) Pageable pageable,
|
| 35 |
+
Model model) {
|
| 36 |
+
|
| 37 |
+
model.addAttribute(
|
| 38 |
+
"faqs",
|
| 39 |
+
faqService.getFaqs(pageable));
|
| 40 |
+
model.addAttribute("activeMenu", CmsMenu.FAQ);
|
| 41 |
+
return "cms/faq/faq-list";
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
@GetMapping({ "create", "create/" })
|
| 45 |
+
public String toCreatePage(Model model) {
|
| 46 |
+
model.addAttribute("createFaqRequest", new CreateFaqRequest());
|
| 47 |
+
return "cms/faq/faq-create";
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
@PostMapping({ "create", "create/" })
|
| 51 |
+
public String createFaq(
|
| 52 |
+
@Valid @ModelAttribute("createFaqRequest") CreateFaqRequest request,
|
| 53 |
+
BindingResult result,
|
| 54 |
+
Model model,
|
| 55 |
+
RedirectAttributes redirectAttributes) {
|
| 56 |
+
|
| 57 |
+
if (result.hasErrors()) {
|
| 58 |
+
model.addAttribute(
|
| 59 |
+
"errorMessage",
|
| 60 |
+
"Vui lòng kiểm tra lại thông tin FAQ.");
|
| 61 |
+
return "cms/faq/faq-create";
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
try {
|
| 65 |
+
|
| 66 |
+
FaqResponse response = faqService.createFaq(request);
|
| 67 |
+
|
| 68 |
+
redirectAttributes.addFlashAttribute(
|
| 69 |
+
"successMessage",
|
| 70 |
+
"Tạo FAQ thành công: "
|
| 71 |
+
+ response.getTitle());
|
| 72 |
+
|
| 73 |
+
return "redirect:" + Routes.FAQ + "/list";
|
| 74 |
+
|
| 75 |
+
} catch (Exception e) {
|
| 76 |
+
|
| 77 |
+
model.addAttribute(
|
| 78 |
+
"errorMessage",
|
| 79 |
+
e.getMessage());
|
| 80 |
+
|
| 81 |
+
return "cms/faq/faq-create";
|
| 82 |
+
}
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
@GetMapping({ "{id}/edit", "{id}/edit/" })
|
| 86 |
+
public String toEditPage(@PathVariable("id") Long id, Model model) {
|
| 87 |
+
FaqResponse faq = faqService.getFaqById(id);
|
| 88 |
+
UpdateFaqRequest request = UpdateFaqRequest.builder()
|
| 89 |
+
.id(faq.getId())
|
| 90 |
+
.title(faq.getTitle())
|
| 91 |
+
.content(faq.getContent())
|
| 92 |
+
.priority(faq.getPriority())
|
| 93 |
+
.build();
|
| 94 |
+
|
| 95 |
+
model.addAttribute(
|
| 96 |
+
"updateFaqRequest",
|
| 97 |
+
request);
|
| 98 |
+
model.addAttribute("faq", faq);
|
| 99 |
+
return "cms/faq/faq-edit";
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
@PostMapping("{id}/edit")
|
| 103 |
+
public String editFaq(@PathVariable Long id,
|
| 104 |
+
@Valid @ModelAttribute("faq") UpdateFaqRequest request,
|
| 105 |
+
BindingResult result,
|
| 106 |
+
Model model) {
|
| 107 |
+
if (result.hasErrors()) {
|
| 108 |
+
return "cms/faq/faq-edit";
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
request.setId(id);
|
| 112 |
+
|
| 113 |
+
try {
|
| 114 |
+
faqService.updateFaq(request);
|
| 115 |
+
|
| 116 |
+
return "redirect:" + Routes.FAQ + "/list";
|
| 117 |
+
} catch (Exception e) {
|
| 118 |
+
model.addAttribute(
|
| 119 |
+
"errorMessage",
|
| 120 |
+
e.getMessage());
|
| 121 |
+
|
| 122 |
+
return "cms/faq/faq-edit";
|
| 123 |
+
}
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
@PostMapping("{id}/delete")
|
| 127 |
+
public String deleteFaq(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
|
| 128 |
+
try {
|
| 129 |
+
faqService.deleteFaq(id);
|
| 130 |
+
|
| 131 |
+
redirectAttributes.addFlashAttribute(
|
| 132 |
+
"successMessage",
|
| 133 |
+
"Xóa faq thành công");
|
| 134 |
+
|
| 135 |
+
} catch (Exception e) {
|
| 136 |
+
redirectAttributes.addFlashAttribute(
|
| 137 |
+
"errorMessage",
|
| 138 |
+
e.getMessage());
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
return "redirect:" + Routes.FAQ + "/list";
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
@PostMapping("{id}/restore")
|
| 145 |
+
public String restoreFaq(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
|
| 146 |
+
try {
|
| 147 |
+
faqService.restoreFaq(id);
|
| 148 |
+
|
| 149 |
+
redirectAttributes.addFlashAttribute(
|
| 150 |
+
"successMessage",
|
| 151 |
+
"Khôi phục faq thành công");
|
| 152 |
+
|
| 153 |
+
} catch (Exception e) {
|
| 154 |
+
redirectAttributes.addFlashAttribute(
|
| 155 |
+
"errorMessage",
|
| 156 |
+
e.getMessage());
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
return "redirect:" + Routes.FAQ + "/list";
|
| 160 |
+
}
|
| 161 |
+
}
|
src/main/java/com/darkfantasy/controller/GameCharacterController.java
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import org.springframework.data.domain.Pageable;
|
| 4 |
+
import org.springframework.data.web.PageableDefault;
|
| 5 |
+
import org.springframework.stereotype.Controller;
|
| 6 |
+
import org.springframework.ui.Model;
|
| 7 |
+
import org.springframework.validation.BindingResult;
|
| 8 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 9 |
+
import org.springframework.web.bind.annotation.RequestParam;
|
| 10 |
+
import org.springframework.web.multipart.MultipartFile;
|
| 11 |
+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
| 12 |
+
|
| 13 |
+
import com.darkfantasy.constant.CmsMenu;
|
| 14 |
+
import com.darkfantasy.constant.Routes;
|
| 15 |
+
import com.darkfantasy.dto.gamecharacter.CreateGameCharacterRequest;
|
| 16 |
+
import com.darkfantasy.dto.gamecharacter.GameCharacterResponse;
|
| 17 |
+
import com.darkfantasy.dto.gamecharacter.UpdateGameCharacterRequest;
|
| 18 |
+
import com.darkfantasy.service.FileStorageService;
|
| 19 |
+
import com.darkfantasy.service.GameCharacterService;
|
| 20 |
+
|
| 21 |
+
import jakarta.validation.Valid;
|
| 22 |
+
import lombok.RequiredArgsConstructor;
|
| 23 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 24 |
+
import org.springframework.web.bind.annotation.ModelAttribute;
|
| 25 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
| 26 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 27 |
+
|
| 28 |
+
@Controller
|
| 29 |
+
@RequestMapping(Routes.CHARACTER + "/")
|
| 30 |
+
@RequiredArgsConstructor
|
| 31 |
+
public class GameCharacterController {
|
| 32 |
+
private final GameCharacterService gameCharacterService;
|
| 33 |
+
private final FileStorageService fileStorageService;
|
| 34 |
+
|
| 35 |
+
@GetMapping({ "list", "list/" })
|
| 36 |
+
public String toList(
|
| 37 |
+
@PageableDefault(size = 10) Pageable pageable,
|
| 38 |
+
Model model) {
|
| 39 |
+
|
| 40 |
+
model.addAttribute(
|
| 41 |
+
"characters",
|
| 42 |
+
gameCharacterService.getCharacters(pageable));
|
| 43 |
+
model.addAttribute("activeMenu", CmsMenu.CHARACTER);
|
| 44 |
+
return "cms/character/character-list";
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
@GetMapping({ "create", "create/" })
|
| 48 |
+
public String toCreatePage(Model model) {
|
| 49 |
+
model.addAttribute("createGameCharacterRequest", new CreateGameCharacterRequest());
|
| 50 |
+
return "cms/character/character-create";
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
@PostMapping({ "create", "create/" })
|
| 54 |
+
public String createCharacter(
|
| 55 |
+
@Valid @ModelAttribute("createGameCharacterRequest") CreateGameCharacterRequest request,
|
| 56 |
+
BindingResult result,
|
| 57 |
+
@RequestParam(value = "imageFile", required = false) MultipartFile imageFile,
|
| 58 |
+
Model model,
|
| 59 |
+
RedirectAttributes redirectAttributes) {
|
| 60 |
+
|
| 61 |
+
if (result.hasErrors()) {
|
| 62 |
+
model.addAttribute("errorMessage", "Vui lòng kiểm tra lại thông tin nhân vật.");
|
| 63 |
+
return "cms/character/character-create";
|
| 64 |
+
|
| 65 |
+
}
|
| 66 |
+
try {
|
| 67 |
+
if (imageFile != null && !imageFile.isEmpty()) {
|
| 68 |
+
request.setImage(fileStorageService.saveFile(imageFile, "characters"));
|
| 69 |
+
}
|
| 70 |
+
GameCharacterResponse response = gameCharacterService.createGameCharacter(request);
|
| 71 |
+
|
| 72 |
+
redirectAttributes.addFlashAttribute(
|
| 73 |
+
"successMessage",
|
| 74 |
+
"Tạo nhân vật thành công: "
|
| 75 |
+
+ response.getName());
|
| 76 |
+
return "redirect:" + Routes.CHARACTER + "/list";
|
| 77 |
+
} catch (Exception e) {
|
| 78 |
+
model.addAttribute(
|
| 79 |
+
"errorMessage",
|
| 80 |
+
e.getMessage());
|
| 81 |
+
|
| 82 |
+
return "cms/character/character-create";
|
| 83 |
+
}
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
@GetMapping({ "{id}/edit", "{id}/edit/" })
|
| 87 |
+
public String toEditPage(@PathVariable("id") Long id, Model model) {
|
| 88 |
+
GameCharacterResponse character = gameCharacterService.getGameCharacterById(id);
|
| 89 |
+
model.addAttribute("character", character);
|
| 90 |
+
return "cms/character/character-edit";
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
@PostMapping("{id}/edit")
|
| 94 |
+
public String editCharacter(@PathVariable Long id,
|
| 95 |
+
@Valid @ModelAttribute("character") UpdateGameCharacterRequest request,
|
| 96 |
+
BindingResult result,
|
| 97 |
+
@RequestParam(value = "imageFile", required = false) MultipartFile imageFile,
|
| 98 |
+
Model model) {
|
| 99 |
+
if (result.hasErrors()) {
|
| 100 |
+
GameCharacterResponse oldCharacter = gameCharacterService.getGameCharacterById(id);
|
| 101 |
+
|
| 102 |
+
request.setImage(
|
| 103 |
+
oldCharacter.getImage());
|
| 104 |
+
|
| 105 |
+
return "cms/character/character-edit";
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
request.setId(id);
|
| 109 |
+
|
| 110 |
+
try {
|
| 111 |
+
if (imageFile != null && !imageFile.isEmpty()) {
|
| 112 |
+
request.setImage(
|
| 113 |
+
fileStorageService.saveFile(imageFile, "characters"));
|
| 114 |
+
}
|
| 115 |
+
gameCharacterService.updateGameCharacter(request);
|
| 116 |
+
|
| 117 |
+
return "redirect:" + Routes.CHARACTER + "/list";
|
| 118 |
+
} catch (Exception e) {
|
| 119 |
+
model.addAttribute(
|
| 120 |
+
"errorMessage",
|
| 121 |
+
e.getMessage());
|
| 122 |
+
|
| 123 |
+
return "cms/character/character-edit";
|
| 124 |
+
}
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
@PostMapping("{id}/delete")
|
| 128 |
+
public String deleteCharacter(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
|
| 129 |
+
try {
|
| 130 |
+
gameCharacterService.deleteCharacter(id);
|
| 131 |
+
|
| 132 |
+
redirectAttributes.addFlashAttribute(
|
| 133 |
+
"successMessage",
|
| 134 |
+
"Xóa nhân vật thành công");
|
| 135 |
+
|
| 136 |
+
} catch (Exception e) {
|
| 137 |
+
redirectAttributes.addFlashAttribute(
|
| 138 |
+
"errorMessage",
|
| 139 |
+
e.getMessage());
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
return "redirect:" + Routes.CHARACTER + "/list";
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
@PostMapping("{id}/restore")
|
| 146 |
+
public String restoreCharacter(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
|
| 147 |
+
try {
|
| 148 |
+
gameCharacterService.restoreCharacter(id);
|
| 149 |
+
|
| 150 |
+
redirectAttributes.addFlashAttribute(
|
| 151 |
+
"successMessage",
|
| 152 |
+
"Khôi phục nhân vật thành công");
|
| 153 |
+
|
| 154 |
+
} catch (Exception e) {
|
| 155 |
+
redirectAttributes.addFlashAttribute(
|
| 156 |
+
"errorMessage",
|
| 157 |
+
e.getMessage());
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
return "redirect:" + Routes.CHARACTER + "/list";
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
}
|
src/main/java/com/darkfantasy/controller/ImageController.java
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import java.io.IOException;
|
| 4 |
+
import java.util.Map;
|
| 5 |
+
|
| 6 |
+
import org.springframework.http.HttpStatus;
|
| 7 |
+
import org.springframework.http.ResponseEntity;
|
| 8 |
+
import org.springframework.stereotype.Controller;
|
| 9 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 10 |
+
import org.springframework.web.bind.annotation.RequestParam;
|
| 11 |
+
import org.springframework.web.bind.annotation.ResponseBody;
|
| 12 |
+
import org.springframework.web.multipart.MultipartFile;
|
| 13 |
+
|
| 14 |
+
import com.darkfantasy.service.FileStorageService;
|
| 15 |
+
|
| 16 |
+
import lombok.RequiredArgsConstructor;
|
| 17 |
+
|
| 18 |
+
@Controller
|
| 19 |
+
@RequiredArgsConstructor
|
| 20 |
+
public class ImageController {
|
| 21 |
+
private final FileStorageService fileStorageService;
|
| 22 |
+
|
| 23 |
+
@PostMapping("/api/upload")
|
| 24 |
+
@ResponseBody
|
| 25 |
+
public ResponseEntity<?> uploadImage(@RequestParam("image") MultipartFile file) {
|
| 26 |
+
try {
|
| 27 |
+
// Gọi Service để lưu file vào thư mục "editor" (nó sẽ tạo ra uploads/editor/)
|
| 28 |
+
String fileUrl = fileStorageService.saveFile(file, "editors");
|
| 29 |
+
|
| 30 |
+
// Trả về JSON cho Quill Frontend: {"url": "/uploads/editor/abc123hash.png"}
|
| 31 |
+
return ResponseEntity.ok(Map.of("url", fileUrl));
|
| 32 |
+
|
| 33 |
+
} catch (IllegalArgumentException e) {
|
| 34 |
+
// Xử lý lỗi file rỗng theo Exception đã ném ở Service
|
| 35 |
+
return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
|
| 36 |
+
} catch (IOException e) {
|
| 37 |
+
// Xử lý lỗi đọc ghi file
|
| 38 |
+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
| 39 |
+
.body(Map.of("error", "Lỗi xử lý file trên server"));
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
}
|
src/main/java/com/darkfantasy/controller/InternalController.java
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import org.springframework.security.crypto.password.PasswordEncoder;
|
| 4 |
+
import org.springframework.stereotype.Controller;
|
| 5 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 6 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 7 |
+
import org.springframework.web.bind.annotation.RequestParam;
|
| 8 |
+
import org.springframework.web.bind.annotation.ResponseBody;
|
| 9 |
+
|
| 10 |
+
import lombok.RequiredArgsConstructor;
|
| 11 |
+
|
| 12 |
+
@Controller
|
| 13 |
+
@RequestMapping("/api/moonblight/internal")
|
| 14 |
+
@RequiredArgsConstructor
|
| 15 |
+
public class InternalController {
|
| 16 |
+
|
| 17 |
+
private final PasswordEncoder passwordEncoder;
|
| 18 |
+
|
| 19 |
+
@GetMapping("/hash")
|
| 20 |
+
@ResponseBody
|
| 21 |
+
public String hash(
|
| 22 |
+
@RequestParam String password) {
|
| 23 |
+
|
| 24 |
+
return passwordEncoder.encode(password);
|
| 25 |
+
}
|
| 26 |
+
}
|
src/main/java/com/darkfantasy/controller/PageController.java
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import org.springframework.data.domain.Page;
|
| 4 |
+
import org.springframework.data.domain.PageRequest;
|
| 5 |
+
import org.springframework.data.domain.Pageable;
|
| 6 |
+
import org.springframework.stereotype.Controller;
|
| 7 |
+
import org.springframework.ui.Model;
|
| 8 |
+
import org.springframework.validation.BindingResult;
|
| 9 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 10 |
+
import org.springframework.web.bind.annotation.ModelAttribute;
|
| 11 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
| 12 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 13 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 14 |
+
import org.springframework.web.bind.annotation.RequestParam;
|
| 15 |
+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
| 16 |
+
|
| 17 |
+
import com.darkfantasy.dto.article.ArticleResponse;
|
| 18 |
+
import com.darkfantasy.dto.contact.CreateContactMessageRequest;
|
| 19 |
+
import com.darkfantasy.dto.contributor.ContributorResponse;
|
| 20 |
+
import com.darkfantasy.entity.enums.ContactCategory;
|
| 21 |
+
import com.darkfantasy.entity.enums.ContactPlatform;
|
| 22 |
+
import com.darkfantasy.service.ArticleService;
|
| 23 |
+
import com.darkfantasy.service.ContactMessageService;
|
| 24 |
+
import com.darkfantasy.service.ContributorService;
|
| 25 |
+
import com.darkfantasy.service.FaqService;
|
| 26 |
+
import com.darkfantasy.service.GameCharacterService;
|
| 27 |
+
import com.darkfantasy.service.StoryService;
|
| 28 |
+
import com.darkfantasy.service.WorldService;
|
| 29 |
+
|
| 30 |
+
import jakarta.validation.Valid;
|
| 31 |
+
import lombok.RequiredArgsConstructor;
|
| 32 |
+
|
| 33 |
+
@Controller
|
| 34 |
+
@RequiredArgsConstructor
|
| 35 |
+
@RequestMapping("/")
|
| 36 |
+
public class PageController {
|
| 37 |
+
private final ArticleService articleService;
|
| 38 |
+
private final GameCharacterService gameCharacterService;
|
| 39 |
+
private final WorldService worldService;
|
| 40 |
+
private final StoryService storyService;
|
| 41 |
+
private final FaqService faqService;
|
| 42 |
+
private final ContactMessageService contactMessageService;
|
| 43 |
+
private final ContributorService contributorService;
|
| 44 |
+
@GetMapping
|
| 45 |
+
public String index(Model model) {
|
| 46 |
+
|
| 47 |
+
model.addAttribute("activePage", "home");
|
| 48 |
+
model.addAttribute("pageTitle", "Moon Blight - Enter the Darkness");
|
| 49 |
+
|
| 50 |
+
model.addAttribute("articles", articleService.getLatestArticles(3));
|
| 51 |
+
model.addAttribute("characters", gameCharacterService.getCharactersLimit(4));
|
| 52 |
+
model.addAttribute("topWorld", worldService.getWorldDeletedFalseHighestPriority());
|
| 53 |
+
model.addAttribute("topStory", storyService.getStoryDeletedFalseHighestPriority());
|
| 54 |
+
|
| 55 |
+
return "forward:/WEB-INF/views/index.jsp";
|
| 56 |
+
// return "index";
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
@GetMapping("story")
|
| 60 |
+
public String story(Model model) {
|
| 61 |
+
model.addAttribute("activePage", "story");
|
| 62 |
+
model.addAttribute("pageTitle", "The Story - Moon Blight");
|
| 63 |
+
|
| 64 |
+
model.addAttribute("stories", storyService.getStoriesDeletedFalse());
|
| 65 |
+
return "forward:/WEB-INF/views/storys/story.jsp";
|
| 66 |
+
// return "story/story";
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
@GetMapping("characters")
|
| 70 |
+
public String characters(Model model) {
|
| 71 |
+
model.addAttribute("activePage", "characters");
|
| 72 |
+
model.addAttribute("pageTitle", "Characters - Moon Blight");
|
| 73 |
+
|
| 74 |
+
model.addAttribute("characters", gameCharacterService.getCharactersDeletedFalse());
|
| 75 |
+
return "forward:/WEB-INF/views/characters/characters.jsp";
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
@GetMapping("enemies")
|
| 79 |
+
public String enemies(Model model) {
|
| 80 |
+
model.addAttribute("activePage", "enemies");
|
| 81 |
+
model.addAttribute("pageTitle", "Enemies - Moon Blight");
|
| 82 |
+
return "forward:/WEB-INF/views/enemies/enemies.jsp";
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
@GetMapping("weapons")
|
| 86 |
+
public String weapons(Model model) {
|
| 87 |
+
model.addAttribute("activePage", "weapons");
|
| 88 |
+
model.addAttribute("pageTitle", "Weapons - Moon Blight");
|
| 89 |
+
return "forward:/WEB-INF/views/weapons/weapons.jsp";
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
@GetMapping("world")
|
| 93 |
+
public String world(Model model) {
|
| 94 |
+
|
| 95 |
+
model.addAttribute("activePage", "world");
|
| 96 |
+
model.addAttribute("pageTitle", "The World - Moon Blight");
|
| 97 |
+
|
| 98 |
+
model.addAttribute("worlds", worldService.getWorldsDeletedFalse());
|
| 99 |
+
|
| 100 |
+
return "forward:/WEB-INF/views/worlds/world.jsp";
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
@GetMapping("news")
|
| 104 |
+
public String news(
|
| 105 |
+
@RequestParam(defaultValue = "6") int limit,
|
| 106 |
+
Model model) {
|
| 107 |
+
|
| 108 |
+
Pageable pageable = PageRequest.of(0, limit);
|
| 109 |
+
|
| 110 |
+
Page<ArticleResponse> page = articleService.getArticlesDeletedFalse(pageable);
|
| 111 |
+
|
| 112 |
+
model.addAttribute(
|
| 113 |
+
"articles",
|
| 114 |
+
page.getContent());
|
| 115 |
+
|
| 116 |
+
model.addAttribute(
|
| 117 |
+
"hasMore",
|
| 118 |
+
page.getTotalElements() > limit);
|
| 119 |
+
|
| 120 |
+
model.addAttribute(
|
| 121 |
+
"nextLimit",
|
| 122 |
+
limit + 6);
|
| 123 |
+
model.addAttribute(
|
| 124 |
+
"featuredArticle",
|
| 125 |
+
articleService.getLatestImportantArticle());
|
| 126 |
+
|
| 127 |
+
return "forward:/WEB-INF/views/news/news.jsp";
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
@GetMapping("aboutUs")
|
| 131 |
+
public String community(
|
| 132 |
+
@RequestParam(defaultValue = "4") int limit,
|
| 133 |
+
Model model) {
|
| 134 |
+
|
| 135 |
+
Pageable pageable = PageRequest.of(0, limit);
|
| 136 |
+
|
| 137 |
+
Page<ContributorResponse> page = contributorService.getContributorsDeletedFalse(pageable);
|
| 138 |
+
|
| 139 |
+
model.addAttribute(
|
| 140 |
+
"contributors",
|
| 141 |
+
page.getContent());
|
| 142 |
+
|
| 143 |
+
model.addAttribute(
|
| 144 |
+
"hasMore",
|
| 145 |
+
page.getTotalElements() > limit);
|
| 146 |
+
|
| 147 |
+
model.addAttribute(
|
| 148 |
+
"nextLimit",
|
| 149 |
+
limit + 4);
|
| 150 |
+
|
| 151 |
+
model.addAttribute(
|
| 152 |
+
"activePage",
|
| 153 |
+
"community");
|
| 154 |
+
|
| 155 |
+
model.addAttribute(
|
| 156 |
+
"pageTitle",
|
| 157 |
+
"Community - Moon Blight");
|
| 158 |
+
|
| 159 |
+
return "forward:/WEB-INF/views/community/aboutUs.jsp";
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
@GetMapping("news/{id}")
|
| 163 |
+
public String newsDetail(@PathVariable("id") Long id,
|
| 164 |
+
Model model) {
|
| 165 |
+
model.addAttribute("article", articleService.getPublicArticleById(id));
|
| 166 |
+
model.addAttribute("articles", articleService.getLatestArticlesExcept(id, 3));
|
| 167 |
+
return "forward:/WEB-INF/views/news/newDetail.jsp";
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
@GetMapping("support")
|
| 171 |
+
public String support(Model model) {
|
| 172 |
+
populateSupportModel(model);
|
| 173 |
+
model.addAttribute("contactRequest", new CreateContactMessageRequest());
|
| 174 |
+
return "forward:/WEB-INF/views/supports/support.jsp";
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
@PostMapping("support")
|
| 178 |
+
public String submitContactMessage(
|
| 179 |
+
@Valid @ModelAttribute("contactRequest") CreateContactMessageRequest request,
|
| 180 |
+
BindingResult result,
|
| 181 |
+
Model model,
|
| 182 |
+
RedirectAttributes redirectAttributes) {
|
| 183 |
+
|
| 184 |
+
if (result.hasErrors()) {
|
| 185 |
+
populateSupportModel(model);
|
| 186 |
+
model.addAttribute("errorMessage", "Vui lòng kiểm tra lại thông tin liên hệ.");
|
| 187 |
+
return "forward:/WEB-INF/views/supports/support.jsp";
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
contactMessageService.createMessage(request);
|
| 191 |
+
|
| 192 |
+
redirectAttributes.addFlashAttribute(
|
| 193 |
+
"successMessage",
|
| 194 |
+
"Tin nhắn đã được gửi thành công.");
|
| 195 |
+
|
| 196 |
+
return "redirect:/support";
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
private void populateSupportModel(Model model) {
|
| 200 |
+
model.addAttribute("activePage", "support");
|
| 201 |
+
model.addAttribute("pageTitle", "Support - Moon Blight");
|
| 202 |
+
model.addAttribute("faqs", faqService.getFaqsDeletedFalse());
|
| 203 |
+
model.addAttribute("categories", ContactCategory.values());
|
| 204 |
+
model.addAttribute("platforms", ContactPlatform.values());
|
| 205 |
+
}
|
| 206 |
+
}
|
src/main/java/com/darkfantasy/controller/StoryController.java
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import org.springframework.data.domain.Pageable;
|
| 4 |
+
import org.springframework.data.web.PageableDefault;
|
| 5 |
+
import org.springframework.stereotype.Controller;
|
| 6 |
+
import org.springframework.ui.Model;
|
| 7 |
+
import org.springframework.validation.BindingResult;
|
| 8 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 9 |
+
import org.springframework.web.bind.annotation.ModelAttribute;
|
| 10 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
| 11 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 12 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 13 |
+
import org.springframework.web.bind.annotation.RequestParam;
|
| 14 |
+
import org.springframework.web.multipart.MultipartFile;
|
| 15 |
+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
| 16 |
+
|
| 17 |
+
import com.darkfantasy.constant.CmsMenu;
|
| 18 |
+
import com.darkfantasy.constant.Routes;
|
| 19 |
+
import com.darkfantasy.dto.story.CreateStoryRequest;
|
| 20 |
+
import com.darkfantasy.dto.story.StoryResponse;
|
| 21 |
+
import com.darkfantasy.dto.story.UpdateStoryRequest;
|
| 22 |
+
import com.darkfantasy.service.FileStorageService;
|
| 23 |
+
import com.darkfantasy.service.StoryService;
|
| 24 |
+
|
| 25 |
+
import jakarta.validation.Valid;
|
| 26 |
+
import lombok.RequiredArgsConstructor;
|
| 27 |
+
|
| 28 |
+
@Controller
|
| 29 |
+
@RequiredArgsConstructor
|
| 30 |
+
@RequestMapping(Routes.STORY + "/")
|
| 31 |
+
public class StoryController {
|
| 32 |
+
private final StoryService storyService;
|
| 33 |
+
private final FileStorageService fileStorageService;
|
| 34 |
+
|
| 35 |
+
@GetMapping({ "list", "list/" })
|
| 36 |
+
public String toList(
|
| 37 |
+
@PageableDefault(size = 10) Pageable pageable,
|
| 38 |
+
Model model) {
|
| 39 |
+
|
| 40 |
+
model.addAttribute(
|
| 41 |
+
"stories",
|
| 42 |
+
storyService.getStories(pageable));
|
| 43 |
+
model.addAttribute("activeMenu", CmsMenu.STORY);
|
| 44 |
+
return "cms/story/story-list";
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
@GetMapping({ "create", "create/" })
|
| 48 |
+
public String toCreatePage(Model model) {
|
| 49 |
+
model.addAttribute("createStoryRequest", new CreateStoryRequest());
|
| 50 |
+
return "cms/story/story-create";
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
@PostMapping({ "create", "create/" })
|
| 54 |
+
public String createStory(
|
| 55 |
+
@Valid @ModelAttribute("createStoryRequest") CreateStoryRequest request,
|
| 56 |
+
BindingResult result,
|
| 57 |
+
@RequestParam(value = "imageFile", required = false) MultipartFile imageFile,
|
| 58 |
+
Model model,
|
| 59 |
+
RedirectAttributes redirectAttributes) {
|
| 60 |
+
|
| 61 |
+
if (result.hasErrors()) {
|
| 62 |
+
return "cms/story/story-create";
|
| 63 |
+
|
| 64 |
+
}
|
| 65 |
+
try {
|
| 66 |
+
if (imageFile != null && !imageFile.isEmpty()) {
|
| 67 |
+
request.setImage(fileStorageService.saveFile(imageFile, "stories"));
|
| 68 |
+
}
|
| 69 |
+
StoryResponse response = storyService.createStory(request);
|
| 70 |
+
|
| 71 |
+
redirectAttributes.addFlashAttribute(
|
| 72 |
+
"successMessage",
|
| 73 |
+
"Tạo câu chuyện thành công: "
|
| 74 |
+
+ response.getTitle());
|
| 75 |
+
return "redirect:" + Routes.STORY + "/list";
|
| 76 |
+
} catch (Exception e) {
|
| 77 |
+
model.addAttribute(
|
| 78 |
+
"errorMessage",
|
| 79 |
+
e.getMessage());
|
| 80 |
+
|
| 81 |
+
return "cms/story/story-create";
|
| 82 |
+
}
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
@GetMapping({ "{id}/edit", "{id}/edit/" })
|
| 86 |
+
public String toEditPage(@PathVariable("id") Long id, Model model) {
|
| 87 |
+
StoryResponse story = storyService.getStoryById(id);
|
| 88 |
+
|
| 89 |
+
model.addAttribute("story", story);
|
| 90 |
+
return "cms/story/story-edit";
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
@PostMapping("{id}/edit")
|
| 94 |
+
public String editStory(@PathVariable Long id,
|
| 95 |
+
@Valid @ModelAttribute("story") UpdateStoryRequest request,
|
| 96 |
+
BindingResult result,
|
| 97 |
+
@RequestParam(value = "imageFile", required = false) MultipartFile imageFile,
|
| 98 |
+
Model model) {
|
| 99 |
+
if (result.hasErrors()) {
|
| 100 |
+
StoryResponse oldStory = storyService.getStoryById(id);
|
| 101 |
+
|
| 102 |
+
request.setImage(
|
| 103 |
+
oldStory.getImage());
|
| 104 |
+
|
| 105 |
+
return "cms/story/story-edit";
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
request.setId(id);
|
| 109 |
+
|
| 110 |
+
try {
|
| 111 |
+
StoryResponse oldStory = storyService.getStoryById(id);
|
| 112 |
+
|
| 113 |
+
if (imageFile != null && !imageFile.isEmpty()) {
|
| 114 |
+
|
| 115 |
+
request.setImage(
|
| 116 |
+
fileStorageService.saveFile(
|
| 117 |
+
imageFile,
|
| 118 |
+
"stories"));
|
| 119 |
+
|
| 120 |
+
} else {
|
| 121 |
+
request.setImage(
|
| 122 |
+
oldStory.getImage());
|
| 123 |
+
}
|
| 124 |
+
storyService.updateStory(request);
|
| 125 |
+
|
| 126 |
+
return "redirect:" + Routes.STORY + "/list";
|
| 127 |
+
} catch (Exception e) {
|
| 128 |
+
model.addAttribute(
|
| 129 |
+
"errorMessage",
|
| 130 |
+
e.getMessage());
|
| 131 |
+
|
| 132 |
+
return "cms/story/story-edit";
|
| 133 |
+
}
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
@PostMapping("{id}/delete")
|
| 137 |
+
public String deleteStory(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
|
| 138 |
+
try {
|
| 139 |
+
storyService.deleteStory(id);
|
| 140 |
+
|
| 141 |
+
redirectAttributes.addFlashAttribute(
|
| 142 |
+
"successMessage",
|
| 143 |
+
"Xóa câu chuyện thành công");
|
| 144 |
+
|
| 145 |
+
} catch (Exception e) {
|
| 146 |
+
redirectAttributes.addFlashAttribute(
|
| 147 |
+
"errorMessage",
|
| 148 |
+
e.getMessage());
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
return "redirect:" + Routes.STORY + "/list";
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
@PostMapping("{id}/restore")
|
| 155 |
+
public String restoreStory(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
|
| 156 |
+
try {
|
| 157 |
+
storyService.restoreStory(id);
|
| 158 |
+
|
| 159 |
+
redirectAttributes.addFlashAttribute(
|
| 160 |
+
"successMessage",
|
| 161 |
+
"Khôi phục câu chuyện thành công");
|
| 162 |
+
|
| 163 |
+
} catch (Exception e) {
|
| 164 |
+
redirectAttributes.addFlashAttribute(
|
| 165 |
+
"errorMessage",
|
| 166 |
+
e.getMessage());
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
return "redirect:" + Routes.STORY + "/list";
|
| 170 |
+
}
|
| 171 |
+
}
|
src/main/java/com/darkfantasy/controller/UserController.java
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import org.springframework.security.core.Authentication;
|
| 4 |
+
import org.springframework.security.core.AuthenticationException;
|
| 5 |
+
import org.springframework.security.core.context.SecurityContextHolder;
|
| 6 |
+
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
| 7 |
+
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
| 8 |
+
import org.springframework.stereotype.Controller;
|
| 9 |
+
import org.springframework.ui.Model;
|
| 10 |
+
import org.springframework.validation.BindingResult;
|
| 11 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 12 |
+
import org.springframework.web.bind.annotation.ModelAttribute;
|
| 13 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 14 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 15 |
+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
| 16 |
+
|
| 17 |
+
import com.darkfantasy.constant.Routes;
|
| 18 |
+
import com.darkfantasy.dto.user.ChangePasswordRequest;
|
| 19 |
+
import com.darkfantasy.dto.user.ForgotPasswordRequest;
|
| 20 |
+
import com.darkfantasy.dto.user.LoginRequest;
|
| 21 |
+
import com.darkfantasy.dto.user.RegisterRequest;
|
| 22 |
+
import com.darkfantasy.dto.user.ResetPasswordRequest;
|
| 23 |
+
import com.darkfantasy.dto.user.UserResponse;
|
| 24 |
+
import com.darkfantasy.dto.user.VerifyOtpRequest;
|
| 25 |
+
import com.darkfantasy.entity.enums.LogAction;
|
| 26 |
+
import com.darkfantasy.entity.enums.LogEntityType;
|
| 27 |
+
import com.darkfantasy.service.AuditLogService;
|
| 28 |
+
import com.darkfantasy.service.UserService;
|
| 29 |
+
|
| 30 |
+
import jakarta.servlet.http.HttpServletRequest;
|
| 31 |
+
import jakarta.servlet.http.HttpServletResponse;
|
| 32 |
+
import jakarta.servlet.http.HttpSession;
|
| 33 |
+
import jakarta.validation.Valid;
|
| 34 |
+
import lombok.RequiredArgsConstructor;
|
| 35 |
+
|
| 36 |
+
@Controller
|
| 37 |
+
@RequiredArgsConstructor
|
| 38 |
+
@RequestMapping(Routes.USER + "/")
|
| 39 |
+
public class UserController {
|
| 40 |
+
private final UserService userService;
|
| 41 |
+
private final AuditLogService auditLogService;
|
| 42 |
+
|
| 43 |
+
@GetMapping({ "login", "login/" })
|
| 44 |
+
public String toLoginPage(Model model) {
|
| 45 |
+
model.addAttribute("loginRequest", new LoginRequest());
|
| 46 |
+
return "cms/auth/login";
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
@GetMapping({ "register", "register/" })
|
| 50 |
+
public String toRegisterPage(Model model) {
|
| 51 |
+
model.addAttribute("registerRequest", new RegisterRequest());
|
| 52 |
+
return "cms/auth/register";
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
@GetMapping({ "reset", "reset/" })
|
| 56 |
+
public String toResetPage() {
|
| 57 |
+
return "cms/auth/reset";
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
@GetMapping({ "change", "change/" })
|
| 61 |
+
public String toChangePasswordPage() {
|
| 62 |
+
return "cms/auth/password-change";
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
@PostMapping("login")
|
| 66 |
+
public String login(
|
| 67 |
+
@Valid @ModelAttribute LoginRequest request,
|
| 68 |
+
BindingResult result,
|
| 69 |
+
Model model,
|
| 70 |
+
HttpServletRequest req) {
|
| 71 |
+
|
| 72 |
+
if (result.hasErrors()) {
|
| 73 |
+
return "cms/auth/login";
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
try {
|
| 77 |
+
|
| 78 |
+
UserResponse user = userService.login(request);
|
| 79 |
+
|
| 80 |
+
req.getSession(true).setAttribute(
|
| 81 |
+
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
|
| 82 |
+
SecurityContextHolder.getContext());
|
| 83 |
+
return switch (user.getRole()) {
|
| 84 |
+
case "ADMIN" -> "redirect:" + Routes.ADMIN + "/dashboard";
|
| 85 |
+
case "STAFF" -> "redirect:" + Routes.DASHBOARD;
|
| 86 |
+
default -> "redirect:/";
|
| 87 |
+
};
|
| 88 |
+
|
| 89 |
+
} catch (AuthenticationException e) {
|
| 90 |
+
|
| 91 |
+
model.addAttribute(
|
| 92 |
+
"errorMessage",
|
| 93 |
+
"Tên đăng nhập hoặc mật khẩu không đúng.");
|
| 94 |
+
|
| 95 |
+
return "cms/auth/login";
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
@PostMapping("logout")
|
| 100 |
+
public String logout(
|
| 101 |
+
HttpServletRequest request,
|
| 102 |
+
HttpServletResponse response,
|
| 103 |
+
Authentication authentication) {
|
| 104 |
+
if (authentication != null) {
|
| 105 |
+
String username = authentication.getName();
|
| 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()
|
| 115 |
+
.logout(request, response, authentication);
|
| 116 |
+
|
| 117 |
+
return "redirect:" + Routes.USER + "/login";
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
@PostMapping("register")
|
| 121 |
+
public String registerAccount(@Valid @ModelAttribute RegisterRequest request,
|
| 122 |
+
BindingResult result,
|
| 123 |
+
Model model,
|
| 124 |
+
RedirectAttributes redirectAttributes) {
|
| 125 |
+
if (result.hasErrors()) {
|
| 126 |
+
return "cms/auth/register";
|
| 127 |
+
}
|
| 128 |
+
try {
|
| 129 |
+
userService.register(request);
|
| 130 |
+
redirectAttributes.addFlashAttribute("successMessage", "Đăng ký thành công! Chờ admin xác nhận");
|
| 131 |
+
return "redirect:" + Routes.USER + "/register";
|
| 132 |
+
} catch (IllegalArgumentException e) {
|
| 133 |
+
request.setPassword("");
|
| 134 |
+
request.setRePassword("");
|
| 135 |
+
model.addAttribute("errorMessage", e.getMessage());
|
| 136 |
+
return "cms/auth/register";
|
| 137 |
+
}
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
@GetMapping("change-password")
|
| 141 |
+
public String changePasswordPage(
|
| 142 |
+
Model model) {
|
| 143 |
+
|
| 144 |
+
model.addAttribute(
|
| 145 |
+
"changePasswordRequest",
|
| 146 |
+
new ChangePasswordRequest());
|
| 147 |
+
return "cms/auth/change-password";
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
@PostMapping("change-password")
|
| 151 |
+
public String changePassword(
|
| 152 |
+
@Valid @ModelAttribute("changePasswordRequest") ChangePasswordRequest request,
|
| 153 |
+
BindingResult result,
|
| 154 |
+
Model model,
|
| 155 |
+
RedirectAttributes redirectAttributes) {
|
| 156 |
+
|
| 157 |
+
if (result.hasErrors()) {
|
| 158 |
+
return "cms/auth/change-password";
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
try {
|
| 162 |
+
|
| 163 |
+
userService.changeCurrentUserPassword(request);
|
| 164 |
+
|
| 165 |
+
redirectAttributes.addFlashAttribute(
|
| 166 |
+
"successMessage",
|
| 167 |
+
"Đổi mật khẩu thành công");
|
| 168 |
+
|
| 169 |
+
return "redirect:" + Routes.USER + "/change-password";
|
| 170 |
+
|
| 171 |
+
} catch (Exception e) {
|
| 172 |
+
|
| 173 |
+
model.addAttribute(
|
| 174 |
+
"errorMessage",
|
| 175 |
+
e.getMessage());
|
| 176 |
+
|
| 177 |
+
return "cms/auth/change-password";
|
| 178 |
+
}
|
| 179 |
+
}
|
| 180 |
+
// @PostMapping("reset/enter")
|
| 181 |
+
// public String resetPassword(@Valid @ModelAttribute ResetPasswordRequest
|
| 182 |
+
// request, BindingResult result,
|
| 183 |
+
// Model model, RedirectAttributes redirectAttributes) {
|
| 184 |
+
// if (result.hasErrors()) {
|
| 185 |
+
// return "reset";
|
| 186 |
+
// }
|
| 187 |
+
// try {
|
| 188 |
+
// userService.changeForgottenPassword(request);
|
| 189 |
+
// redirectAttributes.addAttribute("successMessage", "Thay đổi mật khẩu thành
|
| 190 |
+
// công!");
|
| 191 |
+
// return "redirect:/user/login";
|
| 192 |
+
// } catch (IllegalArgumentException e) {
|
| 193 |
+
// model.addAttribute("errorMessage", e.getMessage());
|
| 194 |
+
// return "reset";
|
| 195 |
+
// }
|
| 196 |
+
|
| 197 |
+
// }
|
| 198 |
+
@GetMapping("forgot-password")
|
| 199 |
+
public String forgotPasswordPage(Model model) {
|
| 200 |
+
model.addAttribute("forgotPasswordRequest", new ForgotPasswordRequest());
|
| 201 |
+
return "cms/auth/forgot-password";
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
@PostMapping("forgot-password")
|
| 205 |
+
public String forgotPassword(
|
| 206 |
+
@Valid @ModelAttribute ForgotPasswordRequest request,
|
| 207 |
+
BindingResult result, HttpSession session,
|
| 208 |
+
Model model) {
|
| 209 |
+
if (result.hasErrors()) {
|
| 210 |
+
return "cms/auth/forgot-password";
|
| 211 |
+
}
|
| 212 |
+
try {
|
| 213 |
+
userService.sendOtp(request);
|
| 214 |
+
session.setAttribute("OTP_EMAIL", request.getEmail());
|
| 215 |
+
return "redirect:" + Routes.USER + "/verify-otp";
|
| 216 |
+
} catch (Exception e) {
|
| 217 |
+
model.addAttribute("errorMessage", e.getMessage());
|
| 218 |
+
return "cms/auth/forgot-password";
|
| 219 |
+
}
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
@GetMapping("verify-otp")
|
| 223 |
+
public String verifyOtpPage(HttpSession session, Model model) {
|
| 224 |
+
VerifyOtpRequest request = new VerifyOtpRequest();
|
| 225 |
+
request.setEmail((String) session.getAttribute("OTP_EMAIL"));
|
| 226 |
+
|
| 227 |
+
model.addAttribute("verifyOtpRequest", request);
|
| 228 |
+
return "cms/auth/verify-otp";
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
@PostMapping("verify-otp")
|
| 232 |
+
public String verifyOtp(
|
| 233 |
+
@Valid @ModelAttribute VerifyOtpRequest request,
|
| 234 |
+
BindingResult result,
|
| 235 |
+
HttpSession session,
|
| 236 |
+
Model model) {
|
| 237 |
+
|
| 238 |
+
if (result.hasErrors()) {
|
| 239 |
+
return "cms/auth/verify-otp";
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
try {
|
| 243 |
+
|
| 244 |
+
userService.verifyOtp(request);
|
| 245 |
+
session.setAttribute(
|
| 246 |
+
"RESET_OTP",
|
| 247 |
+
request.getOtp());
|
| 248 |
+
session.setAttribute(
|
| 249 |
+
"RESET_EMAIL",
|
| 250 |
+
request.getEmail());
|
| 251 |
+
return "redirect:" + Routes.USER + "/reset-password";
|
| 252 |
+
|
| 253 |
+
} catch (Exception e) {
|
| 254 |
+
model.addAttribute(
|
| 255 |
+
"errorMessage", e.getMessage());
|
| 256 |
+
|
| 257 |
+
return "cms/auth/verify-otp";
|
| 258 |
+
}
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
@GetMapping("/reset-password")
|
| 262 |
+
public String resetPasswordPage(
|
| 263 |
+
HttpSession session,
|
| 264 |
+
Model model) {
|
| 265 |
+
ResetPasswordRequest request = new ResetPasswordRequest();
|
| 266 |
+
request.setEmail(
|
| 267 |
+
(String) session.getAttribute("RESET_EMAIL"));
|
| 268 |
+
|
| 269 |
+
request.setOtp(
|
| 270 |
+
(String) session.getAttribute("RESET_OTP"));
|
| 271 |
+
|
| 272 |
+
model.addAttribute("resetPasswordRequest", request);
|
| 273 |
+
if (request.getEmail() == null || request.getOtp() == null) {
|
| 274 |
+
return "redirect:" + Routes.USER + "/forgot-password";
|
| 275 |
+
}
|
| 276 |
+
return "cms/auth/reset-password";
|
| 277 |
+
}
|
| 278 |
+
|
| 279 |
+
@PostMapping("/reset-password")
|
| 280 |
+
public String resetPassword(
|
| 281 |
+
@Valid @ModelAttribute ResetPasswordRequest request,
|
| 282 |
+
BindingResult result,
|
| 283 |
+
HttpSession session,
|
| 284 |
+
Model model,
|
| 285 |
+
RedirectAttributes redirectAttributes) {
|
| 286 |
+
if (result.hasErrors()) {
|
| 287 |
+
return "cms/auth/reset-password";
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
try {
|
| 291 |
+
String email = (String) session.getAttribute("RESET_EMAIL");
|
| 292 |
+
String otp = (String) session.getAttribute("RESET_OTP");
|
| 293 |
+
if (email == null || otp == null) {
|
| 294 |
+
return "redirect:" + Routes.USER + "/forgot-password";
|
| 295 |
+
}
|
| 296 |
+
request.setEmail(email);
|
| 297 |
+
request.setOtp(otp);
|
| 298 |
+
userService.resetPassword(request);
|
| 299 |
+
|
| 300 |
+
session.removeAttribute(
|
| 301 |
+
"RESET_EMAIL");
|
| 302 |
+
|
| 303 |
+
session.removeAttribute(
|
| 304 |
+
"RESET_OTP");
|
| 305 |
+
|
| 306 |
+
redirectAttributes.addFlashAttribute(
|
| 307 |
+
"successMessage",
|
| 308 |
+
"Đổi mật khẩu thành công");
|
| 309 |
+
|
| 310 |
+
return "redirect:" + Routes.USER + "/login";
|
| 311 |
+
|
| 312 |
+
} catch (Exception e) {
|
| 313 |
+
e.printStackTrace();
|
| 314 |
+
model.addAttribute(
|
| 315 |
+
"errorMessage",
|
| 316 |
+
e.getMessage());
|
| 317 |
+
|
| 318 |
+
return "cms/auth/reset-password";
|
| 319 |
+
}
|
| 320 |
+
}
|
| 321 |
+
}
|
src/main/java/com/darkfantasy/controller/WorldController.java
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.controller;
|
| 2 |
+
|
| 3 |
+
import org.springframework.data.domain.Pageable;
|
| 4 |
+
import org.springframework.data.web.PageableDefault;
|
| 5 |
+
import org.springframework.stereotype.Controller;
|
| 6 |
+
import org.springframework.ui.Model;
|
| 7 |
+
import org.springframework.validation.BindingResult;
|
| 8 |
+
import org.springframework.web.bind.annotation.RequestMapping;
|
| 9 |
+
import org.springframework.web.bind.annotation.RequestParam;
|
| 10 |
+
import org.springframework.web.multipart.MultipartFile;
|
| 11 |
+
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
| 12 |
+
|
| 13 |
+
import com.darkfantasy.constant.CmsMenu;
|
| 14 |
+
import com.darkfantasy.constant.Routes;
|
| 15 |
+
import com.darkfantasy.dto.world.CreateWorldRequest;
|
| 16 |
+
import com.darkfantasy.dto.world.UpdateWorldRequest;
|
| 17 |
+
import com.darkfantasy.dto.world.WorldResponse;
|
| 18 |
+
import com.darkfantasy.service.FileStorageService;
|
| 19 |
+
import com.darkfantasy.service.WorldService;
|
| 20 |
+
|
| 21 |
+
import jakarta.validation.Valid;
|
| 22 |
+
import lombok.RequiredArgsConstructor;
|
| 23 |
+
import org.springframework.web.bind.annotation.GetMapping;
|
| 24 |
+
import org.springframework.web.bind.annotation.ModelAttribute;
|
| 25 |
+
import org.springframework.web.bind.annotation.PathVariable;
|
| 26 |
+
import org.springframework.web.bind.annotation.PostMapping;
|
| 27 |
+
|
| 28 |
+
@Controller
|
| 29 |
+
@RequiredArgsConstructor
|
| 30 |
+
@RequestMapping(Routes.WORLD + "/")
|
| 31 |
+
public class WorldController {
|
| 32 |
+
private final WorldService worldService;
|
| 33 |
+
private final FileStorageService fileStorageService;
|
| 34 |
+
|
| 35 |
+
@GetMapping({ "list", "list/" })
|
| 36 |
+
public String toList(
|
| 37 |
+
@PageableDefault(size = 10) Pageable pageable,
|
| 38 |
+
Model model) {
|
| 39 |
+
|
| 40 |
+
model.addAttribute(
|
| 41 |
+
"worlds",
|
| 42 |
+
worldService.getWorlds(pageable));
|
| 43 |
+
model.addAttribute("activeMenu", CmsMenu.WORLD);
|
| 44 |
+
return "cms/world/world-list";
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
@GetMapping({ "create", "create/" })
|
| 48 |
+
public String toCreatePage(Model model) {
|
| 49 |
+
model.addAttribute("createWorldRequest", new CreateWorldRequest());
|
| 50 |
+
return "cms/world/world-create";
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
@PostMapping({ "create", "create/" })
|
| 54 |
+
public String createWorld(
|
| 55 |
+
@Valid @ModelAttribute("createWorldRequest") CreateWorldRequest request,
|
| 56 |
+
BindingResult result,
|
| 57 |
+
@RequestParam(value = "imageFile", required = false) MultipartFile imageFile,
|
| 58 |
+
Model model,
|
| 59 |
+
RedirectAttributes redirectAttributes) {
|
| 60 |
+
|
| 61 |
+
if (result.hasErrors()) {
|
| 62 |
+
model.addAttribute("errorMessage", "Vui lòng kiểm tra lại thông tin thế giới.");
|
| 63 |
+
return "cms/world/world-create";
|
| 64 |
+
|
| 65 |
+
}
|
| 66 |
+
try {
|
| 67 |
+
if (imageFile != null && !imageFile.isEmpty()) {
|
| 68 |
+
request.setImage(fileStorageService.saveFile(imageFile, "worlds"));
|
| 69 |
+
}
|
| 70 |
+
WorldResponse response = worldService.createWorld(request);
|
| 71 |
+
|
| 72 |
+
redirectAttributes.addFlashAttribute(
|
| 73 |
+
"successMessage",
|
| 74 |
+
"Tạo thế giới thành công: "
|
| 75 |
+
+ response.getTitle());
|
| 76 |
+
return "redirect:" + Routes.WORLD + "/list";
|
| 77 |
+
} catch (Exception e) {
|
| 78 |
+
model.addAttribute(
|
| 79 |
+
"errorMessage",
|
| 80 |
+
e.getMessage());
|
| 81 |
+
|
| 82 |
+
return "cms/world/world-create";
|
| 83 |
+
}
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
@GetMapping({ "{id}/edit", "{id}/edit/" })
|
| 87 |
+
public String toEditPage(@PathVariable("id") Long id, Model model) {
|
| 88 |
+
WorldResponse world = worldService.getWorldById(id);
|
| 89 |
+
model.addAttribute("world", world);
|
| 90 |
+
return "cms/world/world-edit";
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
@PostMapping("{id}/edit")
|
| 94 |
+
public String editWorld(@PathVariable Long id,
|
| 95 |
+
@Valid @ModelAttribute("world") UpdateWorldRequest request,
|
| 96 |
+
BindingResult result,
|
| 97 |
+
@RequestParam(value = "imageFile", required = false) MultipartFile imageFile,
|
| 98 |
+
Model model) {
|
| 99 |
+
if (result.hasErrors()) {
|
| 100 |
+
WorldResponse oldWorld = worldService.getWorldById(id);
|
| 101 |
+
|
| 102 |
+
request.setImage(
|
| 103 |
+
oldWorld.getImage());
|
| 104 |
+
|
| 105 |
+
return "cms/world/world-edit";
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
request.setId(id);
|
| 109 |
+
|
| 110 |
+
try {
|
| 111 |
+
if (imageFile != null && !imageFile.isEmpty()) {
|
| 112 |
+
request.setImage(
|
| 113 |
+
fileStorageService.saveFile(imageFile, "worlds"));
|
| 114 |
+
}
|
| 115 |
+
worldService.updateWorld(request);
|
| 116 |
+
|
| 117 |
+
return "redirect:" + Routes.WORLD + "/list";
|
| 118 |
+
} catch (Exception e) {
|
| 119 |
+
model.addAttribute(
|
| 120 |
+
"errorMessage",
|
| 121 |
+
e.getMessage());
|
| 122 |
+
|
| 123 |
+
return "cms/world/world-edit";
|
| 124 |
+
}
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
@PostMapping("{id}/delete")
|
| 128 |
+
public String deleteWorld(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
|
| 129 |
+
try {
|
| 130 |
+
worldService.deleteWorld(id);
|
| 131 |
+
|
| 132 |
+
redirectAttributes.addFlashAttribute(
|
| 133 |
+
"successMessage",
|
| 134 |
+
"Xóa thế giới thành công");
|
| 135 |
+
|
| 136 |
+
} catch (Exception e) {
|
| 137 |
+
redirectAttributes.addFlashAttribute(
|
| 138 |
+
"errorMessage",
|
| 139 |
+
e.getMessage());
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
return "redirect:" + Routes.WORLD + "/list";
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
@PostMapping("{id}/restore")
|
| 146 |
+
public String restoreWorld(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
|
| 147 |
+
try {
|
| 148 |
+
worldService.restoreWorld(id);
|
| 149 |
+
|
| 150 |
+
redirectAttributes.addFlashAttribute(
|
| 151 |
+
"successMessage",
|
| 152 |
+
"Khôi phục thế giới thành công");
|
| 153 |
+
|
| 154 |
+
} catch (Exception e) {
|
| 155 |
+
redirectAttributes.addFlashAttribute(
|
| 156 |
+
"errorMessage",
|
| 157 |
+
e.getMessage());
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
return "redirect:" + Routes.WORLD + "/list";
|
| 161 |
+
}
|
| 162 |
+
}
|
src/main/java/com/darkfantasy/dto/article/ArticleResponse.java
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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;
|
| 9 |
+
import lombok.Builder;
|
| 10 |
+
import lombok.Data;
|
| 11 |
+
import lombok.NoArgsConstructor;
|
| 12 |
+
|
| 13 |
+
@Data
|
| 14 |
+
@NoArgsConstructor
|
| 15 |
+
@AllArgsConstructor
|
| 16 |
+
@Builder
|
| 17 |
+
|
| 18 |
+
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;
|
| 25 |
+
private String createdByUserName;
|
| 26 |
+
private String createdAt;
|
| 27 |
+
private String updatedByUserName;
|
| 28 |
+
private String updatedAt;
|
| 29 |
+
private boolean deleted;
|
| 30 |
+
|
| 31 |
+
public static ArticleResponse fromEntity(Article article) {
|
| 32 |
+
return ArticleResponse.builder()
|
| 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 |
+
public String getSummary() {
|
| 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 |
+
}
|
src/main/java/com/darkfantasy/dto/article/CreateArticleRequest.java
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.article;
|
| 2 |
+
|
| 3 |
+
import com.darkfantasy.entity.Article;
|
| 4 |
+
import com.darkfantasy.entity.enums.ArticleType;
|
| 5 |
+
|
| 6 |
+
import jakarta.validation.constraints.NotBlank;
|
| 7 |
+
import jakarta.validation.constraints.NotNull;
|
| 8 |
+
import jakarta.validation.constraints.Size;
|
| 9 |
+
import lombok.AllArgsConstructor;
|
| 10 |
+
import lombok.Builder;
|
| 11 |
+
import lombok.Data;
|
| 12 |
+
import lombok.NoArgsConstructor;
|
| 13 |
+
|
| 14 |
+
@Data
|
| 15 |
+
@NoArgsConstructor
|
| 16 |
+
@AllArgsConstructor
|
| 17 |
+
@Builder
|
| 18 |
+
public class CreateArticleRequest {
|
| 19 |
+
|
| 20 |
+
@NotBlank(message = "Tiêu đề không được để trống")
|
| 21 |
+
@Size(max = 255, message = "Tiêu đề quá dài")
|
| 22 |
+
private String title;
|
| 23 |
+
|
| 24 |
+
@NotBlank(message = "Nội dung không được để trống")
|
| 25 |
+
private String content;
|
| 26 |
+
|
| 27 |
+
private String thumbnailUrl;
|
| 28 |
+
|
| 29 |
+
@NotNull(message = "Loại bài viết không được để trống")
|
| 30 |
+
private ArticleType type;
|
| 31 |
+
|
| 32 |
+
public Article toEntity() {
|
| 33 |
+
return Article.builder()
|
| 34 |
+
.title(this.title)
|
| 35 |
+
.content(this.content)
|
| 36 |
+
.thumbnailUrl(this.thumbnailUrl)
|
| 37 |
+
.type(this.type)
|
| 38 |
+
.build();
|
| 39 |
+
}
|
| 40 |
+
}
|
src/main/java/com/darkfantasy/dto/article/UpdateArticleRequest.java
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.article;
|
| 2 |
+
|
| 3 |
+
import com.darkfantasy.entity.enums.ArticleType;
|
| 4 |
+
|
| 5 |
+
import jakarta.validation.constraints.NotBlank;
|
| 6 |
+
import jakarta.validation.constraints.NotNull;
|
| 7 |
+
import jakarta.validation.constraints.Size;
|
| 8 |
+
import lombok.AllArgsConstructor;
|
| 9 |
+
import lombok.Builder;
|
| 10 |
+
import lombok.Data;
|
| 11 |
+
import lombok.NoArgsConstructor;
|
| 12 |
+
|
| 13 |
+
@Data
|
| 14 |
+
@NoArgsConstructor
|
| 15 |
+
@AllArgsConstructor
|
| 16 |
+
@Builder
|
| 17 |
+
public class UpdateArticleRequest {
|
| 18 |
+
@NotNull
|
| 19 |
+
private Long id;
|
| 20 |
+
@NotBlank(message = "Tiêu đề không được để trống")
|
| 21 |
+
@Size(max = 255)
|
| 22 |
+
private String title;
|
| 23 |
+
|
| 24 |
+
@NotBlank(message = "Nội dung không được để trống")
|
| 25 |
+
private String content;
|
| 26 |
+
|
| 27 |
+
private String thumbnailUrl;
|
| 28 |
+
|
| 29 |
+
@NotNull(message = "Loại bài viết không được để trống")
|
| 30 |
+
private ArticleType type;
|
| 31 |
+
|
| 32 |
+
}
|
src/main/java/com/darkfantasy/dto/contact/ContactMessageResponse.java
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.contact;
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
import com.darkfantasy.entity.ContactMessage;
|
| 5 |
+
import com.darkfantasy.entity.enums.ContactCategory;
|
| 6 |
+
import com.darkfantasy.entity.enums.ContactPlatform;
|
| 7 |
+
|
| 8 |
+
import lombok.AllArgsConstructor;
|
| 9 |
+
import lombok.Builder;
|
| 10 |
+
import lombok.Data;
|
| 11 |
+
import lombok.NoArgsConstructor;
|
| 12 |
+
|
| 13 |
+
@Data
|
| 14 |
+
@Builder
|
| 15 |
+
@NoArgsConstructor
|
| 16 |
+
@AllArgsConstructor
|
| 17 |
+
public class ContactMessageResponse {
|
| 18 |
+
private Long id;
|
| 19 |
+
private String visitorName;
|
| 20 |
+
|
| 21 |
+
private String visitorEmail;
|
| 22 |
+
|
| 23 |
+
private ContactCategory category;
|
| 24 |
+
|
| 25 |
+
private ContactPlatform platform;
|
| 26 |
+
|
| 27 |
+
private String message;
|
| 28 |
+
|
| 29 |
+
private boolean processed;
|
| 30 |
+
|
| 31 |
+
public static ContactMessageResponse fromEntity(ContactMessage message) {
|
| 32 |
+
return ContactMessageResponse.builder()
|
| 33 |
+
.id(message.getId())
|
| 34 |
+
.visitorName(message.getVisitorName())
|
| 35 |
+
.visitorEmail(message.getVisitorEmail())
|
| 36 |
+
.category(message.getCategory())
|
| 37 |
+
.platform(message.getPlatform())
|
| 38 |
+
.message(message.getMessage())
|
| 39 |
+
.processed(message.isProcessed())
|
| 40 |
+
.build();
|
| 41 |
+
}
|
| 42 |
+
}
|
src/main/java/com/darkfantasy/dto/contact/CreateContactMessageRequest.java
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.contact;
|
| 2 |
+
|
| 3 |
+
import com.darkfantasy.entity.ContactMessage;
|
| 4 |
+
import com.darkfantasy.entity.enums.ContactCategory;
|
| 5 |
+
import com.darkfantasy.entity.enums.ContactPlatform;
|
| 6 |
+
|
| 7 |
+
import jakarta.validation.constraints.Email;
|
| 8 |
+
import jakarta.validation.constraints.NotBlank;
|
| 9 |
+
import jakarta.validation.constraints.NotNull;
|
| 10 |
+
import jakarta.validation.constraints.Size;
|
| 11 |
+
import lombok.AllArgsConstructor;
|
| 12 |
+
import lombok.Builder;
|
| 13 |
+
import lombok.Data;
|
| 14 |
+
import lombok.NoArgsConstructor;
|
| 15 |
+
|
| 16 |
+
@Data
|
| 17 |
+
@NoArgsConstructor
|
| 18 |
+
@AllArgsConstructor
|
| 19 |
+
@Builder
|
| 20 |
+
public class CreateContactMessageRequest {
|
| 21 |
+
|
| 22 |
+
@NotBlank(message = "Vui lòng nhập tên")
|
| 23 |
+
private String visitorName;
|
| 24 |
+
|
| 25 |
+
@NotBlank(message = "Vui lòng nhập email")
|
| 26 |
+
@Email(message = "Email không hợp lệ")
|
| 27 |
+
private String visitorEmail;
|
| 28 |
+
|
| 29 |
+
@NotNull(message = "Vui lòng chọn danh mục")
|
| 30 |
+
private ContactCategory category;
|
| 31 |
+
|
| 32 |
+
@NotNull(message = "Vui lòng chọn nền tảng")
|
| 33 |
+
private ContactPlatform platform;
|
| 34 |
+
|
| 35 |
+
@NotBlank(message = "Vui lòng nhập nội dung")
|
| 36 |
+
@Size(max = 5000)
|
| 37 |
+
private String message;
|
| 38 |
+
|
| 39 |
+
public ContactMessage toEntity() {
|
| 40 |
+
return ContactMessage.builder()
|
| 41 |
+
.visitorName(visitorName)
|
| 42 |
+
.visitorEmail(visitorEmail)
|
| 43 |
+
.category(category)
|
| 44 |
+
.platform(platform)
|
| 45 |
+
.message(message)
|
| 46 |
+
.processed(false)
|
| 47 |
+
.build();
|
| 48 |
+
}
|
| 49 |
+
}
|
src/main/java/com/darkfantasy/dto/contributor/ContributorResponse.java
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.contributor;
|
| 2 |
+
|
| 3 |
+
import com.darkfantasy.entity.Contributor;
|
| 4 |
+
import com.darkfantasy.util.TimeUtil;
|
| 5 |
+
|
| 6 |
+
import lombok.AllArgsConstructor;
|
| 7 |
+
import lombok.Builder;
|
| 8 |
+
import lombok.Data;
|
| 9 |
+
import lombok.NoArgsConstructor;
|
| 10 |
+
|
| 11 |
+
@Data
|
| 12 |
+
@NoArgsConstructor
|
| 13 |
+
@AllArgsConstructor
|
| 14 |
+
@Builder
|
| 15 |
+
public class ContributorResponse {
|
| 16 |
+
private Long id;
|
| 17 |
+
private String name;
|
| 18 |
+
private String position;
|
| 19 |
+
private String avatar;
|
| 20 |
+
private Integer priority;
|
| 21 |
+
private boolean deleted;
|
| 22 |
+
private String createdByUserName;
|
| 23 |
+
private String createdAt;
|
| 24 |
+
private String updatedByUserName;
|
| 25 |
+
private String updatedAt;
|
| 26 |
+
|
| 27 |
+
public static ContributorResponse fromEntity(Contributor contributor) {
|
| 28 |
+
return ContributorResponse.builder()
|
| 29 |
+
.id(contributor.getId())
|
| 30 |
+
.name(contributor.getName())
|
| 31 |
+
.position(contributor.getPosition())
|
| 32 |
+
.avatar(contributor.getAvatar())
|
| 33 |
+
.priority(contributor.getPriority())
|
| 34 |
+
.deleted(contributor.isDeleted())
|
| 35 |
+
.createdByUserName(contributor.getCreatedBy() != null ? contributor.getCreatedBy().getUsername() : null)
|
| 36 |
+
.createdAt(TimeUtil.formatInstant(contributor.getCreatedAt()))
|
| 37 |
+
.updatedByUserName(contributor.getUpdatedBy() != null ? contributor.getUpdatedBy().getUsername() : null)
|
| 38 |
+
.updatedAt(TimeUtil.formatInstant(contributor.getUpdatedAt()))
|
| 39 |
+
.build();
|
| 40 |
+
}
|
| 41 |
+
}
|
src/main/java/com/darkfantasy/dto/contributor/CreateContributorRequest.java
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.contributor;
|
| 2 |
+
|
| 3 |
+
import com.darkfantasy.entity.Article;
|
| 4 |
+
import com.darkfantasy.entity.Contributor;
|
| 5 |
+
|
| 6 |
+
import jakarta.validation.constraints.Max;
|
| 7 |
+
import jakarta.validation.constraints.Min;
|
| 8 |
+
import jakarta.validation.constraints.NotBlank;
|
| 9 |
+
import jakarta.validation.constraints.NotNull;
|
| 10 |
+
import jakarta.validation.constraints.Size;
|
| 11 |
+
import lombok.AllArgsConstructor;
|
| 12 |
+
import lombok.Builder;
|
| 13 |
+
import lombok.Data;
|
| 14 |
+
import lombok.NoArgsConstructor;
|
| 15 |
+
|
| 16 |
+
@Data
|
| 17 |
+
@NoArgsConstructor
|
| 18 |
+
@AllArgsConstructor
|
| 19 |
+
@Builder
|
| 20 |
+
public class CreateContributorRequest {
|
| 21 |
+
@NotBlank(message = "Tên người đóng góp không được để trống")
|
| 22 |
+
@Size(max = 255, message = "Tên người đóng góp không được vượt quá 255 ký tự")
|
| 23 |
+
private String name;
|
| 24 |
+
|
| 25 |
+
@NotBlank(message = "Chức danh không được để trống")
|
| 26 |
+
@Size(max = 255, message = "Chức danh không được vượt quá 255 ký tự")
|
| 27 |
+
private String position;
|
| 28 |
+
|
| 29 |
+
private String avatar;
|
| 30 |
+
|
| 31 |
+
@NotNull(message = "Độ ưu tiên không được để trống")
|
| 32 |
+
@Min(value = 0, message = "Độ ưu tiên phải từ 0 đến 100")
|
| 33 |
+
@Max(value = 100, message = "Độ ưu tiên phải từ 0 đến 100")
|
| 34 |
+
private Integer priority;
|
| 35 |
+
|
| 36 |
+
public Contributor toEntity() {
|
| 37 |
+
return Contributor.builder()
|
| 38 |
+
.name(this.name)
|
| 39 |
+
.position(this.position)
|
| 40 |
+
.avatar(this.avatar)
|
| 41 |
+
.priority(this.priority)
|
| 42 |
+
.build();
|
| 43 |
+
}
|
| 44 |
+
}
|
src/main/java/com/darkfantasy/dto/contributor/UpdateContributorRequest.java
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.contributor;
|
| 2 |
+
|
| 3 |
+
import jakarta.validation.constraints.Max;
|
| 4 |
+
import jakarta.validation.constraints.Min;
|
| 5 |
+
import jakarta.validation.constraints.NotBlank;
|
| 6 |
+
import jakarta.validation.constraints.NotNull;
|
| 7 |
+
import jakarta.validation.constraints.Size;
|
| 8 |
+
import lombok.AllArgsConstructor;
|
| 9 |
+
import lombok.Builder;
|
| 10 |
+
import lombok.Data;
|
| 11 |
+
import lombok.NoArgsConstructor;
|
| 12 |
+
|
| 13 |
+
@Data
|
| 14 |
+
@NoArgsConstructor
|
| 15 |
+
@AllArgsConstructor
|
| 16 |
+
@Builder
|
| 17 |
+
public class UpdateContributorRequest {
|
| 18 |
+
private Long id;
|
| 19 |
+
@NotBlank(message = "Tên người đóng góp không được để trống")
|
| 20 |
+
@Size(max = 255, message = "Tên người đóng góp không được vượt quá 255 ký tự")
|
| 21 |
+
private String name;
|
| 22 |
+
|
| 23 |
+
@NotBlank(message = "Chức danh không được để trống")
|
| 24 |
+
@Size(max = 255, message = "Chức danh không được vượt quá 255 ký tự")
|
| 25 |
+
private String position;
|
| 26 |
+
|
| 27 |
+
private String avatar;
|
| 28 |
+
|
| 29 |
+
@NotNull(message = "Độ ưu tiên không được để trống")
|
| 30 |
+
@Min(value = 0, message = "Độ ưu tiên phải từ 0 đến 100")
|
| 31 |
+
@Max(value = 100, message = "Độ ưu tiên phải từ 0 đến 100")
|
| 32 |
+
private Integer priority;
|
| 33 |
+
|
| 34 |
+
}
|
src/main/java/com/darkfantasy/dto/faq/CreateFaqRequest.java
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.faq;
|
| 2 |
+
|
| 3 |
+
import com.darkfantasy.entity.Faq;
|
| 4 |
+
|
| 5 |
+
import jakarta.validation.constraints.Max;
|
| 6 |
+
import jakarta.validation.constraints.Min;
|
| 7 |
+
import jakarta.validation.constraints.NotBlank;
|
| 8 |
+
import jakarta.validation.constraints.NotNull;
|
| 9 |
+
import jakarta.validation.constraints.Size;
|
| 10 |
+
import lombok.AllArgsConstructor;
|
| 11 |
+
import lombok.Builder;
|
| 12 |
+
import lombok.Data;
|
| 13 |
+
import lombok.NoArgsConstructor;
|
| 14 |
+
|
| 15 |
+
@Data
|
| 16 |
+
@NoArgsConstructor
|
| 17 |
+
@AllArgsConstructor
|
| 18 |
+
@Builder
|
| 19 |
+
public class CreateFaqRequest {
|
| 20 |
+
|
| 21 |
+
@NotBlank(message = "Tiêu đề không được để trống!")
|
| 22 |
+
@Size(max = 255, message = "Tiêu đề không được vượt quá 255 ký tự!")
|
| 23 |
+
private String title;
|
| 24 |
+
@NotBlank(message = "Nội dung không được để trống!")
|
| 25 |
+
private String content;
|
| 26 |
+
|
| 27 |
+
@NotNull(message = "Độ ưu tiên không được để trống!")
|
| 28 |
+
@Min(value = 0, message = "Độ ưu tiên phải từ 0 đến 100!")
|
| 29 |
+
@Max(value = 100, message = "Độ ưu tiên phải từ 0 đến 100!")
|
| 30 |
+
private Integer priority;
|
| 31 |
+
private boolean deleted;
|
| 32 |
+
|
| 33 |
+
public Faq toEntity() {
|
| 34 |
+
return Faq.builder()
|
| 35 |
+
.title(this.title)
|
| 36 |
+
.content(this.content)
|
| 37 |
+
.priority(this.priority)
|
| 38 |
+
.deleted(this.deleted)
|
| 39 |
+
.build();
|
| 40 |
+
}
|
| 41 |
+
}
|
src/main/java/com/darkfantasy/dto/faq/FaqResponse.java
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.faq;
|
| 2 |
+
|
| 3 |
+
import com.darkfantasy.entity.Faq;
|
| 4 |
+
|
| 5 |
+
import lombok.AllArgsConstructor;
|
| 6 |
+
import lombok.Builder;
|
| 7 |
+
import lombok.Data;
|
| 8 |
+
import lombok.NoArgsConstructor;
|
| 9 |
+
|
| 10 |
+
@Data
|
| 11 |
+
@NoArgsConstructor
|
| 12 |
+
@AllArgsConstructor
|
| 13 |
+
@Builder
|
| 14 |
+
public class FaqResponse {
|
| 15 |
+
private Long id;
|
| 16 |
+
private String title;
|
| 17 |
+
private String content;
|
| 18 |
+
private Integer priority;
|
| 19 |
+
private boolean deleted;
|
| 20 |
+
|
| 21 |
+
public static FaqResponse fromEntity(Faq faq) {
|
| 22 |
+
return FaqResponse.builder()
|
| 23 |
+
.id(faq.getId())
|
| 24 |
+
.title(faq.getTitle())
|
| 25 |
+
.content(faq.getContent())
|
| 26 |
+
.priority(faq.getPriority())
|
| 27 |
+
.deleted(faq.isDeleted())
|
| 28 |
+
.build();
|
| 29 |
+
}
|
| 30 |
+
}
|
src/main/java/com/darkfantasy/dto/faq/UpdateFaqRequest.java
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.faq;
|
| 2 |
+
|
| 3 |
+
import jakarta.validation.constraints.Max;
|
| 4 |
+
import jakarta.validation.constraints.Min;
|
| 5 |
+
import jakarta.validation.constraints.NotBlank;
|
| 6 |
+
import jakarta.validation.constraints.NotNull;
|
| 7 |
+
import jakarta.validation.constraints.Size;
|
| 8 |
+
import lombok.AllArgsConstructor;
|
| 9 |
+
import lombok.Builder;
|
| 10 |
+
import lombok.Data;
|
| 11 |
+
import lombok.NoArgsConstructor;
|
| 12 |
+
|
| 13 |
+
@Data
|
| 14 |
+
@NoArgsConstructor
|
| 15 |
+
@AllArgsConstructor
|
| 16 |
+
@Builder
|
| 17 |
+
public class UpdateFaqRequest {
|
| 18 |
+
private Long id;
|
| 19 |
+
|
| 20 |
+
@NotBlank(message = "Tiêu đề không được để trống!")
|
| 21 |
+
@Size(max = 255, message = "Tiêu đề không được vượt quá 255 ký tự!")
|
| 22 |
+
private String title;
|
| 23 |
+
@NotBlank(message = "Nội dung không được để trống!")
|
| 24 |
+
private String content;
|
| 25 |
+
@NotNull(message = "Độ ưu tiên không được để trống!")
|
| 26 |
+
@Min(value = 0, message = "Độ ưu tiên phải từ 0 đến 100!")
|
| 27 |
+
@Max(value = 100, message = "Độ ưu tiên phải từ 0 đến 100!")
|
| 28 |
+
private Integer priority;
|
| 29 |
+
}
|
src/main/java/com/darkfantasy/dto/gamecharacter/CreateGameCharacterRequest.java
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.gamecharacter;
|
| 2 |
+
|
| 3 |
+
import com.darkfantasy.entity.GameCharacter;
|
| 4 |
+
|
| 5 |
+
import jakarta.validation.constraints.Max;
|
| 6 |
+
import jakarta.validation.constraints.Min;
|
| 7 |
+
import jakarta.validation.constraints.NotBlank;
|
| 8 |
+
import jakarta.validation.constraints.NotNull;
|
| 9 |
+
import jakarta.validation.constraints.Size;
|
| 10 |
+
import lombok.AllArgsConstructor;
|
| 11 |
+
import lombok.Builder;
|
| 12 |
+
import lombok.Data;
|
| 13 |
+
import lombok.NoArgsConstructor;
|
| 14 |
+
|
| 15 |
+
@Data
|
| 16 |
+
@NoArgsConstructor
|
| 17 |
+
@AllArgsConstructor
|
| 18 |
+
@Builder
|
| 19 |
+
public class CreateGameCharacterRequest {
|
| 20 |
+
|
| 21 |
+
@NotBlank(message = "Tên nhân vật không được để trống")
|
| 22 |
+
@Size(max = 255, message = "Tên nhân vật không được vượt quá 255 ký tự")
|
| 23 |
+
private String name;
|
| 24 |
+
|
| 25 |
+
@NotBlank(message = "Chủng tộc không được để trống")
|
| 26 |
+
@Size(max = 100, message = "Chủng tộc không được vượt quá 100 ký tự")
|
| 27 |
+
private String race;
|
| 28 |
+
|
| 29 |
+
@NotBlank(message = "Mô tả không được để trống")
|
| 30 |
+
private String description;
|
| 31 |
+
|
| 32 |
+
@Size(max = 255, message = "Quote không được vượt quá 255 ký tự")
|
| 33 |
+
private String quote;
|
| 34 |
+
|
| 35 |
+
private String image;
|
| 36 |
+
|
| 37 |
+
@NotNull(message = "Độ ưu tiên không được để trống")
|
| 38 |
+
@Min(value = 0, message = "Độ ưu tiên phải từ 0 đến 100")
|
| 39 |
+
@Max(value = 100, message = "Độ ưu tiên phải từ 0 đến 100")
|
| 40 |
+
private Integer priority;
|
| 41 |
+
|
| 42 |
+
public GameCharacter toEntity() {
|
| 43 |
+
return GameCharacter.builder()
|
| 44 |
+
.name(this.name)
|
| 45 |
+
.race(this.race)
|
| 46 |
+
.description(this.description)
|
| 47 |
+
.quote(this.quote)
|
| 48 |
+
.image(this.image)
|
| 49 |
+
.priority(this.priority)
|
| 50 |
+
.build();
|
| 51 |
+
}
|
| 52 |
+
}
|
src/main/java/com/darkfantasy/dto/gamecharacter/GameCharacterResponse.java
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.gamecharacter;
|
| 2 |
+
|
| 3 |
+
import com.darkfantasy.entity.GameCharacter;
|
| 4 |
+
|
| 5 |
+
import lombok.AllArgsConstructor;
|
| 6 |
+
import lombok.Builder;
|
| 7 |
+
import lombok.Data;
|
| 8 |
+
import lombok.NoArgsConstructor;
|
| 9 |
+
|
| 10 |
+
@Data
|
| 11 |
+
@NoArgsConstructor
|
| 12 |
+
@AllArgsConstructor
|
| 13 |
+
@Builder
|
| 14 |
+
public class GameCharacterResponse {
|
| 15 |
+
private Long id;
|
| 16 |
+
private String name;
|
| 17 |
+
private String race;
|
| 18 |
+
private String description;
|
| 19 |
+
private String quote;
|
| 20 |
+
private String image;
|
| 21 |
+
private Integer priority;
|
| 22 |
+
private boolean deleted;
|
| 23 |
+
|
| 24 |
+
public static GameCharacterResponse fromEntity(GameCharacter character) {
|
| 25 |
+
return GameCharacterResponse.builder()
|
| 26 |
+
.id(character.getId())
|
| 27 |
+
.name(character.getName())
|
| 28 |
+
.race(character.getRace())
|
| 29 |
+
.description(character.getDescription())
|
| 30 |
+
.quote(character.getQuote())
|
| 31 |
+
.image(character.getImage())
|
| 32 |
+
.priority(character.getPriority())
|
| 33 |
+
.deleted(character.isDeleted())
|
| 34 |
+
.build();
|
| 35 |
+
}
|
| 36 |
+
}
|
src/main/java/com/darkfantasy/dto/gamecharacter/UpdateGameCharacterRequest.java
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.gamecharacter;
|
| 2 |
+
|
| 3 |
+
import com.darkfantasy.entity.GameCharacter;
|
| 4 |
+
|
| 5 |
+
import jakarta.validation.constraints.Max;
|
| 6 |
+
import jakarta.validation.constraints.Min;
|
| 7 |
+
import jakarta.validation.constraints.NotBlank;
|
| 8 |
+
import jakarta.validation.constraints.NotNull;
|
| 9 |
+
import jakarta.validation.constraints.Size;
|
| 10 |
+
import lombok.AllArgsConstructor;
|
| 11 |
+
import lombok.Builder;
|
| 12 |
+
import lombok.Data;
|
| 13 |
+
import lombok.NoArgsConstructor;
|
| 14 |
+
|
| 15 |
+
@Data
|
| 16 |
+
@NoArgsConstructor
|
| 17 |
+
@AllArgsConstructor
|
| 18 |
+
@Builder
|
| 19 |
+
public class UpdateGameCharacterRequest {
|
| 20 |
+
private Long id;
|
| 21 |
+
@NotBlank(message = "Tên nhân vật không được để trống")
|
| 22 |
+
@Size(max = 255, message = "Tên nhân vật không được vượt quá 255 ký tự")
|
| 23 |
+
private String name;
|
| 24 |
+
@NotBlank(message = "Chủng tộc không được để trống")
|
| 25 |
+
@Size(max = 100, message = "Chủng tộc không được vượt quá 100 ký tự")
|
| 26 |
+
private String race;
|
| 27 |
+
@NotBlank(message = "Mô tả không được để trống")
|
| 28 |
+
private String description;
|
| 29 |
+
@Size(max = 255, message = "Quote không được vượt quá 255 ký tự")
|
| 30 |
+
private String quote;
|
| 31 |
+
private String image;
|
| 32 |
+
@NotNull(message = "Độ ưu tiên không được để trống")
|
| 33 |
+
@Min(value = 0, message = "Độ ưu tiên phải từ 0 đến 100")
|
| 34 |
+
@Max(value = 100, message = "Độ ưu tiên phải từ 0 đến 100")
|
| 35 |
+
private Integer priority;
|
| 36 |
+
|
| 37 |
+
public GameCharacter toEntity() {
|
| 38 |
+
return GameCharacter.builder()
|
| 39 |
+
.id(this.id)
|
| 40 |
+
.name(this.name)
|
| 41 |
+
.race(this.race)
|
| 42 |
+
.description(this.description)
|
| 43 |
+
.quote(this.quote)
|
| 44 |
+
.image(this.image)
|
| 45 |
+
.priority(this.priority)
|
| 46 |
+
.build();
|
| 47 |
+
}
|
| 48 |
+
}
|
src/main/java/com/darkfantasy/dto/log/AuditLogResponse.java
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.log;
|
| 2 |
+
|
| 3 |
+
import java.time.Instant;
|
| 4 |
+
|
| 5 |
+
import com.darkfantasy.entity.AuditLog;
|
| 6 |
+
import com.darkfantasy.entity.enums.LogAction;
|
| 7 |
+
import com.darkfantasy.entity.enums.LogEntityType;
|
| 8 |
+
import com.darkfantasy.util.TimeUtil;
|
| 9 |
+
|
| 10 |
+
import lombok.Builder;
|
| 11 |
+
import lombok.Data;
|
| 12 |
+
|
| 13 |
+
@Data
|
| 14 |
+
@Builder
|
| 15 |
+
public class AuditLogResponse {
|
| 16 |
+
private Long id;
|
| 17 |
+
private Long userId;
|
| 18 |
+
private String username;
|
| 19 |
+
private LogEntityType entityType;
|
| 20 |
+
private Long entityId;
|
| 21 |
+
private LogAction action;
|
| 22 |
+
private String description;
|
| 23 |
+
private String createdAt;
|
| 24 |
+
|
| 25 |
+
public static AuditLogResponse fromEntity(AuditLog auditLog) {
|
| 26 |
+
return AuditLogResponse.builder()
|
| 27 |
+
.id(auditLog.getId())
|
| 28 |
+
.userId(auditLog.getUser().getId())
|
| 29 |
+
.username(auditLog.getUser().getUsername())
|
| 30 |
+
.entityType(auditLog.getEntityType())
|
| 31 |
+
.entityId(auditLog.getEntityId())
|
| 32 |
+
.action(auditLog.getAction())
|
| 33 |
+
.description(auditLog.getDescription())
|
| 34 |
+
.createdAt(TimeUtil.formatInstant(auditLog.getCreatedAt()))
|
| 35 |
+
.build();
|
| 36 |
+
}
|
| 37 |
+
}
|
src/main/java/com/darkfantasy/dto/story/CreateStoryRequest.java
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package com.darkfantasy.dto.story;
|
| 2 |
+
|
| 3 |
+
import com.darkfantasy.entity.Story;
|
| 4 |
+
|
| 5 |
+
import jakarta.validation.constraints.Max;
|
| 6 |
+
import jakarta.validation.constraints.Min;
|
| 7 |
+
import jakarta.validation.constraints.NotBlank;
|
| 8 |
+
import jakarta.validation.constraints.NotNull;
|
| 9 |
+
import jakarta.validation.constraints.Size;
|
| 10 |
+
import lombok.AllArgsConstructor;
|
| 11 |
+
import lombok.Builder;
|
| 12 |
+
import lombok.Data;
|
| 13 |
+
import lombok.NoArgsConstructor;
|
| 14 |
+
|
| 15 |
+
@Data
|
| 16 |
+
@NoArgsConstructor
|
| 17 |
+
@AllArgsConstructor
|
| 18 |
+
@Builder
|
| 19 |
+
public class CreateStoryRequest {
|
| 20 |
+
@NotBlank(message = "Tag không được để trống!")
|
| 21 |
+
@Size(max = 255, message = "Tag không được vượt quá 255 ký tự!")
|
| 22 |
+
private String tag;
|
| 23 |
+
|
| 24 |
+
@NotBlank(message = "Tiêu đề không được để trống!")
|
| 25 |
+
@Size(max = 255, message = "Tiêu đề không được vượt quá 255 ký tự!")
|
| 26 |
+
private String title;
|
| 27 |
+
@NotBlank(message = "Nội dung không được để trống!")
|
| 28 |
+
private String content;
|
| 29 |
+
private String image;
|
| 30 |
+
@NotBlank(message = "Nội dung quote không được để trống!")
|
| 31 |
+
private String quoteContent;
|
| 32 |
+
@NotBlank(message = "Tác giả trích dẫn không được để trống!")
|
| 33 |
+
private String quoteAuthor;
|
| 34 |
+
@NotNull(message = "Độ ưu tiên không được để trống!")
|
| 35 |
+
@Min(value = 0, message = "Độ ưu tiên phải từ 0 đến 100!")
|
| 36 |
+
@Max(value = 100, message = "Độ ưu tiên phải từ 0 đến 100!")
|
| 37 |
+
private Integer priority;
|
| 38 |
+
|
| 39 |
+
public Story toEntity() {
|
| 40 |
+
return Story.builder()
|
| 41 |
+
.tag(this.tag)
|
| 42 |
+
.title(this.title)
|
| 43 |
+
.content(this.content)
|
| 44 |
+
.image(this.image)
|
| 45 |
+
.quoteContent(this.quoteContent)
|
| 46 |
+
.quoteAuthor(this.quoteAuthor)
|
| 47 |
+
.priority(this.priority)
|
| 48 |
+
.build();
|
| 49 |
+
}
|
| 50 |
+
}
|