commit 11eaa9b007cbc856032efd8e3d3d4fb192741830
Author: 蔡旻峰 <1441170195@qq.com>
Date: Mon Sep 21 13:04:59 2026 +0800
feat:Init project
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0f97e48
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,37 @@
+# Maven
+target/
+!.mvn/wrapper/maven-wrapper.jar
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+
+# IDE
+.idea/
+.vscode/
+*.iml
+*.iws
+*.ipr
+.DS_Store
+
+# Logs
+logs/
+*.log
+ops_runtime/logs/
+
+# OS
+Thumbs.db
+desktop.ini
+
+# Application specific
+serviceKey.json
+**/config/service/serviceKey.json
+*.class
+
+# SVN (if exists)
+.svn/
+svn.url
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9537179
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# will-product-esign-backend
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..21e95dc
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,236 @@
+
+ 4.0.0
+
+ com.sjmresorts
+ auto-check-in-backend
+ 1.0
+ jar
+
+ auto-check-in-backend
+
+
+ UTF-8
+ 21
+ 1.5.20
+ ${java.version}
+ ${java.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.5.7
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+ org.springframework.boot
+ spring-boot-starter-mail
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback.version}
+
+
+ ch.qos.logback
+ logback-core
+ ${logback.version}
+
+
+ org.springframework.retry
+ spring-retry
+ 2.0.12
+
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 2.8.11
+
+
+ org.projectlombok
+ lombok
+
+
+ org.mapstruct
+ mapstruct
+ 1.6.3
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ 0.2.0
+
+
+ org.mapstruct
+ mapstruct-processor
+ 1.6.3
+
+
+ com.microsoft.sqlserver
+ mssql-jdbc
+
+
+ com.mysql
+ mysql-connector-j
+ runtime
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ com.alibaba.fastjson2
+ fastjson2
+ 2.0.58
+
+
+ org.apache.commons
+ commons-lang3
+ 3.18.0
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ 5.5
+
+
+ org.apache.pdfbox
+ pdfbox
+ 3.0.5
+
+
+ net.sourceforge.tess4j
+ tess4j
+ 5.11.0
+
+
+ com.auth0
+ java-jwt
+ 4.5.0
+
+
+ junit
+ junit
+ test
+
+
+
+ javax.xml.bind
+ jaxb-api
+ 2.3.1
+
+
+
+ org.glassfish.jaxb
+ jaxb-runtime
+ 4.0.5
+
+
+
+ com.google.firebase
+ firebase-admin
+ 9.5.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-ldap
+
+
+
+ com.hierynomus
+ smbj
+ 0.13.0
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${java.version}
+ ${java.version}
+ true
+
+
+ org.mapstruct
+ mapstruct-processor
+ 1.6.3
+
+
+ org.projectlombok
+ lombok
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ 0.2.0
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 3.3.0
+
+ -Xms512m -Xmx1536m -XX:MaxMetaspaceSize=256m -XX:ReservedCodeCacheSize=128m
+
+
+
+
+ repackage
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.3.0
+
+
+
+ true
+ com.sjmresorts.opsapp.MainApplication
+
+
+
+
+
+
+
diff --git a/settings/config.ini b/settings/config.ini
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/java/com/sjmresorts/opsapp/MainApplication.java b/src/main/java/com/sjmresorts/opsapp/MainApplication.java
new file mode 100644
index 0000000..1e19b94
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/MainApplication.java
@@ -0,0 +1,20 @@
+package com.sjmresorts.opsapp;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.web.servlet.ServletComponentScan;
+import org.springframework.data.web.config.EnableSpringDataWebSupport;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+@SpringBootApplication
+@ServletComponentScan
+@EnableScheduling
+@EnableAsync
+@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)
+public class MainApplication{
+
+ public static void main( String[] args ) {
+ SpringApplication.run(MainApplication.class, args);
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminDevicesController.java b/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminDevicesController.java
new file mode 100644
index 0000000..93e5ec3
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminDevicesController.java
@@ -0,0 +1,94 @@
+package com.sjmresorts.opsapp.admin.controllers;
+
+import com.sjmresorts.opsapp.admin.dtos.appsetting.AdminDevicesInfoPageRequest;
+import com.sjmresorts.opsapp.admin.dtos.appsetting.AdminDevicesInfoPageResponse;
+import com.sjmresorts.opsapp.admin.dtos.appsetting.AdminGetClientInfosResponse;
+import com.sjmresorts.opsapp.admin.dtos.appsetting.ClientInfosDto;
+import com.sjmresorts.opsapp.admin.services.AdminDevicesService;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import com.sjmresorts.opsapp.common.dtos.DataTableResult;
+import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.constraints.NotNull;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@Tag(name = "CMS", description = "APIs for Admin Devices functions")
+@RestController
+@RequestMapping("/api/v1/admin")
+public class AdminDevicesController {
+
+ @Resource
+ private AdminDevicesService adminDeviceService;
+
+ @Operation(
+ summary = "Query CMS devices info page",
+ description = "API for query CMS devices info page"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "CMS devices info page queried successful"),
+ @ApiResponse(responseCode = "400", description = "Invalid request data")
+ })
+ @PostMapping(value = "/devices/queryPage", produces = "application/json;charset=UTF-8")
+ public ResponseEntity getAppDevicesByPage(HttpServletRequest request,
+
+ @RequestBody(required = true) AdminDevicesInfoPageRequest pageRequest) {
+
+ DataTableResult devicesInfoList = adminDeviceService.getDevicesInfoListByPage(pageRequest);
+ AdminDevicesInfoPageResponse devicesInfoResponse = new AdminDevicesInfoPageResponse();
+ devicesInfoResponse.setCode("200");
+ devicesInfoResponse.setSuccess(true);
+ devicesInfoResponse.setMessage("Query Successfully");
+ devicesInfoResponse.setData(devicesInfoList);
+ return ResponseEntity.status(HttpStatus.OK).body(devicesInfoResponse);
+ }
+
+ @Operation(
+ summary = "Update CMS devices info",
+ description = "API for CMS devices info update"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "CMS devices info updated successful"),
+ @ApiResponse(responseCode = "400", description = "Invalid request data")
+ })
+ @PostMapping(value = "/devices/update", produces = "application/json;charset=UTF-8")
+ public ResponseEntity updateDevice(
+ HttpServletRequest request,
+ @RequestBody ClientInfosDto requestData) {
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if (loginUserInfo == null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ } else {
+ BaseApiResponse responseData = adminDeviceService.updateDevice(requestData, loginUserInfo);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+ }
+
+ @Operation(
+ summary = "Get one CMS devices info",
+ description = "API for get one CMS devices info"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "CMS devices info got successful"),
+ @ApiResponse(responseCode = "400", description = "Invalid request data")
+ })
+ @GetMapping(value = "/devices/{deviceId}", produces = "application/json;charset=UTF-8")
+ public ResponseEntity getAppDeviceById(
+ @PathVariable(name = "deviceId", required = true) @NotNull Long deviceId) {
+
+ AdminGetClientInfosResponse responseData = new AdminGetClientInfosResponse();
+ ClientInfosDto deviceDto = adminDeviceService.queryDeviceById(deviceId);
+ responseData.setCode("200");
+ responseData.setSuccess(deviceDto != null);
+ responseData.setMessage(deviceDto != null ? "Query Successfully" : "Device does not exists");
+ responseData.setData(deviceDto);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminLoginController.java b/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminLoginController.java
new file mode 100644
index 0000000..3e3f26e
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminLoginController.java
@@ -0,0 +1,158 @@
+package com.sjmresorts.opsapp.admin.controllers;
+
+import com.sjmresorts.opsapp.admin.dtos.system.login.AdminLoginRequest;
+import com.sjmresorts.opsapp.admin.dtos.system.login.AdminLoginResponse;
+import com.sjmresorts.opsapp.admin.dtos.system.login.AdminLogoutResponse;
+import com.sjmresorts.opsapp.admin.dtos.system.login.CmsLoginInfo;
+import com.sjmresorts.opsapp.admin.services.AdminLoginService;
+import com.sjmresorts.opsapp.common.constants.AppConstants;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.authentication.dtos.RefreshTokenResponse;
+import com.sjmresorts.opsapp.authentication.models.UserToken;
+import com.sjmresorts.opsapp.authentication.services.UserTokenService;
+import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Base64;
+
+@Tag(name = "CMS", description = "APIs for CMS administration")
+@RestController
+@RequestMapping("/api/v1/admin")
+@CrossOrigin(origins = "*")
+public class AdminLoginController {
+
+ @Resource
+ private AdminLoginService adminLoginService;
+ @Resource
+ private UserTokenService userTokenService;
+
+ @Operation(
+ summary = "CMS Login",
+ description = "API for CMS login with input ad account and password"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Check ad login success")
+ })
+ @PostMapping(value="/login",produces = "application/json;charset=UTF-8")
+ public ResponseEntity cmsLogin(
+ HttpServletRequest request,
+ @Parameter(description = "login request info", required = true) @Valid @RequestBody AdminLoginRequest requestData){
+ AdminLoginResponse result = new AdminLoginResponse();
+ String passwordVal = requestData.getPassword();
+ if(StringUtils.isNotBlank(passwordVal)) {
+ passwordVal =new String(Base64.getDecoder().decode(passwordVal.getBytes()));
+ }
+ int isCheckLogin;
+ try{
+ isCheckLogin = adminLoginService.checkLoginByLoginIdAndPassword(requestData.getUserCode(), passwordVal);
+ } catch (Exception e) {
+ result.setSuccess(false);
+ result.setCode("500");
+ result.setMessage(e.getCause().toString());
+ return ResponseEntity.status(HttpStatus.OK).body(result);
+ }
+
+ if (isCheckLogin >= 1) {
+ CmsLoginInfo loginInfo;
+ try{
+ loginInfo = adminLoginService.getLoginUserInfoByEmployeeId(requestData.getUserCode());
+ } catch (Exception e) {
+ result.setSuccess(false);
+ result.setCode("500");
+ result.setMessage(e.getCause().toString());
+ return ResponseEntity.status(HttpStatus.OK).body(result);
+ }
+ if (loginInfo != null && loginInfo.getLoginUser()!=null) {
+ userTokenService.clearUserTokenByType(AppConstants.CMS_API_ACCESS_TOKEN_TYPE,requestData.getUserCode());
+ userTokenService.clearUserTokenByType(AppConstants.CMS_REFRESH_TOKEN_TYPE,requestData.getUserCode());
+ LoginUserInfo loginUser = loginInfo.getLoginUser();
+ loginUser.setLoginSource("cms_login");
+ UserToken accessToken = JwtTokenUtil.generateTokenByLoginUser(AppConstants.CMS_API_ACCESS_TOKEN_TYPE,loginUser);
+ userTokenService.saveUserToken(accessToken);
+ UserToken refreshToken = JwtTokenUtil.generateTokenByLoginUser(AppConstants.CMS_REFRESH_TOKEN_TYPE,loginUser);
+ userTokenService.saveUserToken(refreshToken);
+ loginInfo.setAccessToken(accessToken.getTokenValue());
+ loginInfo.setRefreshToken(refreshToken.getTokenValue());
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setMessage("User "+requestData.getUserCode() + " login CMS successful.");
+ result.setLoginInfo(loginInfo);
+ } else {
+ result.setSuccess(false);
+ result.setMessage("User "+requestData.getUserCode()+" not found.");
+ }
+ } else if (isCheckLogin == -2) {
+ result.setSuccess(false);
+ result.setMessage(requestData.getUserCode() + " has been locked.");
+ } else if (isCheckLogin == -3) {
+ result.setSuccess(false);
+ result.setMessage(requestData.getUserCode() + " has been disabled.");
+ } else {
+ result.setSuccess(false);
+ result.setMessage("Incorrect login ID or password.");
+ }
+ return ResponseEntity.status(HttpStatus.OK).body(result);
+ }
+
+ @PostMapping(value="/verifyToken",produces = "application/json;charset=UTF-8")
+ public ResponseEntity verifyToken(
+ HttpServletRequest request,
+ HttpServletResponse response) {
+ return ResponseEntity.status(HttpStatus.OK).body("Token Verified");
+ }
+
+ @PostMapping(value="/logout",produces = "application/json;charset=UTF-8")
+ public ResponseEntity processLogout(
+ HttpServletRequest request,
+ HttpServletResponse response) {
+ AdminLogoutResponse result = new AdminLogoutResponse();
+ LoginUserInfo loginUser = JwtTokenUtil.parseUserFromRequest(request);
+ if(loginUser!=null){
+ adminLoginService.processLogout(loginUser);
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setMessage("Logout successful.");
+ }else{
+ result.setSuccess(false);
+ result.setCode("401");
+ result.setMessage("Unauthorized logout");
+ }
+ return ResponseEntity.status(HttpStatus.OK).body(result);
+ }
+
+ @Operation(
+ summary = "Renew API token by refresh token",
+ description = "API to renew access token"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Access Token refreshed successful")
+ })
+ @PostMapping(value="/refreshToken",produces = "application/json;charset=UTF-8")
+ public ResponseEntity refreshToken(
+ HttpServletRequest request,
+ HttpServletResponse response) {
+ RefreshTokenResponse responseData = new RefreshTokenResponse();
+ LoginUserInfo loginUser = JwtTokenUtil.parseUserFromRequest(request);
+ if(loginUser!=null){
+ responseData = adminLoginService.refreshToken(loginUser);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }else{
+ responseData.setSuccess(false);
+ responseData.setCode("401");
+ responseData.setMessage("Unauthorized operation");
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(responseData);
+ }
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminMenusController.java b/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminMenusController.java
new file mode 100644
index 0000000..7b94ccf
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminMenusController.java
@@ -0,0 +1,48 @@
+package com.sjmresorts.opsapp.admin.controllers;
+
+import com.sjmresorts.opsapp.admin.dtos.menus.AdminMenuStatusRequest;
+import com.sjmresorts.opsapp.admin.dtos.menus.AdminMenuStatusResponse;
+import com.sjmresorts.opsapp.admin.dtos.menus.AdminMenusListResponse;
+import com.sjmresorts.opsapp.admin.services.AdminMenusService;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@Tag(name = "CMS", description = "APIs for CMS sidebar menus")
+@RestController
+@RequestMapping("/api/v1/admin")
+@CrossOrigin(origins = "*")
+public class AdminMenusController {
+
+ @Resource
+ private AdminMenusService adminMenusService;
+
+ @Operation(summary = "List CMS sidebar menus with visibility status")
+ @PostMapping(value = "/menu/list", produces = "application/json;charset=UTF-8")
+ public ResponseEntity listMenus(HttpServletRequest request) {
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if (loginUserInfo == null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ }
+ return ResponseEntity.ok(adminMenusService.listMenus());
+ }
+
+ @Operation(summary = "Update CMS sidebar menu visibility (sys_routers.status)")
+ @PostMapping(value = "/menu/update_status", produces = "application/json;charset=UTF-8")
+ public ResponseEntity updateStatus(
+ HttpServletRequest request,
+ @Valid @RequestBody AdminMenuStatusRequest body) {
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if (loginUserInfo == null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ }
+ return ResponseEntity.ok(adminMenusService.updateStatus(body, loginUserInfo));
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminRoleUsersController.java b/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminRoleUsersController.java
new file mode 100644
index 0000000..81cd5b9
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminRoleUsersController.java
@@ -0,0 +1,101 @@
+package com.sjmresorts.opsapp.admin.controllers;
+
+import com.sjmresorts.opsapp.admin.dtos.permissions.AdminAssignGroupPermissionRequest;
+import com.sjmresorts.opsapp.admin.dtos.permissions.AdminAssignGroupPermissionResponse;
+import com.sjmresorts.opsapp.admin.dtos.roleUsers.AdminGetRoleUsersRequest;
+import com.sjmresorts.opsapp.admin.dtos.roleUsers.AdminGetRoleUsersResponse;
+import com.sjmresorts.opsapp.admin.services.AdminPermissionService;
+import com.sjmresorts.opsapp.admin.services.AdminUserService;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.authentication.models.User;
+import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Tag(name = "CMS", description = "APIs for CMS RoleUser")
+@RestController
+@RequestMapping("/api/v1/admin")
+@CrossOrigin(origins = "*")
+public class AdminRoleUsersController {
+
+ @Resource
+ private AdminUserService adminUserService;
+
+ @Resource
+ private AdminPermissionService adminPermissionService;
+
+ @Operation(
+ summary = "CMS Get Role And User Status",
+ description = "API for CMS Get Role And User Status"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Check CMS Get Role And User Status Success")
+ })
+ @PostMapping(value = "/role/get_role_user_status", produces = "application/json;charset=UTF-8")
+ public ResponseEntity cmsGetRoleUserStatus(
+ HttpServletRequest request,
+ @RequestBody(required = true) AdminGetRoleUsersRequest pageRequest) {
+ AdminGetRoleUsersResponse Requestresult = new AdminGetRoleUsersResponse();
+ Map> serviceResult = new HashMap<>();
+ List assignUsers = adminUserService.listUser(pageRequest,"USER_ROLE");
+ List unassignUsers = adminUserService.listUser(pageRequest,"UNASSIGN_USER");
+ serviceResult.put("assignUser", assignUsers);
+ serviceResult.put("unassignUser", unassignUsers);
+
+ if (serviceResult != null) {
+ Requestresult.setSuccess(true);
+ Requestresult.setMessage("CMS Get Roles Success");
+ Requestresult.setData(serviceResult);
+ Requestresult.setCode("200");
+ }else{
+ Requestresult.setCode("401");
+ Requestresult.setMessage("CMS Get Roles Failed");
+ Requestresult.setSuccess(false);
+ }
+ return ResponseEntity.status(HttpStatus.OK).body(Requestresult);
+ }
+
+ @Operation(
+ summary = "CMS Assign Role Permissions",
+ description = "API for CMS Assign Role Permissions"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "CMS Assign Role Permissions")
+ })
+ @PostMapping(value = "/role/assign_permission", produces = "application/json;charset=UTF-8")
+ public ResponseEntity cmsAssignRolePermissions(
+ HttpServletRequest request,
+ @Parameter(description = "CMS Assign Role Permissions", required = true) @Valid @RequestBody AdminAssignGroupPermissionRequest requestData) {
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if(loginUserInfo==null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ }else{
+ boolean success = adminPermissionService.assginPermission(requestData);
+ AdminAssignGroupPermissionResponse responseData = new AdminAssignGroupPermissionResponse();
+ if(success) {
+ responseData.setCode("200");
+ responseData.setSuccess(true);
+ responseData.setMessage("CMS Assign Role Permissions Success");
+ }else{
+ responseData.setSuccess(false);
+ responseData.setMessage("CMS Assign Role Permissions Failed");
+ }
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+ }
+}
+
+
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminRolesController.java b/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminRolesController.java
new file mode 100644
index 0000000..e4a6503
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminRolesController.java
@@ -0,0 +1,203 @@
+package com.sjmresorts.opsapp.admin.controllers;
+
+import com.sjmresorts.opsapp.admin.dtos.permissions.AdminListPermissionSettingRequest;
+import com.sjmresorts.opsapp.admin.dtos.permissions.AdminListPermissionSettingResponse;
+import com.sjmresorts.opsapp.admin.dtos.roles.*;
+import com.sjmresorts.opsapp.admin.services.AdminPermissionService;
+import com.sjmresorts.opsapp.admin.services.AdminRolesService;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import com.sjmresorts.opsapp.common.dtos.DataTableResult;
+import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@Tag(name = "CMS", description = "APIs for CMS Roles")
+@RestController
+@RequestMapping("/api/v1/admin")
+@CrossOrigin(origins = "*")
+public class AdminRolesController {
+ @Resource
+ private AdminRolesService adminRolesService;
+ @Resource
+ private AdminPermissionService adminPermissionService;
+
+ @Operation(
+ summary = "CMS Get Roles",
+ description = "API for CMS Get Roles"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Check CMS Get Roles success")
+ })
+ @PostMapping(value = "/role/get_roles", produces = "application/json;charset=UTF-8")
+ public ResponseEntity cmsGetRoles(
+ HttpServletRequest request,
+ @RequestBody(required = true) AdminGetRolesRequest pageRequest) {
+ AdminGetRolesResponse Requestresult = new AdminGetRolesResponse();
+ DataTableResult serviceResult = adminRolesService.getRoleInfoListByPage(pageRequest);
+
+ if (serviceResult != null) {
+ Requestresult.setSuccess(true);
+ Requestresult.setMessage("CMS Get Roles Success");
+ Requestresult.setData(serviceResult);
+ Requestresult.setCode("200");
+ }else{
+ Requestresult.setCode("401");
+ Requestresult.setMessage("CMS Get Roles Failed");
+ Requestresult.setSuccess(false);
+ }
+ return ResponseEntity.status(HttpStatus.OK).body(Requestresult);
+ }
+
+
+ @Operation(
+ summary = "CMS Create Role",
+ description = "API for CMS Create Role"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Check CMS Create Role success")
+ })
+ @PostMapping(value = "/role/create_role", produces = "application/json;charset=UTF-8")
+ public ResponseEntity cmsCreateRole(
+ HttpServletRequest request,
+ @Parameter(description = "CMS Create Role info", required = true) @Valid @RequestBody AdminCreateRoleRequest requestData) {
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if(loginUserInfo==null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ }else{
+ BaseApiResponse responseData = adminRolesService.createRole(requestData, loginUserInfo);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+ }
+
+
+ @Operation(
+ summary = "CMS Delete Role",
+ description = "API for CMS Delete Role"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Check CMS Delete Role success")
+ })
+ @PostMapping(value = "/role/delete_role", produces = "application/json;charset=UTF-8")
+ public ResponseEntity cmsDeleteRole(
+ HttpServletRequest request,
+ @Parameter(description = "CMS Delete Role info", required = true) @Valid @RequestBody AdminDeleteRoleRequest requestData) {
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if(loginUserInfo==null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ }else{
+ BaseApiResponse responseData = adminRolesService.deleteRole(requestData, loginUserInfo);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+ }
+
+
+ @Operation(
+ summary = "CMS Modify Role",
+ description = "API for CMS Modify Role"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Check CMS Modify Role success")
+ })
+ @PostMapping(value = "/role/modify_role", produces = "application/json;charset=UTF-8")
+ public ResponseEntity cmsmodifyRole(
+ HttpServletRequest request,
+ @Parameter(description = "CMS modify Role info", required = true) @Valid @RequestBody AdminModifyRoleRequest requestData) {
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if(loginUserInfo==null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ }else{
+ BaseApiResponse responseData = adminRolesService.modifyRole(requestData, loginUserInfo);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+ }
+
+ @Operation(
+ summary = "CMS Get Role By Id",
+ description = "API for CMS Get Role By Id"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Check CMS Get Role success")
+ })
+ @PostMapping(value = "/role/get_role_by_id", produces = "application/json;charset=UTF-8")
+ public ResponseEntity cmsGetRoleById(
+ HttpServletRequest request,
+ @Parameter(description = "CMS modify Role info", required = true) @Valid @RequestBody AdminGetRoleByIdRequest requestData) {
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if(loginUserInfo==null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ }else{
+ AdminGetRoleByIdResponse responseData = adminRolesService.getRoleById(requestData);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+ }
+
+
+ @Operation(
+ summary = "CMS Set Assign Users",
+ description = "API for CMS Set Assign Users"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "CMS Set Assign Users")
+ })
+ @PostMapping(value = "/role/assign_users", produces = "application/json;charset=UTF-8")
+ public ResponseEntity cmsSetAssignUsers(
+ HttpServletRequest request,
+ @Parameter(description = "CMS modify Role info", required = true) @Valid @RequestBody AdminRoleAssignUsersRequest requestData) {
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if(loginUserInfo==null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ }else{
+ AdminRoleAssignUsersResponse responseData = new AdminRoleAssignUsersResponse();
+ Boolean serviceResult = adminRolesService.updateGroupAssignUser(requestData,loginUserInfo);
+ if(serviceResult) {
+ responseData.setCode("200");
+ responseData.setSuccess(true);
+ responseData.setMessage("CMS Set Assign Users Success");
+ }else{
+ responseData.setSuccess(false);
+ responseData.setMessage("CMS Set Assign Users Failed");
+ }
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+ }
+
+ @Operation(
+ summary = "CMS Get Permission",
+ description = "API for CMS Get Permission"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "CMS Get Permission")
+ })
+ @PostMapping(value = "/role/list_permission_setting", produces = "application/json;charset=UTF-8")
+ public ResponseEntity cmsGetListPermission(
+ HttpServletRequest request,
+ @Parameter(description = "CMS Get Permission", required = true) @Valid @RequestBody AdminListPermissionSettingRequest requestData) {
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if(loginUserInfo==null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ }else{
+ AdminListPermissionSettingResponse responseData = adminPermissionService.getPermissionById(requestData);
+ if(responseData!=null) {
+ responseData.setCode("200");
+ responseData.setSuccess(true);
+ responseData.setMessage("CMS Get Permission Success");
+ }else{
+ responseData.setSuccess(false);
+ responseData.setMessage("CMS Get Permission Failed");
+ }
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+ }
+}
+
+
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminUserController.java b/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminUserController.java
new file mode 100644
index 0000000..3d16df5
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/controllers/AdminUserController.java
@@ -0,0 +1,137 @@
+package com.sjmresorts.opsapp.admin.controllers;
+
+import com.sjmresorts.opsapp.admin.dtos.system.user.CmsAppUserPageRequest;
+import com.sjmresorts.opsapp.admin.dtos.system.user.CmsUserDto;
+import com.sjmresorts.opsapp.admin.dtos.system.user.CmsUserInfoResponse;
+import com.sjmresorts.opsapp.admin.dtos.system.user.GetCmsUserResponse;
+import com.sjmresorts.opsapp.admin.services.AdminUserService;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import com.sjmresorts.opsapp.common.dtos.DataTableResult;
+import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.constraints.NotNull;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@Tag(name = "CMS", description = "APIs for Admin User functions")
+@RestController
+@RequestMapping("/api/v1/admin")
+public class AdminUserController {
+
+ @Resource
+ private AdminUserService adminUserService;
+
+ @Operation(
+ summary = "Query CMS user info page",
+ description = "API for query CMS user info page"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "CMS user info page queried successful"),
+ @ApiResponse(responseCode = "400", description = "Invalid request data")
+ })
+ @PostMapping(value = "/user/queryPage", produces = "application/json;charset=UTF-8")
+ public ResponseEntity getAppUserByPage(
+ HttpServletRequest request,
+ @RequestBody(required = true) CmsAppUserPageRequest pageRequest) {
+
+ DataTableResult userInfoList = adminUserService.getUserInfoListByPage(pageRequest);
+ CmsUserInfoResponse cmsUserInfoResponse = new CmsUserInfoResponse();
+ cmsUserInfoResponse.setCode("200");
+ cmsUserInfoResponse.setSuccess(true);
+ cmsUserInfoResponse.setMessage("Query Successfully");
+ cmsUserInfoResponse.setData(userInfoList);
+ return ResponseEntity.status(HttpStatus.OK).body(cmsUserInfoResponse);
+ }
+
+ @Operation(
+ summary = "Create CMS user",
+ description = "API for CMS user creation"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "CMS user created successful"),
+ @ApiResponse(responseCode = "400", description = "Invalid request data")
+ })
+ @PostMapping(value = "/user/create", produces = "application/json;charset=UTF-8")
+ public ResponseEntity createUser(
+ HttpServletRequest request,
+ @RequestBody CmsUserDto requestData) {
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if (loginUserInfo == null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ } else {
+ BaseApiResponse responseData = adminUserService.createUser(requestData, loginUserInfo);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+ }
+
+ @Operation(
+ summary = "Update CMS user status",
+ description = "API for CMS user status update"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "CMS user status updated successful"),
+ @ApiResponse(responseCode = "400", description = "Invalid request data")
+ })
+ @PostMapping(value = "/user/updateStatus", produces = "application/json;charset=UTF-8")
+ public ResponseEntity updateStatus(
+ HttpServletRequest request,
+ @RequestBody CmsUserDto requestData) {
+
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if (loginUserInfo == null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ } else {
+ BaseApiResponse responseData = adminUserService.updateStatus(requestData, loginUserInfo);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+ }
+
+ @Operation(
+ summary = "Update CMS user info",
+ description = "API for CMS user info update"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "CMS user info updated successful"),
+ @ApiResponse(responseCode = "400", description = "Invalid request data")
+ })
+ @PostMapping(value = "/user/update", produces = "application/json;charset=UTF-8")
+ public ResponseEntity updateUser(
+ HttpServletRequest request,
+ @RequestBody CmsUserDto requestData) {
+ LoginUserInfo loginUserInfo = JwtTokenUtil.parseUserFromRequest(request);
+ if (loginUserInfo == null) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ } else {
+ BaseApiResponse responseData = adminUserService.updateUser(requestData, loginUserInfo);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+ }
+
+ @Operation(
+ summary = "Get one CMS user info",
+ description = "API for get one CMS user info"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "CMS user info got successful"),
+ @ApiResponse(responseCode = "400", description = "Invalid request data")
+ })
+ @GetMapping(value = "/user/{userId}", produces = "application/json;charset=UTF-8")
+ public ResponseEntity getUserById(
+ @PathVariable(name = "userId", required = true) @NotNull Long userId) {
+
+ GetCmsUserResponse responseData = new GetCmsUserResponse();
+ CmsUserDto userDto = adminUserService.queryCmsUserById(userId);
+ responseData.setCode("200");
+ responseData.setSuccess(userDto != null);
+ responseData.setMessage(userDto != null ? "Query Successfully" : "User does not exists");
+ responseData.setData(userDto);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/AdminDevicesInfoPageRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/AdminDevicesInfoPageRequest.java
new file mode 100644
index 0000000..021764b
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/AdminDevicesInfoPageRequest.java
@@ -0,0 +1,17 @@
+package com.sjmresorts.opsapp.admin.dtos.appsetting;
+
+import com.sjmresorts.opsapp.common.dtos.BasePageRequest;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Get Admin Devices Info Page Request")
+public class AdminDevicesInfoPageRequest extends BasePageRequest {
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/AdminDevicesInfoPageResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/AdminDevicesInfoPageResponse.java
new file mode 100644
index 0000000..c7d1b98
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/AdminDevicesInfoPageResponse.java
@@ -0,0 +1,18 @@
+package com.sjmresorts.opsapp.admin.dtos.appsetting;
+
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import com.sjmresorts.opsapp.common.dtos.DataTableResult;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Get Admin Devices Info Page Response")
+public class AdminDevicesInfoPageResponse extends BaseApiResponse {
+
+ @Schema(description = "Match Devices Info items")
+ private DataTableResult data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/AdminGetClientInfosRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/AdminGetClientInfosRequest.java
new file mode 100644
index 0000000..22f08dd
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/AdminGetClientInfosRequest.java
@@ -0,0 +1,19 @@
+package com.sjmresorts.opsapp.admin.dtos.appsetting;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "CMS get device Request")
+public class AdminGetClientInfosRequest {
+ @Schema(description = "Device ID of Clients Info")
+ @NotBlank(message = "Device Id must not be blank")
+ private String deviceId;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/AdminGetClientInfosResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/AdminGetClientInfosResponse.java
new file mode 100644
index 0000000..c2803a3
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/AdminGetClientInfosResponse.java
@@ -0,0 +1,17 @@
+package com.sjmresorts.opsapp.admin.dtos.appsetting;
+
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "CMS get device Response")
+public class AdminGetClientInfosResponse extends BaseApiResponse {
+
+ @Schema(description = "Match Device items")
+ private ClientInfosDto data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/ClientInfosDto.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/ClientInfosDto.java
new file mode 100644
index 0000000..107dd81
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/ClientInfosDto.java
@@ -0,0 +1,26 @@
+package com.sjmresorts.opsapp.admin.dtos.appsetting;
+
+import lombok.Data;
+
+import java.time.Instant;
+
+@Data
+public class ClientInfosDto {
+
+ private Long id;
+
+ private Boolean isLatest;
+
+ private Boolean isActive;
+
+ private String clientName;
+
+ private String clientVersion;
+
+ private String deviceFuid;
+
+ private String casinoCode;
+
+ private Instant updatedAt;
+}
+
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/GetAppSettingRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/GetAppSettingRequest.java
new file mode 100644
index 0000000..849cdee
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/appsetting/GetAppSettingRequest.java
@@ -0,0 +1,21 @@
+package com.sjmresorts.opsapp.admin.dtos.appsetting;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.List;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "CMS get App Setting Request")
+public class GetAppSettingRequest {
+ @Schema(description = "App setting keys to retrieve")
+ @NotNull(message = "App setting keys must not be empty")
+ private List appSettingKeys;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/menus/AdminMenuItemDto.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/menus/AdminMenuItemDto.java
new file mode 100644
index 0000000..6d851b8
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/menus/AdminMenuItemDto.java
@@ -0,0 +1,20 @@
+package com.sjmresorts.opsapp.admin.dtos.menus;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class AdminMenuItemDto {
+ private String id;
+ private String parentId;
+ private String heading;
+ private String sectionTitle;
+ private String route;
+ private String fontIcon;
+ private Integer sortNo;
+ /** 1=显示,0=隐藏 */
+ private Integer status;
+ /** 不可关闭(避免锁死后台) */
+ private boolean locked;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/menus/AdminMenuStatusRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/menus/AdminMenuStatusRequest.java
new file mode 100644
index 0000000..8674569
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/menus/AdminMenuStatusRequest.java
@@ -0,0 +1,16 @@
+package com.sjmresorts.opsapp.admin.dtos.menus;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class AdminMenuStatusRequest {
+ @NotBlank
+ private String routerId;
+ /** 1=显示,0=隐藏 */
+ @NotNull
+ private Integer status;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/menus/AdminMenuStatusResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/menus/AdminMenuStatusResponse.java
new file mode 100644
index 0000000..daa24b9
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/menus/AdminMenuStatusResponse.java
@@ -0,0 +1,18 @@
+package com.sjmresorts.opsapp.admin.dtos.menus;
+
+import com.sjmresorts.opsapp.admin.dtos.system.router.UserRouterDto;
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+public class AdminMenuStatusResponse extends BaseApiResponse {
+ /** 更新后当前登录用户侧栏菜单(可立刻刷新) */
+ private List userRouters = new ArrayList<>();
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/menus/AdminMenusListResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/menus/AdminMenusListResponse.java
new file mode 100644
index 0000000..ebb4112
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/menus/AdminMenusListResponse.java
@@ -0,0 +1,16 @@
+package com.sjmresorts.opsapp.admin.dtos.menus;
+
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+public class AdminMenusListResponse extends BaseApiResponse {
+ private List data = new ArrayList<>();
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/permissions/AdminAssignGroupPermissionRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/permissions/AdminAssignGroupPermissionRequest.java
new file mode 100644
index 0000000..cbbd5b9
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/permissions/AdminAssignGroupPermissionRequest.java
@@ -0,0 +1,22 @@
+package com.sjmresorts.opsapp.admin.dtos.permissions;
+
+import com.sjmresorts.opsapp.authentication.models.RouterPermission;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.List;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = " Assign Role Permission Request")
+public class AdminAssignGroupPermissionRequest {
+ @Schema(description = "Role Id",example="200")
+ private Long roleId;
+ @Schema(description = "Router Operation",example="200")
+ private List assignRouterOperation;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/permissions/AdminAssignGroupPermissionResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/permissions/AdminAssignGroupPermissionResponse.java
new file mode 100644
index 0000000..d3b23ed
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/permissions/AdminAssignGroupPermissionResponse.java
@@ -0,0 +1,27 @@
+package com.sjmresorts.opsapp.admin.dtos.permissions;
+
+import com.sjmresorts.opsapp.admin.models.GroupPermissionObject;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of CMS Delete Role")
+public class AdminAssignGroupPermissionResponse {
+ @Schema(description = "Indicate CMS delete roles success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Get roles successfully")
+ private String message;
+
+ @Schema(description = "data of result")
+ private GroupPermissionObject data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/permissions/AdminListPermissionSettingRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/permissions/AdminListPermissionSettingRequest.java
new file mode 100644
index 0000000..e1d3f34
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/permissions/AdminListPermissionSettingRequest.java
@@ -0,0 +1,17 @@
+package com.sjmresorts.opsapp.admin.dtos.permissions;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = " Delete Role Request")
+public class AdminListPermissionSettingRequest {
+ @Schema(description = "Role Id",example="200")
+ private Long roleId;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/permissions/AdminListPermissionSettingResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/permissions/AdminListPermissionSettingResponse.java
new file mode 100644
index 0000000..abf6d06
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/permissions/AdminListPermissionSettingResponse.java
@@ -0,0 +1,27 @@
+package com.sjmresorts.opsapp.admin.dtos.permissions;
+
+import com.sjmresorts.opsapp.admin.models.GroupPermissionObject;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of CMS Delete Role")
+public class AdminListPermissionSettingResponse {
+ @Schema(description = "Indicate CMS delete roles success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Get roles successfully")
+ private String message;
+
+ @Schema(description = "data of result")
+ private GroupPermissionObject data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roleUsers/AdminGetRoleUsersRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roleUsers/AdminGetRoleUsersRequest.java
new file mode 100644
index 0000000..add51a6
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roleUsers/AdminGetRoleUsersRequest.java
@@ -0,0 +1,21 @@
+package com.sjmresorts.opsapp.admin.dtos.roleUsers;
+
+import com.sjmresorts.opsapp.common.dtos.BasePageRequest;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "CMS Get Role Uers Status Request")
+public class AdminGetRoleUsersRequest extends BasePageRequest {
+
+ private Long searchRoleId;
+
+ private String searchRoleName;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roleUsers/AdminGetRoleUsersResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roleUsers/AdminGetRoleUsersResponse.java
new file mode 100644
index 0000000..ee3441f
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roleUsers/AdminGetRoleUsersResponse.java
@@ -0,0 +1,30 @@
+package com.sjmresorts.opsapp.admin.dtos.roleUsers;
+
+import com.sjmresorts.opsapp.authentication.models.User;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of CMS Role Users Status")
+public class AdminGetRoleUsersResponse {
+ @Schema(description = "Indicate CMS get roles success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Get roles successfully")
+ private String message;
+
+ @Schema(description = "data of result")
+ private Map> data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roleUsers/AdminRoleUsersDto.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roleUsers/AdminRoleUsersDto.java
new file mode 100644
index 0000000..69ea795
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roleUsers/AdminRoleUsersDto.java
@@ -0,0 +1,13 @@
+package com.sjmresorts.opsapp.admin.dtos.roleUsers;
+
+import lombok.Data;
+import org.mapstruct.Mapper;
+
+@Data
+@Mapper(componentModel = "spring")
+public class AdminRoleUsersDto {
+
+ private Long id;
+ private Long roleId;
+ private Long userId;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminCreateRoleRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminCreateRoleRequest.java
new file mode 100644
index 0000000..7ade959
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminCreateRoleRequest.java
@@ -0,0 +1,17 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = " Create Role Request")
+public class AdminCreateRoleRequest {
+ @Schema(description = "Role Name",example="200")
+ private String roleName;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminCreateRoleResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminCreateRoleResponse.java
new file mode 100644
index 0000000..9fc9f8d
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminCreateRoleResponse.java
@@ -0,0 +1,27 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import com.sjmresorts.opsapp.authentication.models.Role;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of CMS Create Role")
+public class AdminCreateRoleResponse {
+ @Schema(description = "Indicate CMS create roles success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Get roles successfully")
+ private String message;
+
+ @Schema(description = "data of result")
+ private Role data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminDeleteRoleRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminDeleteRoleRequest.java
new file mode 100644
index 0000000..b479efe
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminDeleteRoleRequest.java
@@ -0,0 +1,19 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = " Delete Role Request")
+public class AdminDeleteRoleRequest {
+ @Schema(description = "Role Id",example="200")
+ private Long roleId;
+ @Schema(description = "Role Name",example="200")
+ private String roleName;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminDeleteRoleResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminDeleteRoleResponse.java
new file mode 100644
index 0000000..3d16497
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminDeleteRoleResponse.java
@@ -0,0 +1,27 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import com.sjmresorts.opsapp.authentication.models.Role;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of CMS Delete Role")
+public class AdminDeleteRoleResponse {
+ @Schema(description = "Indicate CMS delete roles success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Get roles successfully")
+ private String message;
+
+ @Schema(description = "data of result")
+ private Role data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminGetRoleByIdRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminGetRoleByIdRequest.java
new file mode 100644
index 0000000..bd0667e
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminGetRoleByIdRequest.java
@@ -0,0 +1,17 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = " Modify Role Request")
+public class AdminGetRoleByIdRequest {
+ @Schema(description = "Role Id",example="200")
+ private Long roleId;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminGetRoleByIdResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminGetRoleByIdResponse.java
new file mode 100644
index 0000000..c902a7a
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminGetRoleByIdResponse.java
@@ -0,0 +1,27 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import com.sjmresorts.opsapp.authentication.models.Role;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of CMS Get Role By Id")
+public class AdminGetRoleByIdResponse {
+ @Schema(description = "Indicate CMS get roles by id success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Get roles successfully")
+ private String message;
+
+ @Schema(description = "data of result")
+ private Role data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminGetRolesRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminGetRolesRequest.java
new file mode 100644
index 0000000..920a26d
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminGetRolesRequest.java
@@ -0,0 +1,19 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import com.sjmresorts.opsapp.common.dtos.BasePageRequest;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "CMS Roles Request")
+public class AdminGetRolesRequest extends BasePageRequest {
+
+ private String searchRoleName;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminGetRolesResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminGetRolesResponse.java
new file mode 100644
index 0000000..8f279e3
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminGetRolesResponse.java
@@ -0,0 +1,27 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import com.sjmresorts.opsapp.common.dtos.DataTableResult;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of CMS Roles")
+public class AdminGetRolesResponse {
+ @Schema(description = "Indicate CMS get roles success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Get roles successfully")
+ private String message;
+
+ @Schema(description = "data of result")
+ private DataTableResult data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminModifyRoleRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminModifyRoleRequest.java
new file mode 100644
index 0000000..5a15131
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminModifyRoleRequest.java
@@ -0,0 +1,19 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = " Modify Role Request")
+public class AdminModifyRoleRequest {
+ @Schema(description = "Role Id",example="200")
+ private Long roleId;
+ @Schema(description = "Role Name",example="200")
+ private String roleName;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminModifyRoleResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminModifyRoleResponse.java
new file mode 100644
index 0000000..146427d
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminModifyRoleResponse.java
@@ -0,0 +1,27 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import com.sjmresorts.opsapp.authentication.models.Role;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of CMS Modify Role")
+public class AdminModifyRoleResponse {
+ @Schema(description = "Indicate CMS modify roles success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Get roles successfully")
+ private String message;
+
+ @Schema(description = "data of result")
+ private Role data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminRoleAssignUsersRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminRoleAssignUsersRequest.java
new file mode 100644
index 0000000..c6360cc
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminRoleAssignUsersRequest.java
@@ -0,0 +1,21 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.List;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = " Delete Role Request")
+public class AdminRoleAssignUsersRequest {
+ @Schema(description = "Role Id",example="200")
+ private Long roleId;
+ @Schema(description = "Role Name",example="200")
+ private List employeeIds;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminRoleAssignUsersResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminRoleAssignUsersResponse.java
new file mode 100644
index 0000000..cfa3719
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminRoleAssignUsersResponse.java
@@ -0,0 +1,27 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import com.sjmresorts.opsapp.authentication.models.Role;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of CMS Delete Role")
+public class AdminRoleAssignUsersResponse {
+ @Schema(description = "Indicate CMS delete roles success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Get roles successfully")
+ private String message;
+
+ @Schema(description = "data of result")
+ private Role data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminRoleDto.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminRoleDto.java
new file mode 100644
index 0000000..9dfb8e2
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/roles/AdminRoleDto.java
@@ -0,0 +1,11 @@
+package com.sjmresorts.opsapp.admin.dtos.roles;
+
+import lombok.Data;
+import org.mapstruct.Mapper;
+
+@Data
+@Mapper(componentModel = "spring")
+public class AdminRoleDto {
+ private Long roleId;
+ private String roleName;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/login/AdminLoginRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/login/AdminLoginRequest.java
new file mode 100644
index 0000000..8eb994f
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/login/AdminLoginRequest.java
@@ -0,0 +1,25 @@
+package com.sjmresorts.opsapp.admin.dtos.system.login;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "CMS LoginRequest")
+public class AdminLoginRequest {
+
+ @Schema(description = "AD Account of Employee")
+ @NotBlank(message = "Employee Id must not be blank")
+ private String userCode;
+
+ @Schema(description = "Password of AD Account")
+ @NotBlank(message = "Password must not be blank")
+ private String password;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/login/AdminLoginResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/login/AdminLoginResponse.java
new file mode 100644
index 0000000..cc8f9c1
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/login/AdminLoginResponse.java
@@ -0,0 +1,28 @@
+package com.sjmresorts.opsapp.admin.dtos.system.login;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of CMS login with AD")
+public class AdminLoginResponse {
+
+ @Schema(description = "Indicate CMS login authorization success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Login successfully")
+ private String message;
+
+ @Schema(description = "Login Info of response")
+ private CmsLoginInfo loginInfo;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/login/AdminLogoutResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/login/AdminLogoutResponse.java
new file mode 100644
index 0000000..ca1add9
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/login/AdminLogoutResponse.java
@@ -0,0 +1,25 @@
+package com.sjmresorts.opsapp.admin.dtos.system.login;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of CMS logout")
+public class AdminLogoutResponse {
+
+ @Schema(description = "Indicate CMS logout success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Logout successfully")
+ private String message;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/login/CmsLoginInfo.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/login/CmsLoginInfo.java
new file mode 100644
index 0000000..25df173
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/login/CmsLoginInfo.java
@@ -0,0 +1,25 @@
+package com.sjmresorts.opsapp.admin.dtos.system.login;
+
+import com.sjmresorts.opsapp.admin.dtos.system.router.RouterPermissionDto;
+import com.sjmresorts.opsapp.admin.dtos.system.router.UserRouterDto;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.List;
+import java.util.Map;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+public class CmsLoginInfo {
+ private String accessToken;
+ private String refreshToken;
+ private String defaultRouter;
+ private LoginUserInfo loginUser;
+ private List userRouters;
+ private Map> userPermissions;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/router/RouterPermissionDto.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/router/RouterPermissionDto.java
new file mode 100644
index 0000000..cf3dba8
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/router/RouterPermissionDto.java
@@ -0,0 +1,16 @@
+package com.sjmresorts.opsapp.admin.dtos.system.router;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+public class RouterPermissionDto {
+ private String id;
+ private String routerId;
+ private String permissionName;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/router/UserRouterDto.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/router/UserRouterDto.java
new file mode 100644
index 0000000..8c2f0c5
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/router/UserRouterDto.java
@@ -0,0 +1,24 @@
+package com.sjmresorts.opsapp.admin.dtos.system.router;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.List;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+public class UserRouterDto {
+ private String parentId;
+ private String heading;
+ private String sectionTitle;
+ private String route;
+ private String svgIcon;
+ private String fontIcon;
+ private Integer sortNo;
+ private Integer status;
+ private List sub;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/user/CmsAppUserPageRequest.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/user/CmsAppUserPageRequest.java
new file mode 100644
index 0000000..637fb1d
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/user/CmsAppUserPageRequest.java
@@ -0,0 +1,14 @@
+package com.sjmresorts.opsapp.admin.dtos.system.user;
+
+import com.sjmresorts.opsapp.common.dtos.BasePageRequest;
+import lombok.Getter;
+import lombok.Setter;
+
+@Setter
+@Getter
+public class CmsAppUserPageRequest extends BasePageRequest {
+
+ private String searchAccount;
+ private String searchUserName;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/user/CmsUserDto.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/user/CmsUserDto.java
new file mode 100644
index 0000000..54d090f
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/user/CmsUserDto.java
@@ -0,0 +1,19 @@
+package com.sjmresorts.opsapp.admin.dtos.system.user;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.Instant;
+
+@Data
+@NoArgsConstructor
+public class CmsUserDto {
+
+ private Long userId;
+ private String userCode;
+ private String userName;
+ private String email;
+ private Integer status;
+ private Instant updatedAt;
+ private Instant createdAt;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/user/CmsUserInfoResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/user/CmsUserInfoResponse.java
new file mode 100644
index 0000000..7d55719
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/user/CmsUserInfoResponse.java
@@ -0,0 +1,17 @@
+package com.sjmresorts.opsapp.admin.dtos.system.user;
+
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import com.sjmresorts.opsapp.common.dtos.DataTableResult;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "User Info List")
+public class CmsUserInfoResponse extends BaseApiResponse {
+ private DataTableResult data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/user/GetCmsUserResponse.java b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/user/GetCmsUserResponse.java
new file mode 100644
index 0000000..ba55aa9
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/dtos/system/user/GetCmsUserResponse.java
@@ -0,0 +1,15 @@
+package com.sjmresorts.opsapp.admin.dtos.system.user;
+
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of getting CMS User")
+public class GetCmsUserResponse extends BaseApiResponse {
+ private CmsUserDto data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/mappers/ClientInfosMapper.java b/src/main/java/com/sjmresorts/opsapp/admin/mappers/ClientInfosMapper.java
new file mode 100644
index 0000000..a6633b7
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/mappers/ClientInfosMapper.java
@@ -0,0 +1,17 @@
+package com.sjmresorts.opsapp.admin.mappers;
+
+import com.sjmresorts.opsapp.admin.dtos.appsetting.ClientInfosDto;
+import com.sjmresorts.opsapp.authentication.models.ClientInfo;
+import org.mapstruct.Mapper;
+
+import java.util.List;
+
+@Mapper(componentModel = "spring")
+public interface ClientInfosMapper {
+
+ ClientInfosDto toClientInfosDto(ClientInfo device);
+
+ List toClientInfosDtoList(List content);
+
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/mappers/CmsLoginRouterMapper.java b/src/main/java/com/sjmresorts/opsapp/admin/mappers/CmsLoginRouterMapper.java
new file mode 100644
index 0000000..92648ac
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/mappers/CmsLoginRouterMapper.java
@@ -0,0 +1,41 @@
+package com.sjmresorts.opsapp.admin.mappers;
+
+import com.sjmresorts.opsapp.admin.dtos.system.router.RouterPermissionDto;
+import com.sjmresorts.opsapp.admin.dtos.system.router.UserRouterDto;
+import com.sjmresorts.opsapp.authentication.models.RouterPermission;
+import com.sjmresorts.opsapp.authentication.models.SysRouter;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Component
+@RequiredArgsConstructor
+public class CmsLoginRouterMapper {
+ private final SysRouterMapper routerMapper;
+ private final RouterPermissionMapper routerPermissionMapper;
+
+ public List toRouterDtoListWithChildren(List sysRouters) {
+ List routerDtos = new ArrayList();
+ routerMapper.toUserRouterDtoList(sysRouters);
+ for(SysRouter sysRouter:sysRouters){
+ UserRouterDto routerDto = routerMapper.toUserRouterDto(sysRouter);
+ if(sysRouter.getSub()!=null && !sysRouter.getSub().isEmpty()){
+ routerDto.setSub(toRouterDtoListWithChildren(sysRouter.getSub()));
+ }
+ routerDtos.add(routerDto);
+ }
+ return routerDtos;
+ }
+
+ public Map> toRouterPermissionMap(Map> userPermissions) {
+ Map> permissionMap = new HashMap>();
+ userPermissions.forEach((key, value) -> {
+ permissionMap.put(key, routerPermissionMapper.toRouterPermissionDtoList(value));
+ });
+ return permissionMap;
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/mappers/RouterPermissionMapper.java b/src/main/java/com/sjmresorts/opsapp/admin/mappers/RouterPermissionMapper.java
new file mode 100644
index 0000000..fc3df8c
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/mappers/RouterPermissionMapper.java
@@ -0,0 +1,15 @@
+package com.sjmresorts.opsapp.admin.mappers;
+
+import com.sjmresorts.opsapp.admin.dtos.system.router.RouterPermissionDto;
+import com.sjmresorts.opsapp.authentication.models.RouterPermission;
+import org.mapstruct.Mapper;
+
+import java.util.List;
+
+@Mapper(componentModel = "spring")
+public interface RouterPermissionMapper {
+
+ RouterPermissionDto toRouterPermissionDto(RouterPermission entity);
+
+ List toRouterPermissionDtoList(List routerPermissions);
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/mappers/SysRouterMapper.java b/src/main/java/com/sjmresorts/opsapp/admin/mappers/SysRouterMapper.java
new file mode 100644
index 0000000..2cede1e
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/mappers/SysRouterMapper.java
@@ -0,0 +1,17 @@
+package com.sjmresorts.opsapp.admin.mappers;
+
+import com.sjmresorts.opsapp.admin.dtos.system.router.UserRouterDto;
+import com.sjmresorts.opsapp.authentication.models.SysRouter;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+import java.util.List;
+
+@Mapper(componentModel = "spring")
+public interface SysRouterMapper {
+
+ @Mapping(target = "sub", ignore = true)
+ UserRouterDto toUserRouterDto(SysRouter sysRouter);
+
+ List toUserRouterDtoList(List sysRouters);
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/models/GroupPermissionObject.java b/src/main/java/com/sjmresorts/opsapp/admin/models/GroupPermissionObject.java
new file mode 100644
index 0000000..1f6d0fc
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/models/GroupPermissionObject.java
@@ -0,0 +1,17 @@
+package com.sjmresorts.opsapp.admin.models;
+
+import com.sjmresorts.opsapp.authentication.models.SysRouter;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class GroupPermissionObject {
+ private Long groupId;
+ private String groupName;
+ private List groupRouterSetting;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/repositories/RoleUsersRepository.java b/src/main/java/com/sjmresorts/opsapp/admin/repositories/RoleUsersRepository.java
new file mode 100644
index 0000000..dc39aee
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/repositories/RoleUsersRepository.java
@@ -0,0 +1,18 @@
+package com.sjmresorts.opsapp.admin.repositories;
+
+import com.sjmresorts.opsapp.authentication.models.RoleUsers;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface RoleUsersRepository extends JpaRepository {
+
+
+ List findAllByRoleId(Long roleId);
+
+ @Query(value = "select roleUser.roleId from RoleUsers roleUser where employeeId=?1 ")
+ List findRoleIdListByEmployeeId(String employeeId);
+}
\ No newline at end of file
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/repositories/RolesRepository.java b/src/main/java/com/sjmresorts/opsapp/admin/repositories/RolesRepository.java
new file mode 100644
index 0000000..14e4a71
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/repositories/RolesRepository.java
@@ -0,0 +1,23 @@
+package com.sjmresorts.opsapp.admin.repositories;
+
+import com.sjmresorts.opsapp.authentication.models.Role;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface RolesRepository extends JpaRepository {
+
+ Page findAll(Specification listSpec, Pageable pageable);
+
+ Role getRoleByRoleId(Long roleId);
+
+ @Query(value="select r.roleId from Role r where r.roleName = ?1")
+ List queryRoleIdByRoleName(String roleName);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/services/AdminDevicesService.java b/src/main/java/com/sjmresorts/opsapp/admin/services/AdminDevicesService.java
new file mode 100644
index 0000000..02c7719
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/services/AdminDevicesService.java
@@ -0,0 +1,108 @@
+package com.sjmresorts.opsapp.admin.services;
+
+import com.sjmresorts.opsapp.admin.dtos.appsetting.AdminDevicesInfoPageRequest;
+import com.sjmresorts.opsapp.admin.dtos.appsetting.ClientInfosDto;
+import com.sjmresorts.opsapp.admin.mappers.ClientInfosMapper;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.authentication.models.ClientInfo;
+import com.sjmresorts.opsapp.authentication.repositories.DeviceInfoRepository;
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import com.sjmresorts.opsapp.common.dtos.BasePageRequest;
+import com.sjmresorts.opsapp.common.dtos.DataTableResult;
+import com.sjmresorts.opsapp.common.utils.DaoUtil;
+import com.sjmresorts.opsapp.logging.models.AuditLog;
+import com.sjmresorts.opsapp.logging.services.AuditLogService;
+import jakarta.persistence.criteria.Predicate;
+import jakarta.validation.constraints.NotBlank;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+public class AdminDevicesService {
+
+ private final DeviceInfoRepository deviceInfoRepository;
+ private final AuditLogService auditLogService;
+ private final ClientInfosMapper clientInfosMapper;
+ private final DeviceInfoRepository deviceRepository;
+
+ public DataTableResult getDevicesInfoListByPage(AdminDevicesInfoPageRequest pageRequest) {
+
+ Specification listSpec = buildSpecificationByPageRequest(pageRequest);
+ int pageSize = pageRequest.getPageSize();
+ int pageStart = pageRequest.getStartRow() / pageSize;
+ Sort sort = DaoUtil.buildSortByString(pageRequest.getSortOrder(), "id");
+ Pageable pageable = PageRequest.of(pageStart, pageSize, sort);
+ Page resultPage = deviceInfoRepository.findAll(listSpec, pageable);
+ List resultList = clientInfosMapper.toClientInfosDtoList(resultPage.getContent());
+ DataTableResult dataResult = new DataTableResult<>();
+ dataResult.setTotalRecords(resultPage.getTotalElements());
+ dataResult.setPageSize(pageSize);
+ if (resultList == null) {
+ resultList = new ArrayList<>();
+ }
+ dataResult.setRowData(resultList);
+ return dataResult;
+ }
+
+ private Specification buildSpecificationByPageRequest(BasePageRequest pageRequest) {
+
+ return ((root, criteriaQuery, cb) -> {
+ List list = new ArrayList<>();
+// if (StringUtils.isNotBlank(pageRequest.getSearchUserName())) {
+// list.add(cb.like(cb.upper(root.get("userName")),
+// "%" + pageRequest.getSearchUserName().toUpperCase() + "%"));
+// }
+// if (StringUtils.isNotBlank(pageRequest.getSearchAccount())) {
+// list.add(cb.like(cb.upper(root.get("employeeId")),
+// "%" + pageRequest.getSearchAccount().toUpperCase() + "%"));
+// }
+ Predicate[] predicates = new Predicate[list.size()];
+ criteriaQuery.where(list.toArray(predicates));
+ return cb.and(list.toArray(predicates));
+ });
+ }
+
+ public BaseApiResponse updateDevice(ClientInfosDto deviceDto, LoginUserInfo loginUserInfo) {
+ BaseApiResponse result = new BaseApiResponse();
+ AuditLog auditLog = auditLogService.createAuditLog("Update Device", "Update Device from AdminDevicesService.updateDevice()", loginUserInfo);
+ ClientInfo device = deviceRepository.findById(deviceDto.getId()).orElse(null);
+ Instant currentTime = Instant.now();
+ if (device != null) {
+ auditLogService.setLogOldValue(auditLog, device);
+ device.setCasinoCode(deviceDto.getCasinoCode());
+ device.setUpdatedAt(currentTime);
+ deviceRepository.save(device);
+ auditLogService.setLogNewValue(auditLog, device);
+ auditLogService.saveAuditLog(auditLog);
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setMessage("Updated Successfully");
+ } else {
+ result.setSuccess(false);
+ result.setCode("400");
+ result.setMessage("User does not exists");
+ }
+ return result;
+ }
+
+
+ public ClientInfosDto queryDeviceById(@NotBlank long deviceId) {
+ ClientInfosDto deviceDto = null;
+ ClientInfo device = deviceRepository.findById(deviceId).orElse(null);
+
+ if (device != null) {
+ deviceDto = clientInfosMapper.toClientInfosDto(device);
+ }
+ return deviceDto;
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/services/AdminLoginService.java b/src/main/java/com/sjmresorts/opsapp/admin/services/AdminLoginService.java
new file mode 100644
index 0000000..98199d9
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/services/AdminLoginService.java
@@ -0,0 +1,163 @@
+package com.sjmresorts.opsapp.admin.services;
+
+import com.sjmresorts.opsapp.admin.dtos.system.login.CmsLoginInfo;
+import com.sjmresorts.opsapp.admin.mappers.CmsLoginRouterMapper;
+import com.sjmresorts.opsapp.admin.repositories.RoleUsersRepository;
+import com.sjmresorts.opsapp.admin.repositories.RolesRepository;
+import com.sjmresorts.opsapp.common.constants.AppConstants;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.authentication.dtos.RefreshTokenResponse;
+import com.sjmresorts.opsapp.authentication.mappers.UserMapper;
+import com.sjmresorts.opsapp.authentication.models.RouterPermission;
+import com.sjmresorts.opsapp.authentication.models.SysRouter;
+import com.sjmresorts.opsapp.authentication.models.User;
+import com.sjmresorts.opsapp.authentication.models.UserToken;
+import com.sjmresorts.opsapp.authentication.repositories.RouterPermissionRepository;
+import com.sjmresorts.opsapp.authentication.repositories.UserRepository;
+import com.sjmresorts.opsapp.authentication.services.RolePermissionService;
+import com.sjmresorts.opsapp.authentication.services.SysRouterService;
+import com.sjmresorts.opsapp.authentication.services.UserTokenService;
+import com.sjmresorts.opsapp.common.dtos.LDAP.ADAccountInfo;
+import com.sjmresorts.opsapp.common.utils.AESUtil;
+import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
+import com.sjmresorts.opsapp.common.utils.LDAPUtil;
+import jakarta.transaction.Transactional;
+import jakarta.validation.constraints.NotBlank;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.dao.OptimisticLockingFailureException;
+import org.springframework.retry.annotation.Backoff;
+import org.springframework.retry.annotation.Retryable;
+import org.springframework.stereotype.Service;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+@RequiredArgsConstructor
+public class AdminLoginService {
+
+ private final UserRepository userRepository;
+ private final RoleUsersRepository roleUsersRepository;
+ private final UserMapper userMapper;
+ private final CmsLoginRouterMapper cmsLoginRouterMapper;
+ private final SysRouterService sysRouterService;
+ private final RolePermissionService rolePermissionService;
+ private final UserTokenService userTokenService;
+ private final LDAPUtil ldapUtil;
+ private final RolesRepository rolesRepository;
+ private final RouterPermissionRepository routerPermissionRepository;
+
+ // -3=disabled, -2=locked, -1 or 0=incorrect loginId or password, 1=success
+ public int checkLoginByLoginIdAndPassword(String employeeId, String password) {
+ int checkPassed = -1;
+ if(AppConstants.SUPER_ADMIN_ID.equalsIgnoreCase(employeeId)) {
+ User user = userRepository.findByEmployeeId(employeeId).orElse(null);
+ if (user != null) {
+ if(user.getStatus()==null || user.getStatus()==0){
+ return -3; // user disabled by app
+ }else if(user.getStatus()==2) {
+ return -2; // user locked
+ }
+ String encryptPassword = AESUtil.sha256(password);
+ checkPassed = userRepository.countByEmployeeIdAndPassword(employeeId.toUpperCase(), encryptPassword);
+ }
+ }else{
+ //Check AD
+ boolean adResult = ldapUtil.authenticate(employeeId, password);
+ if(adResult) checkPassed = 1;
+ }
+ return checkPassed;
+ }
+
+ @Retryable(retryFor = OptimisticLockingFailureException.class,
+ maxAttempts = 3,
+ backoff = @Backoff(delay = 100))
+ @Transactional
+ public CmsLoginInfo getLoginUserInfoByEmployeeId(@NotBlank(message = "Employee Id must not be blank") String employeeId) {
+ CmsLoginInfo result = null;
+ if(AppConstants.SUPER_ADMIN_ID.equalsIgnoreCase(employeeId)) {
+ User user = userRepository.findByEmployeeId(employeeId).orElse(null);
+ if(user!=null){
+ user.setCmsLastLogin(Instant.now());
+ userRepository.save(user);
+ result = new CmsLoginInfo();
+ LoginUserInfo loginUser = userMapper.toLoginUser(user);
+ result.setLoginUser(loginUser);
+ Map> userPermissions = new HashMap>();
+ userPermissions = rolePermissionService.getFullRouterPermissions();
+ List routers = sysRouterService.getRoutersByPermission("0",userPermissions);
+ result.setUserRouters(cmsLoginRouterMapper.toRouterDtoListWithChildren(routers));
+ result.setUserPermissions(cmsLoginRouterMapper.toRouterPermissionMap(userPermissions));
+ }
+ }else{
+ ADAccountInfo adResult = ldapUtil.getAccountInfo(employeeId);
+ if(adResult!=null){
+ result = new CmsLoginInfo();
+ LoginUserInfo loginUser = new LoginUserInfo();
+ loginUser.setUserName(adResult.getDisplayName());
+ loginUser.setEmployeeId(employeeId);
+ loginUser.setEmail(adResult.getMail());
+ loginUser.setUserCode(employeeId);
+ result.setLoginUser(loginUser);
+ Map> userPermissions = new HashMap>();
+ List userRoleIds = getUserRoleIds(adResult.getDirectGroups());
+ userPermissions = rolePermissionService.getRouterPermissionsByGroupIds(userRoleIds);
+ List routers = sysRouterService.getRoutersByPermission("0",userPermissions);
+ result.setUserRouters(cmsLoginRouterMapper.toRouterDtoListWithChildren(routers));
+ result.setUserPermissions(cmsLoginRouterMapper.toRouterPermissionMap(userPermissions));
+ }
+ }
+
+ return result;
+ }
+
+ public void processLogout(LoginUserInfo loginUser) {
+ if(loginUser!=null){
+ userTokenService.clearUserTokenByType(AppConstants.CMS_API_ACCESS_TOKEN_TYPE,loginUser.getEmployeeId());
+ userTokenService.clearUserTokenByType(AppConstants.CMS_REFRESH_TOKEN_TYPE,loginUser.getEmployeeId());
+ }
+ }
+
+ public RefreshTokenResponse refreshToken(LoginUserInfo loginUser) {
+ RefreshTokenResponse result = new RefreshTokenResponse();
+ UserToken accessToken = null;
+ UserToken refreshToken = null;
+ if(loginUser!=null){
+ accessToken= JwtTokenUtil.generateTokenByLoginUser(AppConstants.CMS_API_ACCESS_TOKEN_TYPE,loginUser);
+ userTokenService.saveUserToken(accessToken);
+ refreshToken=JwtTokenUtil.generateTokenByLoginUser(AppConstants.CMS_REFRESH_TOKEN_TYPE,loginUser);
+ userTokenService.saveUserToken(refreshToken);
+ if(StringUtils.isNotBlank(loginUser.getTokenUuid())){
+ userTokenService.removeTokenByUniqueId(loginUser.getTokenUuid());
+ }
+ }
+
+ if(accessToken!=null){
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setAccessToken(accessToken.getTokenValue());
+ result.setRefreshToken(refreshToken.getTokenValue());
+ result.setMessage("Token renew successful.");
+ }else{
+ result.setSuccess(false);
+ result.setCode("200");
+ result.setMessage("Token failed.");
+ }
+ return result;
+ }
+
+ private List getUserRoleIds(List groupName) {
+ List roleIdList = new ArrayList<>();
+ if(groupName!=null&&!groupName.isEmpty()){
+ for(String roleName : groupName){
+ List roleIds = rolesRepository.queryRoleIdByRoleName(roleName);
+ roleIdList.addAll(roleIds);
+ }
+ }
+ return roleIdList;
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/services/AdminMenusService.java b/src/main/java/com/sjmresorts/opsapp/admin/services/AdminMenusService.java
new file mode 100644
index 0000000..7498e7c
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/services/AdminMenusService.java
@@ -0,0 +1,113 @@
+package com.sjmresorts.opsapp.admin.services;
+
+import com.sjmresorts.opsapp.admin.dtos.menus.AdminMenuItemDto;
+import com.sjmresorts.opsapp.admin.dtos.menus.AdminMenuStatusRequest;
+import com.sjmresorts.opsapp.admin.dtos.menus.AdminMenuStatusResponse;
+import com.sjmresorts.opsapp.admin.dtos.menus.AdminMenusListResponse;
+import com.sjmresorts.opsapp.admin.dtos.system.router.UserRouterDto;
+import com.sjmresorts.opsapp.admin.mappers.CmsLoginRouterMapper;
+import com.sjmresorts.opsapp.admin.repositories.RoleUsersRepository;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.authentication.models.RouterPermission;
+import com.sjmresorts.opsapp.authentication.models.SysRouter;
+import com.sjmresorts.opsapp.authentication.repositories.SysRouterRepository;
+import com.sjmresorts.opsapp.authentication.services.RolePermissionService;
+import com.sjmresorts.opsapp.authentication.services.SysRouterService;
+import com.sjmresorts.opsapp.common.constants.AppConstants;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Service
+@RequiredArgsConstructor
+public class AdminMenusService {
+
+ private static final Set LOCKED_ROUTER_IDS = Set.of(
+ "ESIGN_DASHBOARD",
+ "ESIGN_SYSTEM",
+ "ESIGN_SYS_USERS",
+ "ESIGN_SYS_ROLES",
+ "ESIGN_SYS_MENUS");
+
+ private final SysRouterRepository sysRouterRepository;
+ private final SysRouterService sysRouterService;
+ private final RolePermissionService rolePermissionService;
+ private final RoleUsersRepository roleUsersRepository;
+ private final CmsLoginRouterMapper cmsLoginRouterMapper;
+
+ public AdminMenusListResponse listMenus() {
+ AdminMenusListResponse response = new AdminMenusListResponse();
+ List routers = sysRouterRepository.queryAllRoutersOrderBySort();
+ for (SysRouter router : routers) {
+ AdminMenuItemDto item = new AdminMenuItemDto();
+ item.setId(router.getId());
+ item.setParentId(router.getParentId());
+ item.setHeading(router.getHeading());
+ item.setSectionTitle(router.getSectionTitle());
+ item.setRoute(router.getRoute());
+ item.setFontIcon(router.getFontIcon());
+ item.setSortNo(router.getSortNo());
+ item.setStatus(router.getStatus() == null ? 1 : router.getStatus());
+ item.setLocked(LOCKED_ROUTER_IDS.contains(router.getId()));
+ response.getData().add(item);
+ }
+ response.setSuccess(true);
+ response.setCode("200");
+ response.setMessage("ok");
+ return response;
+ }
+
+ @Transactional
+ public AdminMenuStatusResponse updateStatus(AdminMenuStatusRequest request, LoginUserInfo loginUser) {
+ AdminMenuStatusResponse response = new AdminMenuStatusResponse();
+ if (request == null || StringUtils.isBlank(request.getRouterId()) || request.getStatus() == null) {
+ response.setSuccess(false);
+ response.setCode("400");
+ response.setMessage("routerId and status required");
+ return response;
+ }
+ int status = request.getStatus() == 0 ? 0 : 1;
+ if (LOCKED_ROUTER_IDS.contains(request.getRouterId()) && status == 0) {
+ response.setSuccess(false);
+ response.setCode("400");
+ response.setMessage("This menu cannot be hidden");
+ return response;
+ }
+ SysRouter router = sysRouterRepository.findById(request.getRouterId()).orElse(null);
+ if (router == null) {
+ response.setSuccess(false);
+ response.setCode("404");
+ response.setMessage("menu not found");
+ return response;
+ }
+ router.setStatus(status);
+ sysRouterRepository.save(router);
+
+ response.setUserRouters(buildUserRouters(loginUser));
+ response.setSuccess(true);
+ response.setCode("200");
+ response.setMessage("saved");
+ return response;
+ }
+
+ private List buildUserRouters(LoginUserInfo loginUser) {
+ if (loginUser == null || StringUtils.isBlank(loginUser.getEmployeeId())) {
+ return List.of();
+ }
+ Map> userPermissions;
+ if (AppConstants.SUPER_ADMIN_ID.equalsIgnoreCase(loginUser.getEmployeeId())) {
+ userPermissions = rolePermissionService.getFullRouterPermissions();
+ } else {
+ List roleIds = roleUsersRepository.findRoleIdListByEmployeeId(loginUser.getEmployeeId());
+ userPermissions = rolePermissionService.getRouterPermissionsByGroupIds(
+ roleIds != null ? roleIds : List.of());
+ }
+ List routers = sysRouterService.getRoutersByPermission("0", userPermissions);
+ return cmsLoginRouterMapper.toRouterDtoListWithChildren(routers);
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/services/AdminPermissionService.java b/src/main/java/com/sjmresorts/opsapp/admin/services/AdminPermissionService.java
new file mode 100644
index 0000000..cca62d0
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/services/AdminPermissionService.java
@@ -0,0 +1,109 @@
+package com.sjmresorts.opsapp.admin.services;
+
+
+import com.sjmresorts.opsapp.admin.dtos.permissions.AdminAssignGroupPermissionRequest;
+import com.sjmresorts.opsapp.admin.dtos.permissions.AdminAssignGroupPermissionResponse;
+import com.sjmresorts.opsapp.admin.dtos.permissions.AdminListPermissionSettingRequest;
+import com.sjmresorts.opsapp.admin.dtos.permissions.AdminListPermissionSettingResponse;
+import com.sjmresorts.opsapp.admin.models.GroupPermissionObject;
+import com.sjmresorts.opsapp.authentication.models.*;
+import com.sjmresorts.opsapp.authentication.repositories.RolePermissionRepository;
+import com.sjmresorts.opsapp.authentication.repositories.RouterPermissionRepository;
+import com.sjmresorts.opsapp.authentication.repositories.SysRouterRepository;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Service
+@RequiredArgsConstructor
+public class AdminPermissionService {
+ private final SysRouterRepository sysRouterRepository;
+ private final RouterPermissionRepository routerPermissionRepository;
+ private final RolePermissionRepository rolePermissionRepository;
+
+
+ public AdminListPermissionSettingResponse getPermissionById(AdminListPermissionSettingRequest request) {
+ AdminListPermissionSettingResponse result = new AdminListPermissionSettingResponse();
+ GroupPermissionObject newObject = new GroupPermissionObject();
+
+// 获取路由信息,路由权限,权限组权限
+ List listRouter = sysRouterRepository.getAllChildrenRoutes();
+ List listPermission = routerPermissionRepository.findAll();
+ List listRolePermission = rolePermissionRepository.getAllPermissionByRoleId(request.getRoleId());
+// 判断权限组是否有权限,并将selectAble属性加入返回数据
+ if (listRolePermission.size() > 0) {
+ for(int i=0; i permissionToDelete = rolePermissionRepository.getAllPermissionByRoleId(request.getRoleId());
+ if(request.getAssignRouterOperation()!=null &&request.getAssignRouterOperation().size()>0){
+ List assignRouterOperation = request.getAssignRouterOperation();
+ ListsaveData = new ArrayList<>();
+ for(int i =0;i getAllRoles() {
+ return rolesRepository.findAll();
+ }
+
+ public DataTableResult getRoleInfoListByPage(AdminGetRolesRequest pageRequest) {
+
+ Specification listSpec = buildSpecificationByPageRequest(pageRequest);
+ int pageSize = pageRequest.getPageSize();
+ int pageStart = pageRequest.getStartRow() / pageSize;
+ Sort sort = DaoUtil.buildSortByString(pageRequest.getSortOrder(), "roleId");
+ Pageable pageable = PageRequest.of(pageStart, pageSize, sort);
+ Page resultPage = rolesRepository.findAll(listSpec,pageable);
+ List resultList = roleMapper.toAdminRoleDto(resultPage.getContent());
+ DataTableResult dataResult = new DataTableResult<>();
+ dataResult.setTotalRecords(resultPage.getTotalElements());
+ dataResult.setPageSize(pageSize);
+ if (resultList == null) {
+ resultList = new ArrayList<>();
+ }
+ dataResult.setRowData(resultList);
+ return dataResult;
+ }
+
+
+ public BaseApiResponse createRole(AdminCreateRoleRequest request, LoginUserInfo loginUserInfo) {
+ BaseApiResponse result = new BaseApiResponse();
+ AuditLog auditLog = auditLogService.createAuditLog("Create Role", "Create a new Role from AdminRolesService.createRole()", loginUserInfo);
+ Role newRole = new Role();
+ newRole.setRoleName(request.getRoleName());
+ auditLogService.setLogOldValue(auditLog,null);
+ rolesRepository.save(newRole);
+ auditLogService.setLogNewValue(auditLog,newRole);
+ auditLogService.saveAuditLog(auditLog);
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setMessage("Created Successfully");
+ return result;
+ }
+
+ public BaseApiResponse deleteRole(AdminDeleteRoleRequest request, LoginUserInfo loginUserInfo) {
+ BaseApiResponse result = new BaseApiResponse();
+ AuditLog auditLog = auditLogService.createAuditLog("Delete Role", "Delete a Role from AdminRolesService.deleteRole()", loginUserInfo);
+ auditLogService.setLogOldValue(auditLog,request.getRoleName());
+ rolesRepository.deleteById(request.getRoleId());
+ auditLogService.setLogNewValue(auditLog,"");
+ auditLogService.saveAuditLog(auditLog);
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setMessage("Created Successfully");
+ return result;
+ }
+
+ public BaseApiResponse modifyRole(AdminModifyRoleRequest request, LoginUserInfo loginUserInfo) {
+ BaseApiResponse result = new BaseApiResponse();
+ AuditLog auditLog = auditLogService.createAuditLog("Modify Role", "Modify a Role from AdminRolesService.deleteRole()", loginUserInfo);
+ auditLogService.setLogOldValue(auditLog,request.getRoleName());
+ Role oRole = rolesRepository.getRoleByRoleId((request.getRoleId()));
+ oRole.setRoleName(request.getRoleName());
+ rolesRepository.save(oRole);
+ auditLogService.setLogNewValue(auditLog,"");
+ auditLogService.saveAuditLog(auditLog);
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setMessage("Modify Successfully");
+ return result;
+ }
+
+ public AdminGetRoleByIdResponse getRoleById(AdminGetRoleByIdRequest request) {
+ AdminGetRoleByIdResponse result = new AdminGetRoleByIdResponse();
+ Role getRole = rolesRepository.findById(request.getRoleId()).orElse(null);
+ result.setData(getRole);
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setMessage("Get Role By Id Successfully");
+ return result;
+ }
+
+
+ private Specification buildSpecificationByPageRequest(AdminGetRolesRequest pageRequest) {
+
+ return ((root, criteriaQuery, cb) -> {
+ List list = new ArrayList<>();
+ if (StringUtils.isNotBlank(pageRequest.getSearchRoleName())) {
+ list.add(cb.like(cb.upper(root.get("roleName")),
+ "%" + pageRequest.getSearchRoleName().toUpperCase() + "%"));
+ }
+ Predicate[] predicates = new Predicate[list.size()];
+ criteriaQuery.where(list.toArray(predicates));
+ return cb.and(list.toArray(predicates));
+ });
+ }
+
+ public boolean updateGroupAssignUser(AdminRoleAssignUsersRequest request, LoginUserInfo loginUserInfo) {
+ Long roleId = request.getRoleId();
+ List employeeIdList = request.getEmployeeIds();
+ List dataToDelete =roleUsersRepository.findAllByRoleId(roleId);
+ AuditLog auditLog = auditLogService.createAuditLog("Update Group Assign Users", "Update Group Assign Users from AdminRolesService.updateGroupAssignUser()", loginUserInfo);
+ auditLogService.setLogOldValue(auditLog,dataToDelete);
+ if(employeeIdList!=null) {
+ List saveData = new ArrayList<>();
+ for(int i=0; i0) {
+ roleUsersRepository.saveAll(saveData);
+ }
+ }
+ if(dataToDelete.size()>0) {
+ roleUsersRepository.deleteAll(dataToDelete);
+ }
+ auditLogService.setLogNewValue(auditLog,request);
+ auditLogService.saveAuditLog(auditLog);
+ return true;
+ }
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/admin/services/AdminUserService.java b/src/main/java/com/sjmresorts/opsapp/admin/services/AdminUserService.java
new file mode 100644
index 0000000..3fa96f9
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/admin/services/AdminUserService.java
@@ -0,0 +1,180 @@
+package com.sjmresorts.opsapp.admin.services;
+
+import com.sjmresorts.opsapp.admin.dtos.roleUsers.AdminGetRoleUsersRequest;
+import com.sjmresorts.opsapp.admin.dtos.system.user.CmsAppUserPageRequest;
+import com.sjmresorts.opsapp.admin.dtos.system.user.CmsUserDto;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.authentication.mappers.UserMapper;
+import com.sjmresorts.opsapp.authentication.models.RoleUsers;
+import com.sjmresorts.opsapp.authentication.models.User;
+import com.sjmresorts.opsapp.authentication.repositories.UserRepository;
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import com.sjmresorts.opsapp.common.dtos.DataTableResult;
+import com.sjmresorts.opsapp.common.utils.DaoUtil;
+import com.sjmresorts.opsapp.logging.models.AuditLog;
+import com.sjmresorts.opsapp.logging.services.AuditLogService;
+import jakarta.persistence.criteria.*;
+import jakarta.validation.constraints.NotBlank;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+public class AdminUserService {
+
+ private final UserRepository userRepository;
+ private final AuditLogService auditLogService;
+ private final UserMapper userMapper;
+
+ public DataTableResult getUserInfoListByPage(CmsAppUserPageRequest pageRequest) {
+
+ Specification listSpec = buildSpecificationByPageRequest(pageRequest);
+ int pageSize = pageRequest.getPageSize();
+ int pageStart = pageRequest.getStartRow() / pageSize;
+ Sort sort = DaoUtil.buildSortByString(pageRequest.getSortOrder(), "userId");
+ Pageable pageable = PageRequest.of(pageStart, pageSize, sort);
+ Page resultPage = userRepository.findAll(listSpec, pageable);
+ List resultList = userMapper.toCmsUserDtoList(resultPage.getContent());
+ DataTableResult dataResult = new DataTableResult<>();
+ dataResult.setTotalRecords(resultPage.getTotalElements());
+ dataResult.setPageSize(pageSize);
+ if (resultList == null) {
+ resultList = new ArrayList<>();
+ }
+ dataResult.setRowData(resultList);
+ return dataResult;
+ }
+
+ private Specification buildSpecificationByPageRequest(CmsAppUserPageRequest pageRequest) {
+
+ return ((root, criteriaQuery, cb) -> {
+ List list = new ArrayList<>();
+ if (StringUtils.isNotBlank(pageRequest.getSearchUserName())) {
+ list.add(cb.like(cb.upper(root.get("userName")),
+ "%" + pageRequest.getSearchUserName().toUpperCase() + "%"));
+ }
+ if (StringUtils.isNotBlank(pageRequest.getSearchAccount())) {
+ list.add(cb.like(cb.upper(root.get("employeeId")),
+ "%" + pageRequest.getSearchAccount().toUpperCase() + "%"));
+ }
+ Predicate[] predicates = new Predicate[list.size()];
+ criteriaQuery.where(list.toArray(predicates));
+ return cb.and(list.toArray(predicates));
+ });
+ }
+
+ public List listUser(AdminGetRoleUsersRequest request, String searchType) {
+ Specification listSpec = buildSpecificationByRequest(request,searchType);
+ Sort sort = DaoUtil.buildSortByString(request.getSortOrder(), "employeeId");
+ List resultList = userRepository.findAll(listSpec,sort);
+ return resultList;
+ }
+
+ private Specification buildSpecificationByRequest(AdminGetRoleUsersRequest request, String searchType) {
+ Specification SysUserpec = new Specification() {
+ @Override
+ public Predicate toPredicate(Root root, CriteriaQuery> criteriaQuery, CriteriaBuilder cb) {
+ List list = new ArrayList<>();
+ if (StringUtils.equals(searchType, "USER_ROLE") && request.getSearchRoleId() != null) {
+ Subquery groupSubQuery = criteriaQuery.subquery(String.class);
+ Root subQueryRoot = groupSubQuery.from(RoleUsers.class);
+ groupSubQuery.select(subQueryRoot.get("employeeId"));
+ List subQueryPredicates = new ArrayList<>();
+ subQueryPredicates.add(cb.equal(subQueryRoot.get("roleId"), request.getSearchRoleId()));
+ groupSubQuery.where(subQueryPredicates.toArray(new Predicate[] {}));
+ list.add(cb.in(root.get("employeeId")).value(groupSubQuery));
+ } else if (StringUtils.equals(searchType, "UNASSIGN_USER")) {
+ Subquery groupSubQuery = criteriaQuery.subquery(Long.class);
+ Root subQueryRoot = groupSubQuery.from(RoleUsers.class);
+ groupSubQuery.select(cb.literal(1L));
+ List subQueryPredicates = new ArrayList<>();
+ subQueryPredicates.add(cb.equal(subQueryRoot.get("employeeId"), root.get("employeeId")));
+ groupSubQuery.where(subQueryPredicates.toArray(new Predicate[] {}));
+ list.add(cb.not(cb.exists(groupSubQuery)));
+ }
+ Predicate[] predicates = new Predicate[list.size()];
+ criteriaQuery.where(list.toArray(predicates));
+ return cb.and(list.toArray(predicates));
+ }
+ };
+ return SysUserpec;
+ }
+
+
+
+ public BaseApiResponse createUser(CmsUserDto userDto, LoginUserInfo loginUserInfo) {
+ BaseApiResponse result = new BaseApiResponse();
+ AuditLog auditLog = auditLogService.createAuditLog("Create User", "Create a new User from AdminUserService.createUser()", loginUserInfo);
+ User user = userMapper.toUserEntity(userDto);
+ user.setCasinoCode("");
+ auditLogService.setLogOldValue(auditLog, null);
+ userRepository.save(user);
+ auditLogService.setLogNewValue(auditLog, user);
+ auditLogService.saveAuditLog(auditLog);
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setMessage("Created Successfully");
+ return result;
+ }
+
+ public BaseApiResponse updateUser(CmsUserDto userDto, LoginUserInfo loginUserInfo) {
+ BaseApiResponse result = new BaseApiResponse();
+ AuditLog auditLog = auditLogService.createAuditLog("Update User", "Update User from AdminUserService.updateUser()", loginUserInfo);
+ User user = userRepository.findById(userDto.getUserId()).orElse(null);
+ if (user != null) {
+ auditLogService.setLogOldValue(auditLog, user);
+ userMapper.updateUserFromCmsUserDto(userDto, user);
+ userRepository.save(user);
+ auditLogService.setLogNewValue(auditLog, user);
+ auditLogService.saveAuditLog(auditLog);
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setMessage("Updated Successfully");
+ } else {
+ result.setSuccess(false);
+ result.setCode("400");
+ result.setMessage("User does not exists");
+ }
+ return result;
+ }
+
+ public BaseApiResponse updateStatus(CmsUserDto userDto, LoginUserInfo loginUserInfo) {
+ BaseApiResponse result = new BaseApiResponse();
+ AuditLog auditLog = auditLogService.createAuditLog("Update User Status", "Update User status from AdminUserService.updateStatus()", loginUserInfo);
+ User user = userRepository.findById(userDto.getUserId()).orElse(null);
+ if (user != null) {
+ auditLogService.setLogOldValue(auditLog, user);
+ user.setStatus(userDto.getStatus());
+ userRepository.save(user);
+ auditLogService.setLogNewValue(auditLog, user);
+ auditLogService.saveAuditLog(auditLog);
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setMessage("Status Updated Successfully");
+ } else {
+ result.setSuccess(false);
+ result.setCode("400");
+ result.setMessage("User does not exists");
+ }
+ return result;
+ }
+
+ public CmsUserDto queryCmsUserById(@NotBlank long userId) {
+ CmsUserDto userDto = null;
+ User user = userRepository.findById(userId).orElse(null);
+
+ if (user != null) {
+ userDto = userMapper.toCmsUserDto(user);
+ }
+ return userDto;
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/controllers/MobileAuthorizationController.java b/src/main/java/com/sjmresorts/opsapp/authentication/controllers/MobileAuthorizationController.java
new file mode 100644
index 0000000..b73b420
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/controllers/MobileAuthorizationController.java
@@ -0,0 +1,147 @@
+package com.sjmresorts.opsapp.authentication.controllers;
+
+import com.sjmresorts.opsapp.authentication.dtos.*;
+import com.sjmresorts.opsapp.authentication.services.AdAuthorizeService;
+import com.sjmresorts.opsapp.authentication.services.LoginService;
+import com.sjmresorts.opsapp.common.constants.AppConstants;
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@Tag(name = "Authorization", description = "APIs relate with ad login authorization")
+@RestController
+@RequestMapping("/api/v1/authorization")
+@CrossOrigin(origins = "*")
+public class MobileAuthorizationController {
+ @Resource
+ private LoginService loginService;
+ @Resource
+ private AdAuthorizeService adAuthService;
+
+ @Operation(
+ summary = "appLogout",
+ description = "API for app logout process"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "App logout successful")
+ })
+ @PostMapping(value="/logout",produces = "application/json;charset=UTF-8")
+ public ResponseEntity deviceLogin(
+ HttpServletRequest request) {
+
+ AppLogoutResponse result = new AppLogoutResponse();
+ LoginUserInfo loginUser = JwtTokenUtil.parseUserFromRequest(request);
+ if(loginUser!=null){
+ loginService.processLogout(loginUser);
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setMessage("Logout successful.");
+ return ResponseEntity.status(HttpStatus.OK).body(result);
+ }else{
+ result.setSuccess(false);
+ result.setCode("401");
+ result.setMessage("Unauthorized logout");
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(result);
+ }
+ }
+
+
+ @Operation(
+ summary = "Register Pits info for tracking",
+ description = "API for location tracking registration"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Register Pits successful"),
+ @ApiResponse(responseCode = "400", description = "Invalid request data")
+ })
+ @PostMapping(value="/register_track_pits",produces = "application/json;charset=UTF-8")
+ public ResponseEntity registerTrackScope(
+ HttpServletRequest request,
+ @Parameter(description = "Register request for pits tracking", required = true) @Valid @RequestBody RegisterTrackInfoRequest requestData) {
+ RegisterTrackInfoResponse responseData = loginService.registerTrackPits(requestData.getData());
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }
+
+ @Operation(
+ summary = "Third Party Authorization",
+ description = "API to retrieve access token for third party caller, such as Service Button"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Access Token generated successful"),
+ @ApiResponse(responseCode = "401", description = "Authorization key not math")
+ })
+ @PostMapping(value="/getAccessToken",produces = "application/json;charset=UTF-8")
+ public ResponseEntity getApiAccessToken(
+ HttpServletRequest request,
+ @Parameter(description = "Third party requestor info", required = true) @Valid @RequestBody GetAuthTokenRequest requestData) {
+ String authorizationKey = request.getHeader("Authorization");
+ if(authorizationKey!=null && authorizationKey.equals(JwtTokenUtil.getThirdPartySecretKey())){
+ GetAuthTokenResponse responseData = loginService.assignAccessToken(AppConstants.THIRD_PARTY_TOKEN_TYPE,requestData.getData());
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }else{
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
+ }
+ }
+
+ @Operation(
+ summary = "Renew API token by non-expired token",
+ description = "API to renew access token"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Access Token refreshed successful")
+ })
+ @PostMapping(value="/refreshToken",produces = "application/json;charset=UTF-8")
+ public ResponseEntity refreshToken(
+ HttpServletRequest request,
+ HttpServletResponse response) {
+ RefreshTokenResponse responseData = new RefreshTokenResponse();
+ LoginUserInfo loginUser = JwtTokenUtil.parseUserFromRequest(request);
+ boolean isRenewRefreshToken = (!"false".equalsIgnoreCase(request.getParameter("isRenewRefreshToken")));
+ if(loginUser!=null){
+ responseData = loginService.refreshToken(loginUser,isRenewRefreshToken);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }else{
+ responseData.setSuccess(false);
+ responseData.setCode("401");
+ responseData.setMessage("Unauthorized operation");
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(responseData);
+ }
+ }
+
+ @Operation(
+ summary = "Heart-beat API",
+ description = "API to register last heart-beat time for login user"
+ )
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "Access Token refreshed successful")
+ })
+ @GetMapping(value="/appHeartBeat")
+ public ResponseEntity sendHeartBeat(
+ HttpServletRequest request,
+ HttpServletResponse response) {
+ LoginUserInfo loginUser = JwtTokenUtil.parseUserFromRequest(request);
+ BaseApiResponse responseData = new BaseApiResponse();
+ if(loginUser!=null){
+ boolean registerSuccess = loginService.registerHeartBeat(loginUser);
+ responseData.setSuccess(registerSuccess);
+ return ResponseEntity.status(HttpStatus.OK).body(responseData);
+ }else{
+ responseData.setSuccess(false);
+ responseData.setCode("401");
+ responseData.setMessage("Unauthorized operation");
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(responseData);
+ }
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdAuthorizeResponse.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdAuthorizeResponse.java
new file mode 100644
index 0000000..4614268
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdAuthorizeResponse.java
@@ -0,0 +1,19 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+public class AdAuthorizeResponse {
+
+ private boolean success=false;
+ private String code;
+ private String message;
+ private AdUserInfo userInfo;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdLoginData.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdLoginData.java
new file mode 100644
index 0000000..cd6e0c1
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdLoginData.java
@@ -0,0 +1,42 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Data field of AdLoginRequest")
+public class AdLoginData {
+ @Schema(description = "AD Account of Employee", example = "XXX")
+ @NotBlank(message = "Employee Id must not be blank")
+ private String employeeId;
+
+ @Schema(description = "Password of AD Account", nullable = false)
+ @NotBlank(message = "Password must not be blank")
+ private String password;
+
+ @Schema(description = "UUID of user login device")
+ private String deviceId;
+
+ @Schema(description = "FCM token of current login device")
+ private String deviceToken;
+
+ @Schema(description = "Device name of user login")
+ private String deviceName;
+
+ @Schema(description = "Device version of user login")
+ private String deviceVersion;
+
+ @Schema(description = "App version of current using")
+ private String appVersion;
+
+ @Schema(description = "Is Mockup Login")
+ private Boolean isMock;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdLoginRequest.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdLoginRequest.java
new file mode 100644
index 0000000..0db4425
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdLoginRequest.java
@@ -0,0 +1,25 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+import com.sjmresorts.opsapp.common.dtos.SchemaEntry;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Request info of AD login with password")
+public class AdLoginRequest {
+ @Schema(description = "Schema field of request")
+ @NotNull(message = "Schema attribute must not be null")
+ private SchemaEntry schema;
+
+ @Schema(description = "Data field of request")
+ @NotNull(message = "Data attribute must not be null")
+ private AdLoginData data;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdLoginResponse.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdLoginResponse.java
new file mode 100644
index 0000000..d2f9bfa
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdLoginResponse.java
@@ -0,0 +1,47 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.Set;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of AD login with password")
+public class AdLoginResponse {
+
+ @Schema(description = "Indicate AD authorization success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Login successfully")
+ private String message;
+
+ @Schema(description = "JWT token assign for login user")
+ private String accessToken;
+
+ @Schema(description = "JWT refresh token assign for login user")
+ private String refreshToken;
+
+ @Schema(description = "AD account of login user")
+ private String employeeId;
+
+ @Schema(description = "User name of login user")
+ private String userName;
+
+ @Schema(description = "User Id of login user")
+ private Long userId;
+
+ @Schema(description = "Available access property of login user")
+ private String casinoCode;
+
+ @Schema(description = "Permission Group of login user")
+ private Set permissionGroup;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdUserInfo.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdUserInfo.java
new file mode 100644
index 0000000..e93d4a9
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AdUserInfo.java
@@ -0,0 +1,20 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+@Data
+@Getter
+@Setter
+public class AdUserInfo {
+
+ private String employeeId;
+ private String userName;
+ private List adGroup;
+ private String role;
+ private String email;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AppLogoutResponse.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AppLogoutResponse.java
new file mode 100644
index 0000000..da30f2d
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/AppLogoutResponse.java
@@ -0,0 +1,25 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of App logout")
+public class AppLogoutResponse {
+
+ @Schema(description = "Indicate app logout success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Device logout successfully")
+ private String message;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/DeviceLoginData.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/DeviceLoginData.java
new file mode 100644
index 0000000..f9864d4
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/DeviceLoginData.java
@@ -0,0 +1,45 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Data field of DeviceLoginRequest")
+public class DeviceLoginData {
+ @Schema(description = "Security content provide by SJM launcher")
+ private String securityContent;
+
+ @Schema(description = "UUID of user login device")
+ private String deviceId;
+
+ @Schema(description = "FCM token of current login device")
+ private String deviceToken;
+
+ @Schema(description = "Device name of user login")
+ private String deviceName;
+
+ @Schema(description = "Device version of user login")
+ private String deviceVersion;
+
+ @Schema(description = "Employee ID from Launcher", example = "000001")
+ @NotBlank(message = "Employee Id must not be blank")
+ private String employeeId;
+
+ @Schema(description = "User Name from Launcher", example = "XXX")
+ @NotBlank(message = "User Name must not be blank")
+ private String userName;
+
+ @Schema(description = "Property code from Launcher", example = "GLC")
+ private String casinoCode;
+
+ @Schema(description = "App version of current using")
+ private String appVersion;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/DeviceLoginRequest.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/DeviceLoginRequest.java
new file mode 100644
index 0000000..07ceefd
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/DeviceLoginRequest.java
@@ -0,0 +1,21 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+import com.sjmresorts.opsapp.common.dtos.SchemaEntry;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Login request info of SJM launcher")
+public class DeviceLoginRequest {
+ @Schema(description = "Schema field of request")
+ private SchemaEntry schema;
+
+ @Schema(description = "Data field of request")
+ private DeviceLoginData data;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/DeviceLoginResponse.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/DeviceLoginResponse.java
new file mode 100644
index 0000000..650676d
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/DeviceLoginResponse.java
@@ -0,0 +1,47 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.Set;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of SJM launcher login with AD content")
+public class DeviceLoginResponse {
+
+ @Schema(description = "Indicate AD authorization success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Device login successfully")
+ private String message;
+
+ @Schema(description = "JWT API token assign for this login")
+ private String accessToken;
+
+ @Schema(description = "JWT Refresh token assign for this login")
+ private String refreshToken;
+
+ @Schema(description = "AD account of login user")
+ private String employeeId;
+
+ @Schema(description = "User name of login user")
+ private String userName;
+
+ @Schema(description = "User Id of login user")
+ private Long userId;
+
+ @Schema(description = "Available access property of login user")
+ private String casinoCode;
+
+ @Schema(description = "Permission Group of login user")
+ private Set permissionGroup;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/GetAuthTokenData.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/GetAuthTokenData.java
new file mode 100644
index 0000000..708b1d2
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/GetAuthTokenData.java
@@ -0,0 +1,21 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Data field of GetAuthToken request")
+public class GetAuthTokenData {
+ @Schema(description = "Requester ID of notification API caller", example = "ELO_XXXX")
+ @NotBlank(message = "Requester Id must not be blank")
+ private String requesterId;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/GetAuthTokenRequest.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/GetAuthTokenRequest.java
new file mode 100644
index 0000000..864500a
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/GetAuthTokenRequest.java
@@ -0,0 +1,25 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+import com.sjmresorts.opsapp.common.dtos.SchemaEntry;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Request to get notification API access token")
+public class GetAuthTokenRequest {
+ @Schema(description = "Schema field of request")
+ @NotNull(message = "Schema attribute must not be null")
+ private SchemaEntry schema;
+
+ @Schema(description = "Data field of request")
+ @NotNull(message = "Data attribute must not be null")
+ private GetAuthTokenData data;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/GetAuthTokenResponse.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/GetAuthTokenResponse.java
new file mode 100644
index 0000000..ce4dd11
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/GetAuthTokenResponse.java
@@ -0,0 +1,28 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of notification API access token")
+public class GetAuthTokenResponse {
+
+ @Schema(description = "Indicate access token generated success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Access token generated successfully")
+ private String message;
+
+ @Schema(description = "JWT token assign for service button")
+ private String accessToken;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/LoginUserInfo.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/LoginUserInfo.java
new file mode 100644
index 0000000..659714c
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/LoginUserInfo.java
@@ -0,0 +1,21 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+public class LoginUserInfo {
+ private Long userId;
+ private String employeeId;
+ private String userCode;
+ private String userName;
+ private String adGroup;
+ private String role;
+ private String email;
+ private String loginSource;
+ private String tokenUuid;
+ private String appVersion;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/RefreshTokenResponse.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/RefreshTokenResponse.java
new file mode 100644
index 0000000..1240abd
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/RefreshTokenResponse.java
@@ -0,0 +1,21 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of refreshing App access token")
+public class RefreshTokenResponse extends BaseApiResponse {
+
+ @Schema(description = "New API JWT token generated")
+ private String accessToken;
+
+ @Schema(description = "New Refresh JWT token generated")
+ private String refreshToken;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/RegisterTrackInfoData.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/RegisterTrackInfoData.java
new file mode 100644
index 0000000..6356cbb
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/RegisterTrackInfoData.java
@@ -0,0 +1,26 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.List;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Data field of RegisterTrackInfoRequest")
+public class RegisterTrackInfoData {
+ @Schema(description = "Employee ID of current user")
+ private String employeeId;
+
+ @Schema(description = "Casino code of user selected")
+ private String casinoCode;
+
+ @Schema(description = "Pits code of user selected")
+ private List pitCodes;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/RegisterTrackInfoRequest.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/RegisterTrackInfoRequest.java
new file mode 100644
index 0000000..508ec63
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/RegisterTrackInfoRequest.java
@@ -0,0 +1,22 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+import com.sjmresorts.opsapp.common.dtos.SchemaEntry;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Request info of Track pits registeration")
+public class RegisterTrackInfoRequest {
+ @Schema(description = "Schema field of request")
+ private SchemaEntry schema;
+
+ @Schema(description = "Data field of request")
+ private RegisterTrackInfoData data;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/dtos/RegisterTrackInfoResponse.java b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/RegisterTrackInfoResponse.java
new file mode 100644
index 0000000..9c94f70
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/dtos/RegisterTrackInfoResponse.java
@@ -0,0 +1,26 @@
+package com.sjmresorts.opsapp.authentication.dtos;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Response info of Track pits registeration")
+public class RegisterTrackInfoResponse {
+
+ @Schema(description = "Indicate success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result",example="Pits are registered successfully")
+ private String message;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/filters/JwtAuthFilter.java b/src/main/java/com/sjmresorts/opsapp/authentication/filters/JwtAuthFilter.java
new file mode 100644
index 0000000..b710b61
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/filters/JwtAuthFilter.java
@@ -0,0 +1,111 @@
+package com.sjmresorts.opsapp.authentication.filters;
+
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.exceptions.TokenExpiredException;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.authentication.services.UserTokenService;
+import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
+import com.sjmresorts.opsapp.common.utils.NetworkUtil;
+import jakarta.annotation.Resource;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.NonNull;
+import lombok.Setter;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+import java.util.List;
+
+public class JwtAuthFilter extends OncePerRequestFilter {
+
+ private final AntPathMatcher pathMatcher = new AntPathMatcher();
+ private final Logger logger = LoggerFactory.getLogger(JwtAuthFilter.class);
+ @Setter
+ private List excludePatterns;
+
+ @Setter
+ private List verifyAppVersionList;
+
+ @Setter
+ @Resource
+ private UserTokenService userTokenService;
+
+ @Override
+ protected void doFilterInternal(
+ @NonNull HttpServletRequest request,
+ @NonNull HttpServletResponse response,
+ @NonNull FilterChain filterChain
+ ) throws ServletException, IOException{
+
+ NetworkUtil.checkAndSetAllowOrigin(request,response);
+ NetworkUtil.setSecurityHeaders(request,response);
+ String token = request.getHeader("Authorization");
+ String uri = request.getRequestURI();
+ if(StringUtils.isNotBlank(token) && token.startsWith("Bearer ")){
+ token = token.substring(7);
+ }
+ if(StringUtils.isNotBlank(token)) {
+ JWTVerifier jwtVerifier = JwtTokenUtil.getJwtVerifier();
+ try {
+ jwtVerifier.verify(token);
+ }catch (TokenExpiredException e) {
+ sendErrorResponse(response,HttpServletResponse.SC_UNAUTHORIZED,"Token Expired");
+ request.setAttribute("AUTH_EXCEPTION","Token Expired");
+ return;
+ }catch (Exception e) {
+ sendErrorResponse(response,HttpServletResponse.SC_UNAUTHORIZED,"Unauthorized");
+ request.setAttribute("AUTH_EXCEPTION","Unauthorized");
+ return;
+ }
+ LoginUserInfo loginInfo = JwtTokenUtil.parseUserFromToken(token);
+ if(loginInfo==null){
+ sendErrorResponse(response,HttpServletResponse.SC_UNAUTHORIZED,"Unauthorized");
+ request.setAttribute("AUTH_EXCEPTION","Invalid token");
+ return;
+ }else{
+ if(!userTokenService.checkUserTokenValid(loginInfo.getTokenUuid())){
+ sendErrorResponse(response,HttpServletResponse.SC_UNAUTHORIZED,"Not Existing Token");
+ request.setAttribute("AUTH_EXCEPTION","Not Existing Token");
+ return;
+ }
+ }
+
+ }else if(!"OPTIONS".equals(request.getMethod())) {
+ sendErrorResponse(response,HttpServletResponse.SC_UNAUTHORIZED,"Unauthorized");
+ request.setAttribute("AUTH_EXCEPTION","Unauthorized");
+ return;
+ }
+ filterChain.doFilter(request, response);
+ }
+
+
+ @Override
+ protected boolean shouldNotFilter(@NonNull HttpServletRequest request) {
+ if (excludePatterns == null) {
+ return false;
+ }
+ return excludePatterns.stream()
+ .anyMatch(pattern -> pathMatcher.match(pattern, request.getRequestURI()));
+ }
+
+ private boolean needCheckAppVersion(@NonNull HttpServletRequest request){
+ if(verifyAppVersionList==null || verifyAppVersionList.isEmpty()){
+ return false;
+ }
+ return verifyAppVersionList.stream()
+ .anyMatch(pattern -> pathMatcher.match(pattern, request.getRequestURI()));
+ }
+
+ private void sendErrorResponse(HttpServletResponse response, int errorCode, String errorMessage) throws IOException {
+ response.setStatus(errorCode);
+ response.setContentType("application/text");
+ response.setCharacterEncoding("UTF-8");
+ response.getWriter().write(errorMessage);
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/mappers/RoleMapper.java b/src/main/java/com/sjmresorts/opsapp/authentication/mappers/RoleMapper.java
new file mode 100644
index 0000000..cf714fb
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/mappers/RoleMapper.java
@@ -0,0 +1,14 @@
+package com.sjmresorts.opsapp.authentication.mappers;
+
+import com.sjmresorts.opsapp.admin.dtos.roles.AdminRoleDto;
+import com.sjmresorts.opsapp.authentication.models.Role;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+import java.util.List;
+
+@Mapper(componentModel = "spring")
+public interface RoleMapper {
+ @Mapping(source = "roleId", target = "roleName")
+ List toAdminRoleDto(List roles);
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/mappers/UserMapper.java b/src/main/java/com/sjmresorts/opsapp/authentication/mappers/UserMapper.java
new file mode 100644
index 0000000..7bfdb5a
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/mappers/UserMapper.java
@@ -0,0 +1,31 @@
+package com.sjmresorts.opsapp.authentication.mappers;
+
+import com.sjmresorts.opsapp.admin.dtos.system.user.CmsUserDto;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.authentication.models.User;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+
+import java.util.List;
+
+@Mapper(componentModel = "spring")
+public interface UserMapper {
+
+ @Mapping(source = "ldapGroup", target = "adGroup")
+ @Mapping(source = "employeeId", target = "userCode")
+ LoginUserInfo toLoginUser(User user);
+
+ @Mapping(source = "employeeId", target = "userCode")
+ CmsUserDto toCmsUserDto(User user);
+
+ @Mapping(source = "employeeId", target = "userCode")
+ List toCmsUserDtoList(List users);
+
+ @Mapping(source = "userCode", target = "employeeId")
+ User toUserEntity(CmsUserDto userDto);
+
+ @Mapping(target = "userId", ignore = true)
+ @Mapping(source = "userCode", target = "employeeId")
+ void updateUserFromCmsUserDto(CmsUserDto dto, @MappingTarget User entity);
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/models/ClientInfo.java b/src/main/java/com/sjmresorts/opsapp/authentication/models/ClientInfo.java
new file mode 100644
index 0000000..2af9752
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/models/ClientInfo.java
@@ -0,0 +1,50 @@
+package com.sjmresorts.opsapp.authentication.models;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.time.Instant;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name="client_infos")
+public class ClientInfo {
+
+ @Id
+ @Column(name = "id",nullable = false)
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "is_latest")
+ private Boolean isLatest;
+
+ @Column(name = "is_active")
+ private Boolean isActive;
+
+ @Column(name = "current_login_user")
+ private String currentLoginUser;
+
+ @Column(name = "client_version")
+ private String clientVersion;
+
+ @Column(name = "device_fuid")
+ private String deviceFuid;
+
+ @Column(name = "client_name")
+ private String clientName;
+
+ @Column(name = "casino_code")
+ private String casinoCode;
+
+ @Column(name = "created_at")
+ private Instant createdAt;
+
+ @Column(name = "updated_at")
+ private Instant updatedAt;
+
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/models/Role.java b/src/main/java/com/sjmresorts/opsapp/authentication/models/Role.java
new file mode 100644
index 0000000..a80b25e
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/models/Role.java
@@ -0,0 +1,21 @@
+package com.sjmresorts.opsapp.authentication.models;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name="roles")
+public class Role {
+ @Id
+ @Column(name = "role_id")
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long roleId;
+
+ @Column(name = "role_name")
+ private String roleName;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/models/RolePermission.java b/src/main/java/com/sjmresorts/opsapp/authentication/models/RolePermission.java
new file mode 100644
index 0000000..fde2091
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/models/RolePermission.java
@@ -0,0 +1,24 @@
+package com.sjmresorts.opsapp.authentication.models;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name="role_permissions")
+public class RolePermission {
+ @Id
+ @Column(name = "id")
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "role_id")
+ private Long roleId;
+
+ @Column(name = "permission_id")
+ private Long permissionId;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/models/RoleUsers.java b/src/main/java/com/sjmresorts/opsapp/authentication/models/RoleUsers.java
new file mode 100644
index 0000000..072480f
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/models/RoleUsers.java
@@ -0,0 +1,24 @@
+package com.sjmresorts.opsapp.authentication.models;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name="role_users")
+public class RoleUsers {
+ @Id
+ @Column(name = "id")
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "role_id")
+ private Long roleId;
+
+ @Column(name = "employee_id")
+ private String employeeId;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/models/RouterPermission.java b/src/main/java/com/sjmresorts/opsapp/authentication/models/RouterPermission.java
new file mode 100644
index 0000000..f177d26
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/models/RouterPermission.java
@@ -0,0 +1,25 @@
+package com.sjmresorts.opsapp.authentication.models;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name="permissions")
+public class RouterPermission {
+ @Id
+ @Column(name = "permission_id")
+ private Long id;
+ @Column(name = "router_id")
+ private String routerId;
+ @Column(name = "permission_name")
+ private String permissionName;
+ @Transient
+ private boolean selected=false;
+ @Transient
+ private String dataPermissionTag;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/models/SimpleUser.java b/src/main/java/com/sjmresorts/opsapp/authentication/models/SimpleUser.java
new file mode 100644
index 0000000..195a0cd
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/models/SimpleUser.java
@@ -0,0 +1,30 @@
+package com.sjmresorts.opsapp.authentication.models;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name="users")
+public class SimpleUser {
+ @Id
+ @Column(name = "user_id")
+ private Long userId;
+
+ @Column(name = "employee_id")
+ private String employeeId;
+
+ @Column(name = "username")
+ private String userName;
+
+ @Column(name = "email")
+ private String email;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/models/SysRouter.java b/src/main/java/com/sjmresorts/opsapp/authentication/models/SysRouter.java
new file mode 100644
index 0000000..e5855a3
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/models/SysRouter.java
@@ -0,0 +1,45 @@
+package com.sjmresorts.opsapp.authentication.models;
+
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name="sys_routers")
+public class SysRouter {
+ @Id
+ @Column(name = "id")
+ private String id;
+ @Column(name="parent_id")
+ private String parentId;
+ @Column(name="heading")
+ private String heading;
+ @Column(name="section_title")
+ private String sectionTitle;
+ @Column(name="route")
+ private String route;
+ @Column(name="svg_icon")
+ private String svgIcon;
+ @Column(name="font_icon")
+ private String fontIcon;
+ @Column(name="sort_no")
+ private Integer sortNo;
+ @Column(name="status")
+ private Integer status;
+
+ @Transient
+ private List sub;
+
+ @Transient
+ private List routePermissions=new ArrayList();
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/models/User.java b/src/main/java/com/sjmresorts/opsapp/authentication/models/User.java
new file mode 100644
index 0000000..fff1c1f
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/models/User.java
@@ -0,0 +1,81 @@
+package com.sjmresorts.opsapp.authentication.models;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.hibernate.annotations.SQLDelete;
+import org.hibernate.annotations.SQLRestriction;
+
+import java.time.Instant;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name="users")
+@SQLDelete(sql = "UPDATE users SET deleted_at = CURRENT_TIMESTAMP WHERE user_id = ?")
+@SQLRestriction("deleted_at IS NULL")
+public class User {
+
+ @Id
+ @Column(name = "user_id",nullable = false)
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long userId;
+
+ @Column(name = "employee_id",nullable = false)
+ private String employeeId;
+
+ @Column(name = "username")
+ private String userName;
+
+ @Column(name = "ldap_group")
+ private String ldapGroup;
+
+ @Column(name = "role")
+ private String role;
+
+ @Column(name = "casino_code",nullable = false)
+ private String casinoCode;
+
+ @Column(name = "pits")
+ private String pitCodes;
+
+ @Column(name = "device_id")
+ private String deviceId;
+
+ @Column(name = "device_token")
+ private String deviceToken;
+
+ @Column(name = "online_status")
+ private Boolean onlineStatus;
+
+ @Column(name = "last_heartbeat")
+ private Instant lastHeartbeat;
+
+ @Column(name = "email")
+ private String email;
+
+ @Column(name = "password")
+ private String password;
+
+ @Column(name = "status")
+ private Integer status;
+
+ @Column(name = "last_login")
+ private Instant lastLogin;
+
+ @Column(name = "cms_last_login")
+ private Instant cmsLastLogin;
+
+ @Column(name = "is_deleted")
+ private Boolean deleted;
+
+ @Column(name = "deleted_at")
+ private Instant deletedAt;
+
+ @Version
+ @Column(name="version")
+ private Long version;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/models/UserRole.java b/src/main/java/com/sjmresorts/opsapp/authentication/models/UserRole.java
new file mode 100644
index 0000000..1f359b1
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/models/UserRole.java
@@ -0,0 +1,25 @@
+package com.sjmresorts.opsapp.authentication.models;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name="user_roles")
+public class UserRole {
+
+ @Id
+ @Column(name = "id",nullable = false)
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "user_id")
+ private Long userId;
+
+ @Column(name = "role_id")
+ private Long roleId;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/models/UserToken.java b/src/main/java/com/sjmresorts/opsapp/authentication/models/UserToken.java
new file mode 100644
index 0000000..9d25c63
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/models/UserToken.java
@@ -0,0 +1,40 @@
+package com.sjmresorts.opsapp.authentication.models;
+
+import jakarta.persistence.*;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.time.Instant;
+import java.util.Date;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@Entity
+@Table(name="user_tokens")
+public class UserToken {
+ @Id
+ @Column(name = "token_id",nullable = false)
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long tokenId;
+
+ @Column(name = "user_code",nullable = false)
+ private String userCode;
+
+ @Column(name = "token_type",nullable = false)
+ private String tokenType;
+
+ @Column(name = "token_value",nullable = false)
+ private String tokenValue;
+
+ @Column(name = "token_uuid")
+ private String tokenUniqueId;
+
+ @Column(name = "created_at")
+ private Instant createdAt;
+
+ @Column(name = "expiry_time")
+ private Date expiryTime;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/repositories/DeviceInfoRepository.java b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/DeviceInfoRepository.java
new file mode 100644
index 0000000..73e9009
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/DeviceInfoRepository.java
@@ -0,0 +1,29 @@
+package com.sjmresorts.opsapp.authentication.repositories;
+
+import com.sjmresorts.opsapp.authentication.models.ClientInfo;
+import jakarta.transaction.Transactional;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.time.Instant;
+import java.util.List;
+
+@Repository
+public interface DeviceInfoRepository extends JpaRepository {
+
+ List findAllByDeviceFuid(String deviceId);
+
+ Page findAll(Specification listSpec, Pageable pageable);
+
+ @Modifying
+ @Transactional
+ @Query(value="update ClientInfo set currentLoginUser=null, updatedAt=?2 " +
+ "where currentLoginUser=?1 ")
+ void clearLogoutDevice(String employeeId, Instant updateTime);
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/repositories/RolePermissionRepository.java b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/RolePermissionRepository.java
new file mode 100644
index 0000000..0e98e98
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/RolePermissionRepository.java
@@ -0,0 +1,16 @@
+package com.sjmresorts.opsapp.authentication.repositories;
+
+import com.sjmresorts.opsapp.authentication.models.RolePermission;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface RolePermissionRepository extends JpaRepository {
+
+ @Query("SELECT rp FROM RolePermission rp WHERE rp.roleId = ?1")
+ List getAllPermissionByRoleId(Long roleId);
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/repositories/RouterPermissionRepository.java b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/RouterPermissionRepository.java
new file mode 100644
index 0000000..58322bf
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/RouterPermissionRepository.java
@@ -0,0 +1,21 @@
+package com.sjmresorts.opsapp.authentication.repositories;
+
+import com.sjmresorts.opsapp.authentication.models.RouterPermission;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface RouterPermissionRepository extends JpaRepository {
+
+ @Query(value="select rp from RouterPermission rp where rp.id in " +
+ " (select rop.permissionId from RolePermission rop where rop.roleId in ?1) ")
+ List queryPermissionByRoleIds(List roleIds);
+
+ @Query(value="select rp from RouterPermission rp where rp.id in " +
+ " (select rop.permissionId from RolePermission rop where rop.roleId in ?1) and rp.routerId in ?2 ")
+ List queryPermissionByRoleAndRouterIds(List roleIds,List routerIds);
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/repositories/SysRouterRepository.java b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/SysRouterRepository.java
new file mode 100644
index 0000000..be58ca4
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/SysRouterRepository.java
@@ -0,0 +1,24 @@
+package com.sjmresorts.opsapp.authentication.repositories;
+
+import com.sjmresorts.opsapp.authentication.models.SysRouter;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface SysRouterRepository extends JpaRepository {
+
+ @Query(value="select rte from SysRouter rte where rte.status=1 order by rte.sortNo")
+ List queryAllActiveRouter();
+
+ @Query(value="select rte from SysRouter rte order by rte.sortNo")
+ List queryAllRoutersOrderBySort();
+
+ @Query(value="select rte from SysRouter rte where rte.heading is not null and rte.status=1 order by rte.sortNo")
+ List getAllChildrenRoutes();
+
+ @Query(value="select rte.id from SysRouter rte where heading=?1")
+ List queryIdsByHeading(String appRouterName);
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/repositories/UserRepository.java b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/UserRepository.java
new file mode 100644
index 0000000..9677afc
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/UserRepository.java
@@ -0,0 +1,66 @@
+package com.sjmresorts.opsapp.authentication.repositories;
+
+import com.sjmresorts.opsapp.authentication.models.User;
+import jakarta.transaction.Transactional;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.time.Instant;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface UserRepository extends JpaRepository {
+ Optional findByEmployeeId(String employeeId);
+
+ Optional findByDeviceId(String deviceId);
+
+ List findByEmployeeIdIn(Collection employeeIds);
+
+ Optional findByDeviceToken(String deviceToken);
+
+ Page findAll(Specification listSpec, Pageable pageable);
+
+ List findAll(Specification spec, Sort sort);
+
+ @Query(value="select usr from User usr where casinoCode=?1 and pitCodes like %?2% and lastHeartbeat >= ?3")
+ List queryOnlineUserForPit(String casinoCode, String pitCode, Instant heartbeatAfter);
+
+ int countByEmployeeIdAndPassword(String upperCase, String encryptPassword);
+
+ @Modifying
+ @Transactional
+ @Query(value="update User set casinoCode='', pitCodes='', deviceId=null, deviceToken=null, onlineStatus=false, lastHeartbeat=null " +
+ " where employeeId=?1 ")
+ void clearAppLoginInfoByUser(String employeeId);
+
+ @Modifying
+ @Transactional
+ @Query(value="update User set lastHeartbeat=?2 where employeeId=?1 ")
+ int registerHeartBeat(String employeeId, Instant now);
+
+ @Query(value = "select u.employeeId from User u where u.lastHeartbeat < ?1 order by u.lastHeartbeat asc")
+ List findExpiredSessionEmployeeIds(Instant checkExpiryTime, Pageable pageable);
+
+ @Modifying
+ @Transactional
+ @Query(value="update User set deviceId=null, deviceToken=null where employeeId<>?1 and deviceId=?2")
+ void clearOtherUserDeviceInfo(String employeeId, String deviceId);
+
+ @Modifying
+ @Transactional
+ @Query("update User u set u.deviceToken = null where u.deviceToken = ?1")
+ int clearDeviceTokenByToken(String deviceToken);
+
+ @Modifying
+ @Transactional
+ @Query("update User u set u.deviceToken = null where u.employeeId = ?1")
+ int clearDeviceTokenByEmployeeId(String employeeId);
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/repositories/UserRoleRepository.java b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/UserRoleRepository.java
new file mode 100644
index 0000000..d083704
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/UserRoleRepository.java
@@ -0,0 +1,15 @@
+package com.sjmresorts.opsapp.authentication.repositories;
+
+import com.sjmresorts.opsapp.authentication.models.UserRole;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface UserRoleRepository extends JpaRepository {
+
+ @Query(value="select usrRole.id from UserRole usrRole where userId=?1 ")
+ List findRoleIdListByUserId(Long userId);
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/repositories/UserTokenRepository.java b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/UserTokenRepository.java
new file mode 100644
index 0000000..b97d14a
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/repositories/UserTokenRepository.java
@@ -0,0 +1,30 @@
+package com.sjmresorts.opsapp.authentication.repositories;
+
+import com.sjmresorts.opsapp.authentication.models.UserToken;
+import jakarta.transaction.Transactional;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.Date;
+
+@Repository
+public interface UserTokenRepository extends JpaRepository {
+
+ int countByTokenUniqueId(String tokenUuid);
+ int countByTokenTypeAndTokenValue(String tokenType, String tokenValue);
+
+ @Modifying
+ @Transactional
+ void deleteAllByTokenTypeAndUserCode(String tokenType, String userCode);
+
+ @Modifying
+ @Transactional
+ void deleteAllByTokenUniqueId(String tokenUuid);
+
+ @Modifying
+ @Transactional
+ @Query(value="delete from UserToken where expiryTime1")
+ void deleteAllExpiredBeforeTime(Date expiryTime);
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/services/AdAuthorizeService.java b/src/main/java/com/sjmresorts/opsapp/authentication/services/AdAuthorizeService.java
new file mode 100644
index 0000000..9559e73
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/services/AdAuthorizeService.java
@@ -0,0 +1,76 @@
+package com.sjmresorts.opsapp.authentication.services;
+
+import com.sjmresorts.opsapp.authentication.dtos.AdAuthorizeResponse;
+import com.sjmresorts.opsapp.authentication.dtos.AdUserInfo;
+import com.sjmresorts.opsapp.common.dtos.LDAP.ADAccountInfo;
+import com.sjmresorts.opsapp.common.utils.LDAPUtil;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class AdAuthorizeService {
+ @Resource
+ private LDAPUtil ldapUtil;
+
+ public AdAuthorizeResponse mockupAdUserLogin(String userCode, String password) {
+ AdAuthorizeResponse result = new AdAuthorizeResponse();
+
+ result.setSuccess(true);
+ result.setCode("200");
+ AdUserInfo adUser = new AdUserInfo();
+ adUser.setEmployeeId(userCode);
+ adUser.setAdGroup(List.of("APP Admin"));
+ adUser.setUserName("chanTaiman");
+ adUser.setRole("ADRole");
+ adUser.setEmail("TestEmail@sjm.com");
+ result.setUserInfo(adUser);
+ return result;
+ }
+
+ public AdAuthorizeResponse verfiyAdUser(String employeeID, String password) {
+ AdAuthorizeResponse result = new AdAuthorizeResponse();
+
+ /*************Check And Get AD Account************/
+ boolean checkPassed = false;
+ checkPassed = ldapUtil.authenticate(employeeID, password);
+ ADAccountInfo account = ldapUtil.getAccountInfo(employeeID);
+ /*************Check And Get AD Account************/
+
+ if (account == null) {
+ // Handle user not found, e.g. throw custom exception or return error
+ throw new RuntimeException("User not found in LDAP: " + employeeID);
+ }
+
+ result.setSuccess(true);
+ result.setCode("200");
+ AdUserInfo adUser = new AdUserInfo();
+ adUser.setEmployeeId(employeeID);
+ adUser.setAdGroup(account.getDirectGroups());
+ adUser.setUserName(account.getDisplayName());
+ adUser.setRole("ADRole");
+ adUser.setEmail(account.getMail());
+ result.setUserInfo(adUser);
+ return result;
+ }
+
+ public AdAuthorizeResponse getAdUser(String userCode) {
+ AdAuthorizeResponse result = new AdAuthorizeResponse();
+
+ ADAccountInfo account = ldapUtil.getAccountInfo(userCode);
+
+ result.setSuccess(true);
+ result.setCode("200");
+ AdUserInfo adUser = new AdUserInfo();
+ adUser.setEmployeeId(userCode);
+ adUser.setAdGroup(account.getDirectGroups());
+// adUser.setAdGroup(List.of("APP Admin"));
+ adUser.setUserName(account.getDisplayName());
+ adUser.setRole("ADRole");
+
+ adUser.setEmail(account.getMail());
+ result.setUserInfo(adUser);
+ return result;
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/services/LoginService.java b/src/main/java/com/sjmresorts/opsapp/authentication/services/LoginService.java
new file mode 100644
index 0000000..6cdcbbd
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/services/LoginService.java
@@ -0,0 +1,174 @@
+package com.sjmresorts.opsapp.authentication.services;
+
+import com.sjmresorts.opsapp.admin.repositories.RolesRepository;
+import com.sjmresorts.opsapp.authentication.dtos.*;
+import com.sjmresorts.opsapp.authentication.mappers.UserMapper;
+import com.sjmresorts.opsapp.authentication.models.ClientInfo;
+import com.sjmresorts.opsapp.authentication.models.RouterPermission;
+import com.sjmresorts.opsapp.authentication.models.User;
+import com.sjmresorts.opsapp.authentication.models.UserToken;
+import com.sjmresorts.opsapp.authentication.repositories.DeviceInfoRepository;
+import com.sjmresorts.opsapp.authentication.repositories.RouterPermissionRepository;
+import com.sjmresorts.opsapp.authentication.repositories.SysRouterRepository;
+import com.sjmresorts.opsapp.authentication.repositories.UserRepository;
+import com.sjmresorts.opsapp.common.constants.AppConstants;
+import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
+import jakarta.transaction.Transactional;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.dao.OptimisticLockingFailureException;
+import org.springframework.retry.annotation.Backoff;
+import org.springframework.retry.annotation.Retryable;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+
+import java.time.Instant;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+@RequiredArgsConstructor
+public class LoginService {
+
+ private final UserRepository userRepository;
+ private final DeviceInfoRepository deviceInfoRepository;
+ private final UserTokenService userTokenService;
+ private final RolesRepository rolesRepository;
+ private final RouterPermissionRepository routerPermissionRepository;
+ private final SysRouterRepository sysRouterRepository;
+
+ @Retryable(retryFor = OptimisticLockingFailureException.class,
+ maxAttempts = 3,
+ backoff = @Backoff(delay = 100))
+ @Transactional
+ public RegisterTrackInfoResponse registerTrackPits(@Valid RegisterTrackInfoData data) {
+ RegisterTrackInfoResponse result = new RegisterTrackInfoResponse();
+ User user = userRepository.findByEmployeeId(data.getEmployeeId()).orElse(null);
+ if(user==null) {
+ result.setSuccess(false);
+ result.setCode("USER_NOT_LOGIN");
+ result.setMessage("Current user has not login");
+ }else{
+ String pits = "";
+ if(data.getPitCodes()!=null) {
+ List selectPitCodes=data.getPitCodes();
+ for(String pitCode:selectPitCodes) {
+ pits +="["+pitCode+"]";
+ }
+ }
+ user.setPitCodes(pits);
+ user.setLastHeartbeat(Instant.now());
+ userRepository.save(user);
+ result.setSuccess(true);
+ result.setMessage("Pits registered successfully");
+ }
+ return result;
+ }
+
+ private void clearLastLoginInfo(String employeeId){
+ userTokenService.clearUserTokenByType(AppConstants.APP_API_ACCESS_TOKEN_TYPE,employeeId);
+ userTokenService.clearUserTokenByType(AppConstants.APP_REFRESH_TOKEN_TYPE,employeeId);
+ deviceInfoRepository.clearLogoutDevice(employeeId,Instant.now());
+ userRepository.clearAppLoginInfoByUser(employeeId);
+ }
+ public GetAuthTokenResponse assignAccessToken(String requestSource, @NotNull(message = "Data attribute must not be null") GetAuthTokenData data) {
+ GetAuthTokenResponse result = new GetAuthTokenResponse();
+ LoginUserInfo thirdPartyUser = new LoginUserInfo();
+ thirdPartyUser.setEmployeeId(data.getRequesterId());
+ thirdPartyUser.setUserCode(data.getRequesterId());
+ thirdPartyUser.setUserName(data.getRequesterId());
+ thirdPartyUser.setLoginSource(requestSource);
+ UserToken accessToken = JwtTokenUtil.generateTokenByLoginUser(AppConstants.THIRD_PARTY_TOKEN_TYPE, thirdPartyUser);
+ userTokenService.saveUserToken(accessToken);
+ result.setAccessToken(accessToken.getTokenValue());
+ result.setCode("200");
+ result.setSuccess(true);
+ return result;
+ }
+
+ @Retryable(retryFor = OptimisticLockingFailureException.class,
+ maxAttempts = 3,
+ backoff = @Backoff(delay = 100))
+ @Transactional
+ public void processLogout(LoginUserInfo loginUser) {
+ if(loginUser!=null){
+ clearLastLoginInfo(loginUser.getEmployeeId());
+ }
+ }
+
+ public RefreshTokenResponse refreshToken(LoginUserInfo loginUser, boolean isRenewRefreshToken) {
+ RefreshTokenResponse result = new RefreshTokenResponse();
+ UserToken accessToken = null;
+ UserToken refreshToken = null;
+ if(loginUser!=null){
+ accessToken=JwtTokenUtil.generateTokenByLoginUser(AppConstants.APP_API_ACCESS_TOKEN_TYPE,loginUser);
+ userTokenService.saveUserToken(accessToken);
+ result.setAccessToken(accessToken.getTokenValue());
+ if(isRenewRefreshToken){
+ refreshToken=JwtTokenUtil.generateTokenByLoginUser(AppConstants.APP_REFRESH_TOKEN_TYPE,loginUser);
+ userTokenService.saveUserToken(refreshToken);
+ result.setRefreshToken(refreshToken.getTokenValue());
+ if(StringUtils.isNotBlank(loginUser.getTokenUuid())){
+ userTokenService.removeTokenByUniqueId(loginUser.getTokenUuid());
+ }
+ }
+ }
+
+ if(accessToken!=null){
+ result.setSuccess(true);
+ result.setCode("200");
+ result.setMessage("Token renew successful.");
+ }else{
+ result.setSuccess(false);
+ result.setCode("200");
+ result.setMessage("Token failed.");
+ }
+ return result;
+ }
+
+ public boolean registerHeartBeat(LoginUserInfo loginUser) {
+ boolean result = false;
+ if(loginUser!=null && StringUtils.isNotBlank(loginUser.getEmployeeId())){
+ int updateCnt = userRepository.registerHeartBeat(loginUser.getEmployeeId(), Instant.now());
+ result = updateCnt>0;
+ }
+ return result;
+ }
+
+ public boolean logoutFirstIdleSessionByTime(Instant checkExpiryTime) {
+ boolean hasRecord =false;
+ List idleUsers = userRepository.findExpiredSessionEmployeeIds(
+ checkExpiryTime, PageRequest.of(0, 1));
+ String idleUserCode = idleUsers == null || idleUsers.isEmpty() ? null : idleUsers.get(0);
+ if(StringUtils.isNotBlank(idleUserCode)) {
+ hasRecord=true;
+ userRepository.clearAppLoginInfoByUser(idleUserCode);
+ deviceInfoRepository.clearLogoutDevice(idleUserCode,Instant.now());
+ }
+ return hasRecord;
+ }
+
+ private Set getUserPermissionGroup(List groupName) {
+ Set permissionGroup = new HashSet<>();
+ if(groupName!=null&&!groupName.isEmpty()){
+ for(String roleName : groupName){
+ List roleIds = rolesRepository.queryRoleIdByRoleName(roleName);
+ List appRouterIds = sysRouterRepository.queryIdsByHeading(AppConstants.APP_ROUTER_NAME);
+ if(!roleIds.isEmpty() && !appRouterIds.isEmpty()){
+ List permissions = routerPermissionRepository.queryPermissionByRoleAndRouterIds(roleIds,appRouterIds);
+ if(!permissions.isEmpty()){
+ Set names = permissions.stream()
+ .map(RouterPermission::getPermissionName)
+ .collect(Collectors.toSet());
+ permissionGroup.addAll(names);
+ }
+ }
+ }
+ }
+ return permissionGroup;
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/services/RolePermissionService.java b/src/main/java/com/sjmresorts/opsapp/authentication/services/RolePermissionService.java
new file mode 100644
index 0000000..1a3310b
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/services/RolePermissionService.java
@@ -0,0 +1,50 @@
+package com.sjmresorts.opsapp.authentication.services;
+
+import com.sjmresorts.opsapp.authentication.models.RouterPermission;
+import com.sjmresorts.opsapp.authentication.repositories.RouterPermissionRepository;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+@RequiredArgsConstructor
+public class RolePermissionService {
+ private final RouterPermissionRepository routerPermissionRepository;
+
+ public Map> getFullRouterPermissions(){
+ List fullPermission = routerPermissionRepository.findAll();
+ return constructPermissionMap(fullPermission);
+ }
+
+ public Map> getRouterPermissionsByGroupIds(List userRoleIds) {
+ List rolePermissions = null;
+ if(userRoleIds.isEmpty()){
+ rolePermissions = new ArrayList();
+ }else{
+ rolePermissions = routerPermissionRepository.queryPermissionByRoleIds(userRoleIds);
+ }
+ return constructPermissionMap(rolePermissions);
+ }
+
+ private Map> constructPermissionMap(List permissions){
+ Map> result= new HashMap>();
+ if(permissions!=null) {
+ for(RouterPermission rpItem: permissions) {
+ if(result.containsKey(rpItem.getRouterId())) {
+ result.get(rpItem.getRouterId()).add(rpItem);
+ }else {
+ List permissionList = new ArrayList();
+ permissionList.add(rpItem);
+ result.put(rpItem.getRouterId(), permissionList);
+ }
+ }
+ }
+ return result;
+ }
+
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/services/SysRouterService.java b/src/main/java/com/sjmresorts/opsapp/authentication/services/SysRouterService.java
new file mode 100644
index 0000000..028a46b
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/services/SysRouterService.java
@@ -0,0 +1,59 @@
+package com.sjmresorts.opsapp.authentication.services;
+
+import com.sjmresorts.opsapp.authentication.models.RouterPermission;
+import com.sjmresorts.opsapp.authentication.models.SysRouter;
+import com.sjmresorts.opsapp.authentication.repositories.SysRouterRepository;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Service
+@RequiredArgsConstructor
+public class SysRouterService {
+
+ private final SysRouterRepository sysRouterRepository;
+
+ public List getRoutersByPermission(String rootId,
+ Map> userPermissions) {
+ List srcRouters = sysRouterRepository.queryAllActiveRouter();
+ if(StringUtils.isBlank(rootId)) {
+ rootId = "0";
+ }
+ return this.constructRouterByParent(srcRouters,rootId,userPermissions);
+ }
+
+ private List constructRouterByParent(List srcRouters, String parentId,Map> userPermissions) {
+ List resultList = new ArrayList();
+ // get root routers
+ for(int i=0;i subList = constructRouterByParent(srcRouters,rItem.getId(),userPermissions);
+ if(!subList.isEmpty()) {
+ SysRouter router = buildRouter(rItem,new String[] { "sortNo","status","heading" });
+ router.setSub(subList);
+ resultList.add(router);
+ }
+ }
+ }
+ }
+ return resultList;
+ }
+
+ public SysRouter buildRouter(SysRouter router,String[] filter) {
+ SysRouter returnRouter = new SysRouter();
+ BeanUtils.copyProperties(router, returnRouter, filter);
+ return returnRouter;
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/authentication/services/UserTokenService.java b/src/main/java/com/sjmresorts/opsapp/authentication/services/UserTokenService.java
new file mode 100644
index 0000000..94cd7d3
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/authentication/services/UserTokenService.java
@@ -0,0 +1,63 @@
+package com.sjmresorts.opsapp.authentication.services;
+
+import com.sjmresorts.opsapp.authentication.models.UserToken;
+import com.sjmresorts.opsapp.authentication.repositories.UserTokenRepository;
+import jakarta.transaction.Transactional;
+import lombok.RequiredArgsConstructor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.dao.OptimisticLockingFailureException;
+import org.springframework.retry.annotation.Backoff;
+import org.springframework.retry.annotation.Retryable;
+import org.springframework.stereotype.Service;
+
+import java.time.Instant;
+import java.util.Date;
+
+@Service
+@RequiredArgsConstructor
+public class UserTokenService {
+ private final Logger logger = LoggerFactory.getLogger(UserTokenService.class);
+ private final UserTokenRepository userTokenRepository;
+
+ @Retryable(retryFor = OptimisticLockingFailureException.class,
+ maxAttempts = 3,
+ backoff = @Backoff(delay = 100))
+ @Transactional
+ public void saveUserToken(UserToken userToken){
+ if(userToken!=null){
+ if(userToken.getCreatedAt()==null){
+ userToken.setCreatedAt(Instant.now());
+ }
+ userTokenRepository.save(userToken);
+ }
+ }
+
+ public boolean checkUserTokenValid(String tokenUuid){
+ return userTokenRepository.countByTokenUniqueId(tokenUuid)>0;
+ }
+
+ public void clearUserTokenByType(String tokenType, String userCode){
+ try {
+ userTokenRepository.deleteAllByTokenTypeAndUserCode(tokenType, userCode);
+ }catch(Exception err){
+ logger.warn("Encountered ignorable error when remove user token:{}",err.getMessage());
+ }
+ }
+
+ public void removeTokenByUniqueId(String tokenUuid) {
+ try {
+ userTokenRepository.deleteAllByTokenUniqueId(tokenUuid);
+ }catch(Exception err){
+ logger.warn("Encountered ignorable error when delete user token:{}",err.getMessage());
+ }
+ }
+
+ public void expireUserTokenByTime(Date expiryTime) {
+ try{
+ userTokenRepository.deleteAllExpiredBeforeTime(expiryTime);
+ }catch(Exception err){
+ logger.warn("Encountered ignorable error when delete expired user token:{}",err.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/config/AsyncConfig.java b/src/main/java/com/sjmresorts/opsapp/common/config/AsyncConfig.java
new file mode 100644
index 0000000..5dc1987
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/config/AsyncConfig.java
@@ -0,0 +1,52 @@
+package com.sjmresorts.opsapp.common.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.AsyncConfigurer;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 限制 @Async 并发,避免无界线程打满 Hikari 连接池。
+ * OPERA/OHIP 上传使用独立池,避免被归档 OCR / 日志等任务挤占。
+ */
+@Configuration
+@EnableAsync
+public class AsyncConfig implements AsyncConfigurer {
+
+ /** 默认 @Async 池(OCR、审计日志等)。 */
+ public static final String TASK_EXECUTOR = "taskExecutor";
+
+ /** 签名归档后推 OPERA 专用池。 */
+ public static final String OHIP_UPLOAD_EXECUTOR = "ohipUploadExecutor";
+
+ @Override
+ @Bean(name = TASK_EXECUTOR)
+ public Executor getAsyncExecutor() {
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ executor.setCorePoolSize(2);
+ executor.setMaxPoolSize(5);
+ executor.setQueueCapacity(500);
+ executor.setThreadNamePrefix("ops-async-");
+ executor.initialize();
+ return executor;
+ }
+
+ @Bean(name = OHIP_UPLOAD_EXECUTOR)
+ public Executor ohipUploadExecutor() {
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ executor.setCorePoolSize(2);
+ executor.setMaxPoolSize(4);
+ executor.setQueueCapacity(200);
+ executor.setThreadNamePrefix("ohip-upload-");
+ executor.setWaitForTasksToCompleteOnShutdown(true);
+ executor.setAwaitTerminationSeconds(60);
+ // 队列满时回退到调用线程,避免静默丢弃导致 ohipStatus 永久 PENDING
+ executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+ executor.initialize();
+ return executor;
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/config/GoogleApiProxyFactory.java b/src/main/java/com/sjmresorts/opsapp/common/config/GoogleApiProxyFactory.java
new file mode 100644
index 0000000..ab1bfa6
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/config/GoogleApiProxyFactory.java
@@ -0,0 +1,56 @@
+package com.sjmresorts.opsapp.common.config;
+
+import com.google.api.client.http.HttpTransport;
+import com.google.api.client.http.javanet.NetHttpTransport;
+import com.google.auth.http.HttpTransportFactory;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.util.Optional;
+
+/**
+ * Firebase Admin / Google OAuth 默认不走 JVM {@code https.proxyHost},需显式配置 HttpTransport。
+ */
+@Slf4j
+@Component
+public class GoogleApiProxyFactory {
+
+ @Value("${proxy.enabled:false}")
+ private boolean proxyEnabled;
+
+ @Value("${proxy.host:}")
+ private String proxyHost;
+
+ @Value("${proxy.port:}")
+ private String proxyPort;
+
+ public Optional resolveProxy() {
+ if (!proxyEnabled || StringUtils.isBlank(proxyHost) || StringUtils.isBlank(proxyPort)) {
+ return Optional.empty();
+ }
+ try {
+ int port = Integer.parseInt(proxyPort.trim());
+ Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost.trim(), port));
+ log.info("Google API HTTP proxy: {}:{}", proxyHost.trim(), port);
+ return Optional.of(proxy);
+ } catch (NumberFormatException e) {
+ log.warn("Invalid proxy.port={}, Google API proxy disabled", proxyPort);
+ return Optional.empty();
+ }
+ }
+
+ public HttpTransport createHttpTransport() {
+ NetHttpTransport.Builder builder = new NetHttpTransport.Builder();
+ resolveProxy().ifPresent(builder::setProxy);
+ return builder.build();
+ }
+
+ public HttpTransportFactory createTransportFactory() {
+ HttpTransport transport = createHttpTransport();
+ return () -> transport;
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/config/JvmProxyConfigurator.java b/src/main/java/com/sjmresorts/opsapp/common/config/JvmProxyConfigurator.java
new file mode 100644
index 0000000..0a10711
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/config/JvmProxyConfigurator.java
@@ -0,0 +1,40 @@
+package com.sjmresorts.opsapp.common.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import jakarta.annotation.PostConstruct;
+
+/**
+ * Must run before Firebase Admin SDK initializes (Google OAuth needs outbound HTTPS).
+ */
+@Slf4j
+@Component
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class JvmProxyConfigurator {
+
+ @Value("${proxy.enabled:false}")
+ private boolean proxyEnabled;
+
+ @Value("${proxy.host:}")
+ private String proxyHost;
+
+ @Value("${proxy.port:}")
+ private String proxyPort;
+
+ @PostConstruct
+ public void applyJvmProxy() {
+ if (!proxyEnabled || StringUtils.isBlank(proxyHost) || StringUtils.isBlank(proxyPort)) {
+ return;
+ }
+ System.setProperty("https.proxyHost", proxyHost.trim());
+ System.setProperty("https.proxyPort", proxyPort.trim());
+ System.setProperty("http.proxyHost", proxyHost.trim());
+ System.setProperty("http.proxyPort", proxyPort.trim());
+ log.info("JVM HTTPS proxy enabled: {}:{}", proxyHost, proxyPort);
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/config/LdapConfig.java b/src/main/java/com/sjmresorts/opsapp/common/config/LdapConfig.java
new file mode 100644
index 0000000..542ad17
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/config/LdapConfig.java
@@ -0,0 +1,39 @@
+package com.sjmresorts.opsapp.common.config;
+
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.core.support.LdapContextSource;
+
+@Configuration
+public class LdapConfig {
+ @Value("${spring.ldap.urls}")
+ private String ldapUrl;
+
+ @Value("${spring.ldap.base}")
+ private String baseDn;
+
+ @Value("${spring.ldap.username}")
+ private String managerDn;
+
+ @Value("${spring.ldap.password}")
+ private String managerPassword;
+
+ @Bean
+ public LdapContextSource contextSource() {
+ LdapContextSource source = new LdapContextSource();
+ source.setUrl(ldapUrl);
+ source.setBase(baseDn);
+ source.setUserDn(managerDn);
+ source.setPassword(managerPassword);
+ source.setReferral("follow");
+ return source;
+ }
+
+ @Bean
+ public LdapTemplate ldapTemplate() {
+ return new LdapTemplate(contextSource());
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/config/OpsAppDbConfig.java b/src/main/java/com/sjmresorts/opsapp/common/config/OpsAppDbConfig.java
new file mode 100644
index 0000000..0105adf
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/config/OpsAppDbConfig.java
@@ -0,0 +1,96 @@
+package com.sjmresorts.opsapp.common.config;
+
+import com.sjmresorts.opsapp.common.db.DbDialect;
+import com.zaxxer.hikari.HikariDataSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.jdbc.DataSourceBuilder;
+import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.orm.jpa.JpaTransactionManager;
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+import org.springframework.retry.annotation.EnableRetry;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import jakarta.persistence.EntityManagerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@EnableRetry
+@Configuration
+@EnableTransactionManagement
+@EnableJpaRepositories(
+ basePackages = {
+ "com.sjmresorts.opsapp.authentication",
+ "com.sjmresorts.opsapp.notifications",
+ "com.sjmresorts.opsapp.logging",
+ "com.sjmresorts.opsapp.esign",
+ "com.sjmresorts.opsapp.appsetting",
+ "com.sjmresorts.opsapp.admin",
+ "com.sjmresorts.opsapp.profile.repositories"
+ },
+ entityManagerFactoryRef = "opsAppEntityManagerFactory",
+ transactionManagerRef = "opsAppTransactionManager"
+)
+public class OpsAppDbConfig {
+
+ private static final Logger log = LoggerFactory.getLogger(OpsAppDbConfig.class);
+
+ @Bean
+ @Primary
+ @ConfigurationProperties("spring.datasource.ops-app-db")
+ public HikariDataSource opsAppDataSource() {
+ return DataSourceBuilder.create()
+ .type(HikariDataSource.class)
+ .build();
+ }
+
+ @Primary
+ @Bean(name = "opsAppEntityManagerFactory")
+ @org.springframework.context.annotation.DependsOn({"esignCaptureSchemaMigrator", "esignCmsSchemaMigrator"})
+ public LocalContainerEntityManagerFactoryBean opsAppEntityManagerFactory(
+ EntityManagerFactoryBuilder builder,
+ DbDialect dbDialect) {
+ Map jpaProps = new HashMap<>();
+ jpaProps.put("hibernate.dialect", dbDialect.hibernateDialectClass());
+ return builder
+ .dataSource(opsAppDataSource())
+ .packages(
+ "com.sjmresorts.opsapp.authentication",
+ "com.sjmresorts.opsapp.notifications",
+ "com.sjmresorts.opsapp.logging",
+ "com.sjmresorts.opsapp.esign",
+ "com.sjmresorts.opsapp.appsetting",
+ "com.sjmresorts.opsapp.admin",
+ "com.sjmresorts.opsapp.profile.models"
+ ).persistenceUnit("opsAppDb")
+ .properties(jpaProps)
+ .build();
+ }
+
+ @Primary
+ @Bean(name = "opsAppTransactionManager")
+ public PlatformTransactionManager opsAppTransactionManager(
+ @Qualifier("opsAppEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
+ return new JpaTransactionManager(entityManagerFactory);
+ }
+
+ @Bean
+ ApplicationRunner logOpsAppDbPoolSize(
+ @Qualifier("opsAppDataSource") HikariDataSource dataSource, DbDialect dbDialect) {
+ return args -> log.info(
+ "ops-app-db Hikari pool: name={}, maximumPoolSize={}, minimumIdle={}, dbType={}",
+ dataSource.getPoolName(),
+ dataSource.getMaximumPoolSize(),
+ dataSource.getMinimumIdle(),
+ dbDialect.type());
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/config/SwaggerConfig.java b/src/main/java/com/sjmresorts/opsapp/common/config/SwaggerConfig.java
new file mode 100644
index 0000000..22e1323
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/config/SwaggerConfig.java
@@ -0,0 +1,19 @@
+package com.sjmresorts.opsapp.common.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+
+@Configuration
+public class SwaggerConfig {
+ @Bean
+ public OpenAPI customOpenAPI() {
+ return new OpenAPI()
+ .info(new Info()
+ .title("API Document for ops application")
+ .version("1.0")
+ .description("API document for SJM ops application"));
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/config/WebFilterConfig.java b/src/main/java/com/sjmresorts/opsapp/common/config/WebFilterConfig.java
new file mode 100644
index 0000000..0857f9c
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/config/WebFilterConfig.java
@@ -0,0 +1,142 @@
+package com.sjmresorts.opsapp.common.config;
+
+import com.sjmresorts.opsapp.common.filters.CorsHeaderFilter;
+import com.sjmresorts.opsapp.common.filters.JwtAuthFilter;
+import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Configuration
+public class WebFilterConfig {
+
+ @Value("${ops-app.security.jwt.secret_key}")
+ private String secretKey;
+
+ @Value("${ops-app.security.jwt.app_authorization_code}")
+ private String appAuthorizationCode;
+
+ @Value("${ops-app.security.jwt.api_token_expiration}")
+ private long apiTokenExpiration;
+
+ @Value("${ops-app.security.jwt.cms_session_expiration}")
+ private long cmsSessionExpiration;
+
+ @Value("${ops-app.security.jwt.app_session_expiration}")
+ private long appSessionExpiration;
+
+ @Value("${ops-app.security.notification.authorization_code}")
+ private String thirdPartyAuthorization;
+
+ @Value("${ops-app.security.notification.expiration}")
+ private long thirdPartyExpiration;
+
+ @Value("${ops-app.api-logging-level}")
+ private String apiLogLevel;
+
+
+
+ @Bean
+ public FilterRegistrationBean corsHeaderFilter() {
+ FilterRegistrationBean cors = new FilterRegistrationBean<>();
+ cors.setFilter(new CorsHeaderFilter());
+ cors.addUrlPatterns("/api/*");
+ cors.setOrder(1);
+ cors.setName("CorsHeaderFilter");
+ return cors;
+ }
+
+ @Bean
+ public FilterRegistrationBean jwtAuthFilter() {
+ JwtTokenUtil.setSecretKey(secretKey);
+ JwtTokenUtil.setAccessTokenExpiration(apiTokenExpiration);
+ JwtTokenUtil.setAppSessionExpiration(appSessionExpiration);
+ JwtTokenUtil.setCmsSessionExpiration(cmsSessionExpiration);
+ JwtTokenUtil.setThirdPartySecretKey(thirdPartyAuthorization);
+ JwtTokenUtil.setThirdPartyExpiration(thirdPartyExpiration);
+ JwtTokenUtil.setAppLoginAuthorizationCode(appAuthorizationCode);
+ List excludePatterns = Arrays.asList(
+ "/api/v1/authorization/ad_login",
+ "/api/v1/authorization/device_login",
+ "/api/v1/authorization/getAccessToken",
+ "/api/v1/admin/login",
+ "/api/v1/logging/createAppLog",
+ "/api/v1/logging/auditLog/**",
+ "/api/v1/logging/apiLog/**",
+ "/api/v1/logs/**",
+ "/api/v1/esign/capture-logs/**",
+ "/api/v1/esign/auth/**",
+ "/api/v1/esign/**",
+ "/esign-api/api/v1/esign/**",
+ "**/api/v1/esign/**",
+ "/api/v1/sinosecu/auth/**",
+ "/api/v1/sinosecu/**",
+ "/esign-api/api/v1/sinosecu/**",
+ "**/api/v1/sinosecu/**",
+ "/api/v1/esign/ios-capture-submit",
+ "/api/v1/esign/ios-pending-tasks",
+ "/api/v1/esign/ios-task-list",
+ "/api/v1/esign/ios-task-sign",
+ "/api/v1/esign/ios-task/*",
+ "/api/v1/sinosecu/ios-capture-submit",
+ "/api/v1/sinosecu/ios-pending-tasks",
+ "/api/v1/sinosecu/ios-task-list",
+ "/api/v1/sinosecu/ios-task-sign",
+ "/api/v1/sinosecu/ios-task/*",
+ "/api/v1/config/pit_list/*",
+ "/api/v1/profile/*",
+ "/api/v1/profile/getPatronImage/*",
+ "/api/v1/profile/*",
+ "/api/v1/profile/**",
+ "/swagger-ui.html",
+ "/swagger-ui",
+ "/swagger-ui/**",
+ "/swagger*/*",
+ "/v3/**",
+ "/api/v1/notifications/**"
+ );
+ List verifyAppVersionList = Arrays.asList(
+ "/api/v1/Pit/*"
+ );
+ FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
+ JwtAuthFilter filter = new JwtAuthFilter();
+ filter.setExcludePatterns(excludePatterns);
+ filter.setVerifyAppVersionList(verifyAppVersionList);
+ registrationBean.setFilter(filter);
+ registrationBean.addUrlPatterns("/*");
+ registrationBean.setOrder(2);
+ registrationBean.setName("RequestJwtFilter");
+
+ return registrationBean;
+ }
+
+// @Bean
+// public FilterRegistrationBean apiLoggingFilterRegistration(
+// ApiLoggingFilter apiLoggingFilter) {
+// if(apiLoggingFilter!=null){
+// if("ERROR".equalsIgnoreCase(apiLogLevel)){
+// apiLoggingFilter.setDebugLevel("ERROR");
+// apiLoggingFilter.setDebugLevelCode(500);
+// }else if("WARN".equalsIgnoreCase(apiLogLevel)){
+// apiLoggingFilter.setDebugLevel("WARN");
+// apiLoggingFilter.setDebugLevelCode(400);
+// }else{
+// apiLoggingFilter.setDebugLevel("INFO");
+// apiLoggingFilter.setDebugLevelCode(0);
+// }
+// }
+// FilterRegistrationBean registration = new FilterRegistrationBean<>();
+// registration.setFilter(apiLoggingFilter);
+// registration.addUrlPatterns("/api/*");
+// registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
+// registration.setName("apiLoggingFilter");
+// return registration;
+// }
+
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/constants/AppConstants.java b/src/main/java/com/sjmresorts/opsapp/common/constants/AppConstants.java
new file mode 100644
index 0000000..65e9c48
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/constants/AppConstants.java
@@ -0,0 +1,16 @@
+package com.sjmresorts.opsapp.common.constants;
+
+public final class AppConstants {
+ public static final String DEFAULT_CASINO_CODE="GLC";
+ public static final String SUPER_ADMIN_ID="SYSADMIN";
+ public static final String CMS_REFRESH_TOKEN_TYPE="CMS_REFRESH_TOKEN";
+ public static final String APP_REFRESH_TOKEN_TYPE="APP_REFRESH_TOKEN";
+ public static final String THIRD_PARTY_TOKEN_TYPE="THIRD_PARTY_TOKEN";
+ public static final String CMS_API_ACCESS_TOKEN_TYPE="CMS_API_ACCESS_TOKEN";
+ public static final String APP_API_ACCESS_TOKEN_TYPE="APP_API_ACCESS_TOKEN";
+ public static final String EMAIL_NOTIFICATION="EMAIL";
+ public static final String FIREBASE_NOTIFICATION="FCM";
+ public static final int TASK_DEFAULT_EXPIRY=24;
+ public static final int MAX_SESSION_IDLE_TIME=4;
+ public static final String APP_ROUTER_NAME="Mobile App Permission";
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/db/DbDialect.java b/src/main/java/com/sjmresorts/opsapp/common/db/DbDialect.java
new file mode 100644
index 0000000..007de5b
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/db/DbDialect.java
@@ -0,0 +1,75 @@
+package com.sjmresorts.opsapp.common.db;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+
+/**
+ * 多库 DDL / 原生 SQL 方言。业务层继续用 JPA;启动迁移与少量原生 SQL 走此接口。
+ */
+public interface DbDialect {
+
+ DbType type();
+
+ /** Hibernate dialect FQCN */
+ String hibernateDialectClass();
+
+ String varchar(int length);
+
+ /** 大文本(JSON / Base64 引用等) */
+ String text();
+
+ String datetime();
+
+ String boolType();
+
+ String intType();
+
+ String bigintType();
+
+ /** 如:{@code id BIGINT IDENTITY(1,1) NOT NULL PRIMARY KEY} */
+ String bigintIdentityPrimaryKey(String columnName);
+
+ /** {@code ALTER TABLE t ADD [COLUMN] c type} */
+ String alterAddColumn(String table, String column, String typeAndNullability);
+
+ /**
+ * 带默认值的 NOT NULL 列。
+ * @param defaultLiteral 不含引号的字面量,如 {@code CARD}
+ */
+ String alterAddNotNullVarcharWithDefault(
+ String table, String column, int length, String constraintName, String defaultLiteral);
+
+ /**
+ * CREATE TABLE 列尾:NOT NULL + 默认值。
+ * @param constraintName 仅 SQL Server 使用的约束名
+ * @param defaultExpression 已含引号或数字的表达式,如 {@code 'CARD'}、{@code 0}、{@code '[]'}
+ */
+ String notNullWithDefault(String constraintName, String defaultExpression);
+
+ boolean tableExists(JdbcTemplate jdbc, String table);
+
+ boolean columnExists(JdbcTemplate jdbc, String columnTable, String column);
+
+ /** 索引/约束等对象是否存在(跨库尽力而为) */
+ boolean objectExists(JdbcTemplate jdbc, String objectName);
+
+ void renameTable(JdbcTemplate jdbc, String from, String to);
+
+ /** SQL Server 约束/索引重命名;其它库尽力而为或跳过 */
+ void renameObject(JdbcTemplate jdbc, String from, String to);
+
+ /** 在已有 SELECT ... ORDER BY 上包一层「只取 1 行」 */
+ String limitOne(String selectSql);
+
+ /** UTC 时间函数,用于 INSERT 默认值表达式 */
+ String utcTimestampFunction();
+
+ /** 软删除等 UPDATE 用的「当前时间」函数 */
+ String nowFunction();
+
+ default void addColumnIfMissing(
+ JdbcTemplate jdbc, String table, String column, String typeAndNullability) {
+ if (!columnExists(jdbc, table, column)) {
+ jdbc.execute(alterAddColumn(table, column, typeAndNullability));
+ }
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/db/DbDialectConfig.java b/src/main/java/com/sjmresorts/opsapp/common/db/DbDialectConfig.java
new file mode 100644
index 0000000..559d440
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/db/DbDialectConfig.java
@@ -0,0 +1,47 @@
+package com.sjmresorts.opsapp.common.db;
+
+import com.zaxxer.hikari.HikariDataSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.sql.DataSource;
+
+@Configuration
+public class DbDialectConfig {
+
+ private static final Logger log = LoggerFactory.getLogger(DbDialectConfig.class);
+
+ @Bean
+ public DbDialect dbDialect(
+ @Value("${ops-app.db.type:auto}") String configuredType,
+ @Qualifier("opsAppDataSource") DataSource dataSource) {
+ DbType explicit = DbType.fromToken(configuredType);
+ DbType resolved = explicit != null ? explicit : DbType.fromJdbcUrl(resolveJdbcUrl(dataSource));
+ DbDialect dialect = switch (resolved) {
+ case MYSQL -> new MySqlDbDialect();
+ case POSTGRES -> new PostgresDbDialect();
+ case SQLSERVER -> new SqlServerDbDialect();
+ };
+ log.info(
+ "ops-app db dialect: type={}, hibernate={}, configuredType={}",
+ dialect.type(),
+ dialect.hibernateDialectClass(),
+ configuredType);
+ return dialect;
+ }
+
+ private static String resolveJdbcUrl(DataSource dataSource) {
+ if (dataSource instanceof HikariDataSource hikari) {
+ return hikari.getJdbcUrl();
+ }
+ try {
+ return dataSource.getConnection().getMetaData().getURL();
+ } catch (Exception e) {
+ return "";
+ }
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/db/DbType.java b/src/main/java/com/sjmresorts/opsapp/common/db/DbType.java
new file mode 100644
index 0000000..bd69001
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/db/DbType.java
@@ -0,0 +1,36 @@
+package com.sjmresorts.opsapp.common.db;
+
+/** 业务库类型(可由配置或 JDBC URL 推断)。 */
+public enum DbType {
+ SQLSERVER,
+ MYSQL,
+ POSTGRES;
+
+ public static DbType fromToken(String raw) {
+ if (raw == null || raw.isBlank() || "auto".equalsIgnoreCase(raw.trim())) {
+ return null;
+ }
+ String t = raw.trim().toLowerCase();
+ return switch (t) {
+ case "mysql", "mariadb" -> MYSQL;
+ case "postgres", "postgresql", "pg" -> POSTGRES;
+ case "sqlserver", "mssql", "sql-server" -> SQLSERVER;
+ default -> throw new IllegalArgumentException(
+ "Unsupported ops-app.db.type='" + raw + "' (use sqlserver|mysql|postgres|auto)");
+ };
+ }
+
+ public static DbType fromJdbcUrl(String jdbcUrl) {
+ if (jdbcUrl == null) {
+ return SQLSERVER;
+ }
+ String u = jdbcUrl.toLowerCase();
+ if (u.startsWith("jdbc:mysql:") || u.startsWith("jdbc:mariadb:")) {
+ return MYSQL;
+ }
+ if (u.startsWith("jdbc:postgresql:") || u.startsWith("jdbc:postgres:")) {
+ return POSTGRES;
+ }
+ return SQLSERVER;
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/db/MySqlDbDialect.java b/src/main/java/com/sjmresorts/opsapp/common/db/MySqlDbDialect.java
new file mode 100644
index 0000000..901fdf0
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/db/MySqlDbDialect.java
@@ -0,0 +1,138 @@
+package com.sjmresorts.opsapp.common.db;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+
+public final class MySqlDbDialect implements DbDialect {
+
+ @Override
+ public DbType type() {
+ return DbType.MYSQL;
+ }
+
+ @Override
+ public String hibernateDialectClass() {
+ return "org.hibernate.dialect.MySQLDialect";
+ }
+
+ @Override
+ public String varchar(int length) {
+ return "VARCHAR(" + length + ")";
+ }
+
+ @Override
+ public String text() {
+ return "LONGTEXT";
+ }
+
+ @Override
+ public String datetime() {
+ return "DATETIME(6)";
+ }
+
+ @Override
+ public String boolType() {
+ return "TINYINT(1)";
+ }
+
+ @Override
+ public String intType() {
+ return "INT";
+ }
+
+ @Override
+ public String bigintType() {
+ return "BIGINT";
+ }
+
+ @Override
+ public String bigintIdentityPrimaryKey(String columnName) {
+ return columnName + " BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY";
+ }
+
+ @Override
+ public String alterAddColumn(String table, String column, String typeAndNullability) {
+ return "ALTER TABLE " + table + " ADD COLUMN " + column + " " + typeAndNullability;
+ }
+
+ @Override
+ public String alterAddNotNullVarcharWithDefault(
+ String table, String column, int length, String constraintName, String defaultLiteral) {
+ return "ALTER TABLE "
+ + table
+ + " ADD COLUMN "
+ + column
+ + " "
+ + varchar(length)
+ + notNullWithDefault(constraintName, "'" + defaultLiteral + "'");
+ }
+
+ @Override
+ public String notNullWithDefault(String constraintName, String defaultExpression) {
+ // MySQL:列级 DEFAULT;TEXT/BLOB 需表达式形式 DEFAULT (...)
+ return " NOT NULL DEFAULT (" + defaultExpression + ")";
+ }
+
+ @Override
+ public boolean tableExists(JdbcTemplate jdbc, String table) {
+ Integer count = jdbc.queryForObject(
+ "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?",
+ Integer.class,
+ table);
+ return count != null && count > 0;
+ }
+
+ @Override
+ public boolean columnExists(JdbcTemplate jdbc, String table, String column) {
+ Integer count = jdbc.queryForObject(
+ "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?",
+ Integer.class,
+ table,
+ column);
+ return count != null && count > 0;
+ }
+
+ @Override
+ public boolean objectExists(JdbcTemplate jdbc, String objectName) {
+ Integer c1 = jdbc.queryForObject(
+ "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = ?",
+ Integer.class,
+ objectName);
+ if (c1 != null && c1 > 0) {
+ return true;
+ }
+ Integer c2 = jdbc.queryForObject(
+ "SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND INDEX_NAME = ?",
+ Integer.class,
+ objectName);
+ return c2 != null && c2 > 0;
+ }
+
+ @Override
+ public void renameTable(JdbcTemplate jdbc, String from, String to) {
+ jdbc.execute("RENAME TABLE `" + from + "` TO `" + to + "`");
+ }
+
+ @Override
+ public void renameObject(JdbcTemplate jdbc, String from, String to) {
+ // MySQL 约束/索引重命名语法因版本而异;遗留 sinosecu 迁移主要针对 SQL Server
+ }
+
+ @Override
+ public String limitOne(String selectSql) {
+ String sql = selectSql.trim();
+ if (sql.toLowerCase().contains(" limit ")) {
+ return sql;
+ }
+ return sql + " LIMIT 1";
+ }
+
+ @Override
+ public String utcTimestampFunction() {
+ return "UTC_TIMESTAMP(6)";
+ }
+
+ @Override
+ public String nowFunction() {
+ return "CURRENT_TIMESTAMP(6)";
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/db/PostgresDbDialect.java b/src/main/java/com/sjmresorts/opsapp/common/db/PostgresDbDialect.java
new file mode 100644
index 0000000..9bc2f2d
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/db/PostgresDbDialect.java
@@ -0,0 +1,133 @@
+package com.sjmresorts.opsapp.common.db;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+
+public final class PostgresDbDialect implements DbDialect {
+
+ @Override
+ public DbType type() {
+ return DbType.POSTGRES;
+ }
+
+ @Override
+ public String hibernateDialectClass() {
+ return "org.hibernate.dialect.PostgreSQLDialect";
+ }
+
+ @Override
+ public String varchar(int length) {
+ return "VARCHAR(" + length + ")";
+ }
+
+ @Override
+ public String text() {
+ return "TEXT";
+ }
+
+ @Override
+ public String datetime() {
+ return "TIMESTAMP(6)";
+ }
+
+ @Override
+ public String boolType() {
+ return "BOOLEAN";
+ }
+
+ @Override
+ public String intType() {
+ return "INT";
+ }
+
+ @Override
+ public String bigintType() {
+ return "BIGINT";
+ }
+
+ @Override
+ public String bigintIdentityPrimaryKey(String columnName) {
+ return columnName + " BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY";
+ }
+
+ @Override
+ public String alterAddColumn(String table, String column, String typeAndNullability) {
+ return "ALTER TABLE " + table + " ADD COLUMN " + column + " " + typeAndNullability;
+ }
+
+ @Override
+ public String alterAddNotNullVarcharWithDefault(
+ String table, String column, int length, String constraintName, String defaultLiteral) {
+ return "ALTER TABLE "
+ + table
+ + " ADD COLUMN "
+ + column
+ + " "
+ + varchar(length)
+ + notNullWithDefault(constraintName, "'" + defaultLiteral + "'");
+ }
+
+ @Override
+ public String notNullWithDefault(String constraintName, String defaultExpression) {
+ return " NOT NULL DEFAULT " + defaultExpression;
+ }
+
+ @Override
+ public boolean tableExists(JdbcTemplate jdbc, String table) {
+ Integer count = jdbc.queryForObject(
+ "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'public' AND TABLE_NAME = ?",
+ Integer.class,
+ table);
+ return count != null && count > 0;
+ }
+
+ @Override
+ public boolean columnExists(JdbcTemplate jdbc, String table, String column) {
+ Integer count = jdbc.queryForObject(
+ "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'public' AND TABLE_NAME = ? AND COLUMN_NAME = ?",
+ Integer.class,
+ table,
+ column);
+ return count != null && count > 0;
+ }
+
+ @Override
+ public boolean objectExists(JdbcTemplate jdbc, String objectName) {
+ Integer count = jdbc.queryForObject(
+ "SELECT COUNT(*) FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
+ + "WHERE n.nspname = 'public' AND c.relname = ?",
+ Integer.class,
+ objectName);
+ return count != null && count > 0;
+ }
+
+ @Override
+ public void renameTable(JdbcTemplate jdbc, String from, String to) {
+ jdbc.execute("ALTER TABLE " + from + " RENAME TO " + to);
+ }
+
+ @Override
+ public void renameObject(JdbcTemplate jdbc, String from, String to) {
+ if (objectExists(jdbc, from) && !objectExists(jdbc, to)) {
+ jdbc.execute("ALTER INDEX IF EXISTS " + from + " RENAME TO " + to);
+ }
+ }
+
+ @Override
+ public String limitOne(String selectSql) {
+ String sql = selectSql.trim();
+ if (sql.toLowerCase().contains(" limit ")) {
+ return sql;
+ }
+ return sql + " LIMIT 1";
+ }
+
+ @Override
+ public String utcTimestampFunction() {
+ return "(NOW() AT TIME ZONE 'UTC')";
+ }
+
+ @Override
+ public String nowFunction() {
+ return "CURRENT_TIMESTAMP";
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/db/SqlServerDbDialect.java b/src/main/java/com/sjmresorts/opsapp/common/db/SqlServerDbDialect.java
new file mode 100644
index 0000000..9c6a106
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/db/SqlServerDbDialect.java
@@ -0,0 +1,135 @@
+package com.sjmresorts.opsapp.common.db;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+
+public final class SqlServerDbDialect implements DbDialect {
+
+ @Override
+ public DbType type() {
+ return DbType.SQLSERVER;
+ }
+
+ @Override
+ public String hibernateDialectClass() {
+ return "org.hibernate.dialect.SQLServerDialect";
+ }
+
+ @Override
+ public String varchar(int length) {
+ return "NVARCHAR(" + length + ")";
+ }
+
+ @Override
+ public String text() {
+ return "NVARCHAR(MAX)";
+ }
+
+ @Override
+ public String datetime() {
+ return "DATETIME2(7)";
+ }
+
+ @Override
+ public String boolType() {
+ return "BIT";
+ }
+
+ @Override
+ public String intType() {
+ return "INT";
+ }
+
+ @Override
+ public String bigintType() {
+ return "BIGINT";
+ }
+
+ @Override
+ public String bigintIdentityPrimaryKey(String columnName) {
+ return columnName + " BIGINT IDENTITY(1,1) NOT NULL PRIMARY KEY";
+ }
+
+ @Override
+ public String alterAddColumn(String table, String column, String typeAndNullability) {
+ return "ALTER TABLE " + table + " ADD " + column + " " + typeAndNullability;
+ }
+
+ @Override
+ public String alterAddNotNullVarcharWithDefault(
+ String table, String column, int length, String constraintName, String defaultLiteral) {
+ return "ALTER TABLE "
+ + table
+ + " ADD "
+ + column
+ + " "
+ + varchar(length)
+ + notNullWithDefault(constraintName, "'" + defaultLiteral + "'");
+ }
+
+ @Override
+ public String notNullWithDefault(String constraintName, String defaultExpression) {
+ return " NOT NULL CONSTRAINT " + constraintName + " DEFAULT (" + defaultExpression + ")";
+ }
+
+ @Override
+ public boolean tableExists(JdbcTemplate jdbc, String table) {
+ Integer count = jdbc.queryForObject(
+ "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = ?",
+ Integer.class,
+ table);
+ return count != null && count > 0;
+ }
+
+ @Override
+ public boolean columnExists(JdbcTemplate jdbc, String table, String column) {
+ Integer count = jdbc.queryForObject(
+ "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND COLUMN_NAME = ?",
+ Integer.class,
+ table,
+ column);
+ return count != null && count > 0;
+ }
+
+ @Override
+ public boolean objectExists(JdbcTemplate jdbc, String objectName) {
+ Integer count = jdbc.queryForObject(
+ "SELECT COUNT(*) FROM sys.objects WHERE name = ?", Integer.class, objectName);
+ return count != null && count > 0;
+ }
+
+ @Override
+ public void renameTable(JdbcTemplate jdbc, String from, String to) {
+ jdbc.execute("EXEC sp_rename '" + from + "', '" + to + "'");
+ }
+
+ @Override
+ public void renameObject(JdbcTemplate jdbc, String from, String to) {
+ jdbc.execute("EXEC sp_rename '" + from + "', '" + to + "', 'OBJECT'");
+ }
+
+ @Override
+ public String limitOne(String selectSql) {
+ String sql = selectSql.trim();
+ String lower = sql.toLowerCase();
+ if (lower.startsWith("select top")) {
+ return sql;
+ }
+ if (lower.startsWith("select distinct ")) {
+ return "SELECT DISTINCT TOP 1 " + sql.substring("select distinct ".length());
+ }
+ if (lower.startsWith("select ")) {
+ return "SELECT TOP 1 " + sql.substring("select ".length());
+ }
+ return sql;
+ }
+
+ @Override
+ public String utcTimestampFunction() {
+ return "SYSUTCDATETIME()";
+ }
+
+ @Override
+ public String nowFunction() {
+ return "CURRENT_TIMESTAMP";
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/dtos/BaseApiResponse.java b/src/main/java/com/sjmresorts/opsapp/common/dtos/BaseApiResponse.java
new file mode 100644
index 0000000..2033ef8
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/dtos/BaseApiResponse.java
@@ -0,0 +1,23 @@
+package com.sjmresorts.opsapp.common.dtos;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class BaseApiResponse {
+
+ @Schema(description = "Indicate API success status")
+ private boolean success=false;
+
+ @Schema(description = "Status Code of response",example="200")
+ private String code;
+
+ @Schema(description = "Description of result")
+ private String message;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/dtos/BasePageRequest.java b/src/main/java/com/sjmresorts/opsapp/common/dtos/BasePageRequest.java
new file mode 100644
index 0000000..eec5141
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/dtos/BasePageRequest.java
@@ -0,0 +1,17 @@
+package com.sjmresorts.opsapp.common.dtos;
+
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class BasePageRequest {
+ private int startRow = 0;
+ private int endRow = 20;
+ private int pageSize = 20;
+ private String sortOrder;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/dtos/DataTableResult.java b/src/main/java/com/sjmresorts/opsapp/common/dtos/DataTableResult.java
new file mode 100644
index 0000000..2a24ed7
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/dtos/DataTableResult.java
@@ -0,0 +1,24 @@
+package com.sjmresorts.opsapp.common.dtos;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Setter
+@Getter
+public class DataTableResult {
+
+ private List rowData;
+
+ private int pageSize = 10;
+
+ private long totalRecords = 0;
+
+ private long startRow = 0;
+
+ public DataTableResult() {
+ this.rowData = new ArrayList<>();
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/dtos/LDAP/ADAccountInfo.java b/src/main/java/com/sjmresorts/opsapp/common/dtos/LDAP/ADAccountInfo.java
new file mode 100644
index 0000000..d234e69
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/dtos/LDAP/ADAccountInfo.java
@@ -0,0 +1,45 @@
+package com.sjmresorts.opsapp.common.dtos.LDAP;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class ADAccountInfo {
+
+ @Schema(description = "LDAP User sn")
+ private String sn;
+
+ @Schema(description = "LDAP User Account Code")
+ private String adAccountCode;
+
+ @Schema(description = "LDAP User Given Name")
+ private String givenName;
+
+ @Schema(description = "LDAP User Display Name")
+ private String displayName;
+
+ @Schema(description = "LDAP User Title")
+ private String title;
+
+ @Schema(description = "LDAP User Department")
+ private String department;
+
+ @Schema(description = "LDAP User Mail")
+ private String mail;
+
+ @Schema(description = "LDAP User Company")
+ private String company;
+
+ @Schema(description = "LDAP User Account Control")
+ private String userAccountControl;
+
+ @Schema(description = "LDAP User Belong Direct Groups")
+ private List directGroups;
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/dtos/SchemaEntry.java b/src/main/java/com/sjmresorts/opsapp/common/dtos/SchemaEntry.java
new file mode 100644
index 0000000..ee132db
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/dtos/SchemaEntry.java
@@ -0,0 +1,25 @@
+package com.sjmresorts.opsapp.common.dtos;
+
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@Schema(description = "Schema info of request")
+public class SchemaEntry {
+
+ @Schema(description = "Unique code of the ELO terminal",example="ELO-001")
+ @NotBlank(message = "Terminal code must not be blank")
+ private String terminalCode;
+
+ @Schema(description = "Unique ID for the transaction",example="txn-12345")
+ @NotBlank(message = "Transaction ID must not be blank")
+ private String transactionId;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/dtos/UserCredentialsDto.java b/src/main/java/com/sjmresorts/opsapp/common/dtos/UserCredentialsDto.java
new file mode 100644
index 0000000..7587b1d
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/dtos/UserCredentialsDto.java
@@ -0,0 +1,18 @@
+package com.sjmresorts.opsapp.common.dtos;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class UserCredentialsDto {
+ private String username;
+ private String password;
+ private List adGroup;
+
+ public UserCredentialsDto(String username, String password, List adGroup) {
+ this.username = username;
+ this.password = password;
+ this.adGroup = adGroup;
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/dtos/httpclient/HttpRequestDefine.java b/src/main/java/com/sjmresorts/opsapp/common/dtos/httpclient/HttpRequestDefine.java
new file mode 100644
index 0000000..d7afac8
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/dtos/httpclient/HttpRequestDefine.java
@@ -0,0 +1,24 @@
+package com.sjmresorts.opsapp.common.dtos.httpclient;
+
+import java.util.Map;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.springframework.web.multipart.MultipartFile;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class HttpRequestDefine {
+ private String requestUrl;
+ private String requestMethod="GET";
+ private int timeout=0;
+ private String entityContent="";
+ private String requestCharSet ="UTF-8";
+ private String responseCharSet = "UTF-8";
+ private boolean isUpload =false;
+ private MultipartFile uploadFile = null;
+ private String fileParaName;
+ private Map headers;
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/dtos/httpclient/HttpResponseResult.java b/src/main/java/com/sjmresorts/opsapp/common/dtos/httpclient/HttpResponseResult.java
new file mode 100644
index 0000000..dc0f012
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/dtos/httpclient/HttpResponseResult.java
@@ -0,0 +1,39 @@
+package com.sjmresorts.opsapp.common.dtos.httpclient;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.Date;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class HttpResponseResult {
+
+ private int statusCode;
+ private String responseContent;
+ private String errorMessage;
+ private String requestUrl;
+ private String requestContent;
+ private String requestTime;
+ private String responseTime;
+ private Date requestTimeVal;
+ private Date responseTimeVal;
+
+ public boolean isSuccessful() {
+ return statusCode >= 200 && statusCode < 300;
+ }
+
+ public boolean isTransportError() {
+ return statusCode < 0;
+ }
+
+ public String bodyOrEmpty() {
+ return responseContent != null ? responseContent : "";
+ }
+
+ public String errorOrEmpty() {
+ return errorMessage != null ? errorMessage : "";
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/exception/GlobalExceptionHandler.java b/src/main/java/com/sjmresorts/opsapp/common/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000..95c2209
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/exception/GlobalExceptionHandler.java
@@ -0,0 +1,35 @@
+package com.sjmresorts.opsapp.common.exception;
+
+import com.sjmresorts.opsapp.common.dtos.BaseApiResponse;
+import com.sjmresorts.opsapp.common.exceptions.TokenExpiredException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * 全局异常处理器
+ */
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+ private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+ /**
+ * 处理Token过期异常
+ * 返回401状态码
+ */
+ @ExceptionHandler(TokenExpiredException.class)
+ public ResponseEntity handleTokenExpiredException(TokenExpiredException e) {
+ logger.warn("Token expired: {}", e.getMessage());
+
+ BaseApiResponse response = new BaseApiResponse();
+ response.setSuccess(false);
+ response.setCode("401");
+ response.setMessage("Token has expired");
+
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
+ }
+}
+
diff --git a/src/main/java/com/sjmresorts/opsapp/common/exceptions/TokenExpiredException.java b/src/main/java/com/sjmresorts/opsapp/common/exceptions/TokenExpiredException.java
new file mode 100644
index 0000000..aa65443
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/exceptions/TokenExpiredException.java
@@ -0,0 +1,17 @@
+package com.sjmresorts.opsapp.common.exceptions;
+
+/**
+ * Token过期异常
+ * 当JWT token过期时抛出此异常
+ */
+public class TokenExpiredException extends RuntimeException {
+
+ public TokenExpiredException(String message) {
+ super(message);
+ }
+
+ public TokenExpiredException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
+
diff --git a/src/main/java/com/sjmresorts/opsapp/common/filters/CorsHeaderFilter.java b/src/main/java/com/sjmresorts/opsapp/common/filters/CorsHeaderFilter.java
new file mode 100644
index 0000000..9da8381
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/filters/CorsHeaderFilter.java
@@ -0,0 +1,54 @@
+package com.sjmresorts.opsapp.common.filters;
+
+import com.sjmresorts.opsapp.common.utils.NetworkUtil;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.NonNull;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+/**
+ * 所有 /api 请求统一加 CORS(含 OPTIONS 预检)。
+ * Esign 等 JWT 白名单路径会跳过 JwtAuthFilter,仍需本过滤器处理跨域。
+ */
+public class CorsHeaderFilter extends OncePerRequestFilter {
+
+ private static boolean isPrinterWebSocketPath(HttpServletRequest request) {
+ String uri = request.getRequestURI();
+ return uri != null && (uri.contains("/esign/ws/printer-tasks") || uri.contains("/sinosecu/ws/printer-tasks"));
+ }
+
+ private static boolean isWebSocketUpgrade(HttpServletRequest request) {
+ String upgrade = request.getHeader("Upgrade");
+ return upgrade != null && upgrade.equalsIgnoreCase("websocket");
+ }
+
+ @Override
+ protected boolean shouldNotFilter(HttpServletRequest request) {
+ // WebSocket 升级请求不能提前写 CORS 响应头,否则会破坏 101 握手
+ return isPrinterWebSocketPath(request) && isWebSocketUpgrade(request);
+ }
+
+ @Override
+ protected void doFilterInternal(
+ @NonNull HttpServletRequest request,
+ @NonNull HttpServletResponse response,
+ @NonNull FilterChain filterChain
+ ) throws ServletException, IOException {
+ NetworkUtil.checkAndSetAllowOrigin(request, response);
+ response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
+ response.addHeader("Access-Control-Allow-Headers",
+ "Content-Type, Authorization, clientId, X-CMS-Operator, X-Client-Type, X-Requested-With, Accept, Origin");
+ response.addHeader("Access-Control-Allow-Credentials", "true");
+ response.addHeader("Access-Control-Max-Age", "3600");
+
+ if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
+ response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ return;
+ }
+ filterChain.doFilter(request, response);
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/filters/JwtAuthFilter.java b/src/main/java/com/sjmresorts/opsapp/common/filters/JwtAuthFilter.java
new file mode 100644
index 0000000..c1c603f
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/filters/JwtAuthFilter.java
@@ -0,0 +1,116 @@
+package com.sjmresorts.opsapp.common.filters;
+
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.exceptions.TokenExpiredException;
+import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
+import com.sjmresorts.opsapp.common.utils.NetworkUtil;
+import jakarta.annotation.Resource;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.NonNull;
+import lombok.Setter;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+import java.util.List;
+
+public class JwtAuthFilter extends OncePerRequestFilter {
+
+ private final AntPathMatcher pathMatcher = new AntPathMatcher();
+ private final Logger logger = LoggerFactory.getLogger(JwtAuthFilter.class);
+ @Setter
+ private List excludePatterns;
+
+ @Setter
+ private List verifyAppVersionList;
+
+
+
+ @Override
+ protected void doFilterInternal(
+ @NonNull HttpServletRequest request,
+ @NonNull HttpServletResponse response,
+ @NonNull FilterChain filterChain
+ ) throws ServletException, IOException{
+
+ NetworkUtil.checkAndSetAllowOrigin(request,response);
+ NetworkUtil.setSecurityHeaders(request,response);
+ // 仅解析 Bearer;Nginx auth_basic 等会带 Authorization: Basic,不能当作 JWT 校验
+ String token = extractBearerToken(request.getHeader("Authorization"));
+ if(StringUtils.isNotBlank(token)) {
+ JWTVerifier jwtVerifier = JwtTokenUtil.getJwtVerifier();
+ try {
+ jwtVerifier.verify(token);
+ }catch (TokenExpiredException e) {
+ sendErrorResponse(response,HttpServletResponse.SC_UNAUTHORIZED,"Token Expired");
+ request.setAttribute("AUTH_EXCEPTION","Token Expired");
+ return;
+ }catch (Exception e) {
+ sendErrorResponse(response,HttpServletResponse.SC_UNAUTHORIZED,"Unauthorized");
+ request.setAttribute("AUTH_EXCEPTION","Unauthorized");
+ return;
+ }
+ }else if(!"OPTIONS".equals(request.getMethod())) {
+ sendErrorResponse(response,HttpServletResponse.SC_UNAUTHORIZED,"Unauthorized");
+ request.setAttribute("AUTH_EXCEPTION","Unauthorized");
+ return;
+ }
+ filterChain.doFilter(request, response);
+ }
+
+
+ @Override
+ protected boolean shouldNotFilter(@NonNull HttpServletRequest request) {
+ if (excludePatterns == null) {
+ return false;
+ }
+ String path = normalizeRequestPath(request);
+ return excludePatterns.stream()
+ .anyMatch(pattern -> pathMatcher.match(pattern, path));
+ }
+
+ /** 仅 Bearer JWT;Basic/Digest 等忽略,避免经 Nginx 鉴权后误报 401。 */
+ static String extractBearerToken(String authorizationHeader) {
+ if (StringUtils.isBlank(authorizationHeader)) {
+ return null;
+ }
+ String h = authorizationHeader.trim();
+ if (h.regionMatches(true, 0, "Bearer ", 0, 7)) {
+ String t = h.substring(7).trim();
+ return StringUtils.isNotBlank(t) ? t : null;
+ }
+ return null;
+ }
+
+ private static String normalizeRequestPath(HttpServletRequest request) {
+ String uri = request.getRequestURI();
+ if (uri == null || uri.isEmpty()) {
+ return "/";
+ }
+ while (uri.contains("//")) {
+ uri = uri.replace("//", "/");
+ }
+ return uri;
+ }
+
+ private boolean needCheckAppVersion(@NonNull HttpServletRequest request){
+ if(verifyAppVersionList==null || verifyAppVersionList.isEmpty()){
+ return false;
+ }
+ return verifyAppVersionList.stream()
+ .anyMatch(pattern -> pathMatcher.match(pattern, request.getRequestURI()));
+ }
+
+ private void sendErrorResponse(HttpServletResponse response, int errorCode, String errorMessage) throws IOException {
+ response.setStatus(errorCode);
+ response.setContentType("application/text");
+ response.setCharacterEncoding("UTF-8");
+ response.getWriter().write(errorMessage);
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/utils/AESUtil.java b/src/main/java/com/sjmresorts/opsapp/common/utils/AESUtil.java
new file mode 100644
index 0000000..428d8a1
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/utils/AESUtil.java
@@ -0,0 +1,150 @@
+package com.sjmresorts.opsapp.common.utils;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.SecureRandom;
+import java.util.Base64;
+import java.util.Map;
+
+public class AESUtil {
+
+ private static final String AES = "AES";
+ private static final int GCM_IV_LENGTH = 12; // GCM推荐使用12字节的IV
+ private static final int GCM_TAG_LENGTH = 16; // GCM认证标签长度(128位)
+
+ private static final Base64.Encoder base64Encoder = Base64.getEncoder();
+ private static final Base64.Decoder base64Decoder = Base64.getDecoder();
+
+// static {
+// java.security.Security.setProperty("crypto.policy", "unlimited");
+// removeCryptographyRestrictions();
+// }
+
+ public static void removeCryptographyRestrictions() {
+ try {
+ /*
+ * Do the following, but with reflection to bypass access checks:
+ * JceSecurity.isRestricted = false;
+ * JceSecurity.defaultPolicy.perms.clear();
+ * JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
+ */
+ final Class> jceSecurity = Class.forName("javax.crypto.JceSecurity");
+ final Class> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
+ final Class> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");
+
+ final Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
+ isRestrictedField.setAccessible(true);
+ // 去除isRestricted的final限制
+ final Field modifiersField = Field.class.getDeclaredField("modifiers");
+ modifiersField.setAccessible(true);
+ modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
+ isRestrictedField.set(null, false);
+
+ final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
+ defaultPolicyField.setAccessible(true);
+ final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);
+
+ final Field perms = cryptoPermissions.getDeclaredField("perms");
+ perms.setAccessible(true);
+ ((Map, ?>) perms.get(defaultPolicy)).clear();
+
+ final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
+ instance.setAccessible(true);
+ defaultPolicy.add((Permission) instance.get(null));
+
+ } catch (final Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static SecretKey generateKey() throws Exception {
+ KeyGenerator keyGen = KeyGenerator.getInstance("AES");
+ keyGen.init(256);
+ return keyGen.generateKey();
+ }
+ public static SecretKey stringToSecretKey(String keyString) {
+ byte[] keyBytes = base64Decoder.decode(keyString);
+ return new SecretKeySpec(keyBytes, AES);
+ }
+ public static String secretKeyToString(SecretKey secretKey) {
+ byte[] keyBytes = secretKey.getEncoded();
+ return base64Encoder.encodeToString(keyBytes);
+ }
+ /**
+ * AES加密
+ */
+ public static String AES256Encrypt(String key, String content) {
+ try {
+ SecretKey secretKey = stringToSecretKey(key);
+ Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+ byte[] iv = new byte[GCM_IV_LENGTH];
+ SecureRandom secureRandom = new SecureRandom();
+ secureRandom.nextBytes(iv); // 生成随机的IV
+ GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey, spec);
+
+ byte[] ciphertext = cipher.doFinal(content.getBytes());
+ byte[] encryptedData = new byte[iv.length + ciphertext.length];
+ System.arraycopy(iv, 0, encryptedData, 0, iv.length);
+ System.arraycopy(ciphertext, 0, encryptedData, iv.length, ciphertext.length);
+ return base64Encoder.encodeToString(encryptedData);
+
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+ return null;
+ }
+ /**
+ * AES解密
+ */
+ public static String AES256Decrypt(String key, String content) {
+ try {
+ SecretKey secretKey = stringToSecretKey(key);
+ byte[] encryptedData = base64Decoder.decode(content);
+ Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+ // 从加密数据中提取IV
+ byte[] iv = new byte[GCM_IV_LENGTH];
+ System.arraycopy(encryptedData, 0, iv, 0, iv.length);
+
+ GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);
+
+ // 解密数据
+ byte[] ciphertext = new byte[encryptedData.length - GCM_IV_LENGTH];
+ System.arraycopy(encryptedData, GCM_IV_LENGTH, ciphertext, 0, ciphertext.length);
+ byte[] byteDecode = cipher.doFinal(ciphertext);
+ return new String(byteDecode, StandardCharsets.UTF_8);
+ } catch (Exception e) {
+ //e.printStackTrace();
+ }
+ return null;
+ }
+
+ public static String sha256(String plainString) {
+ String cipherString = null;
+ try {
+ // 获取实例(SHA-512同理)
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+ // 计算摘要
+ byte[] cipherBytes = messageDigest.digest(plainString.getBytes(StandardCharsets.UTF_8));
+ // 输出为16进制字符串
+ StringBuilder sb = new StringBuilder();
+ for (byte b : cipherBytes) {
+ sb.append(String.format("%02x", b));
+ }
+ cipherString = sb.toString();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return cipherString;
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/utils/DaoUtil.java b/src/main/java/com/sjmresorts/opsapp/common/utils/DaoUtil.java
new file mode 100644
index 0000000..5b0ca5e
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/utils/DaoUtil.java
@@ -0,0 +1,92 @@
+package com.sjmresorts.opsapp.common.utils;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import jakarta.persistence.EntityManager;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Strings;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+
+
+public class DaoUtil {
+
+ public static int TYPE_STRING = 0;
+ public static int TYPE_DATE = 1;
+ public static int TYPE_INT = 2;
+ public static int TYPE_DOUBLE = 3;
+ public static int TYPE_FLOAT = 4;
+ private static EntityManager entityManager;
+ public static final int BATCH_SIZE = 100;
+
+ public static EntityManager getEntityManager() {
+ return entityManager;
+ }
+
+ public static void setEntityManager(EntityManager srcEntityManager) {
+ entityManager = srcEntityManager;
+ }
+
+ public static Sort buildSortByString(String sortProperties, String defaultSortProperty) {
+ Sort sort = null;
+ String sortColumn;
+ String sortInfo = sortProperties;
+ Direction defaultSortDir = Direction.ASC;
+ if (StringUtils.isBlank(sortInfo)) {
+ sortInfo = defaultSortProperty;
+ }
+ String[] listSortProperty = sortInfo.split(";");
+ for (String s : listSortProperty) {
+ String[] propertyAttr = s.split("-");
+ Direction currentDir = defaultSortDir;
+ if (propertyAttr.length > 1) {
+ sortColumn = propertyAttr[0];
+ currentDir = Strings.CI.equals("DESC", propertyAttr[1]) ? Direction.DESC : Direction.ASC;
+ } else {
+ sortColumn = s;
+ }
+ if (sort == null) {
+ sort = Sort.by(currentDir, sortColumn);
+ } else {
+ sort = sort.and(Sort.by(currentDir, sortColumn));
+ }
+ }
+ return sort;
+ }
+
+ public static List parseMultipleFilter(String filerValues) {
+ List returnVals = new ArrayList<>();
+ if (StringUtils.isNotBlank(filerValues)) {
+ String[] searchIds = filerValues.split(";");
+ Collections.addAll(returnVals, searchIds);
+ }
+ return returnVals;
+ }
+
+ public static List parseMultipleFilter(String filerValues, Class valueType) {
+ List returnVals = new ArrayList<>();
+ if (StringUtils.isNotBlank(filerValues)) {
+ Constructor classConstructor = null;
+ try {
+ classConstructor = valueType.getConstructor(String.class);
+ } catch (Exception ignore) {
+ }
+ if (classConstructor != null) {
+ String[] listSplitValues = filerValues.split(";");
+ for (String listSplitValue : listSplitValues) {
+ try {
+ T val = classConstructor.newInstance(listSplitValue);
+ returnVals.add(val);
+ } catch (Exception ignore) {
+
+ }
+ }
+ }
+ }
+ return returnVals;
+ }
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/utils/DateTimeUtil.java b/src/main/java/com/sjmresorts/opsapp/common/utils/DateTimeUtil.java
new file mode 100644
index 0000000..561c753
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/utils/DateTimeUtil.java
@@ -0,0 +1,206 @@
+package com.sjmresorts.opsapp.common.utils;
+
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Calendar;
+import java.util.Date;
+
+public class DateTimeUtil {
+
+ /**
+ * 根据String型时间,获取long型时间,单位毫秒
+ * @param inVal 时间字符串
+ * @return long型时间
+ */
+ public static long fromDateStringToLong(String inVal) {
+ Date date = null;
+ SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS");
+ try {
+ date = inputFormat.parse(inVal);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return date.getTime();
+ }
+ public static long getTimeSpanSSS(Date startDate,Date endDate) {
+ SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss:SSS");
+ String startStr = timeFormat.format(startDate);
+ String endStr = timeFormat.format(endDate);
+
+ long startMS = fromDateStringToLong(startStr);
+ long endMS = fromDateStringToLong(endStr);
+ long returnMS = endMS-startMS;
+
+ return returnMS;
+ }
+ public static Date getDateFromString(String dateTimeValue,String dateFormat) {
+ SimpleDateFormat dtFormat = new SimpleDateFormat(dateFormat);
+ Date returnDate = null;
+ if (dateTimeValue != null && StringUtils.isNotBlank(dateTimeValue)) {
+ try {
+ returnDate = dtFormat.parse(dateTimeValue);
+ } catch (Exception err) {
+
+ }
+ }
+ return returnDate;
+ }
+
+ public static String getDateByFormat(Date dtDateTime, String strFormat) {
+
+ String returnDate = null;
+
+ if (dtDateTime != null) {
+ try {
+ SimpleDateFormat formatter;
+ formatter = new SimpleDateFormat(strFormat);
+ returnDate = formatter.format(dtDateTime);
+ } catch (Exception err) {
+
+ }
+ }
+ return returnDate;
+ }
+
+
+ public static String getCurrentDate(String strFormat) {
+
+ return getDateByFormat(new Date(), strFormat);
+ }
+
+ public static Date getCurrentDateTime(String strFormat) {
+ SimpleDateFormat dtFormat = new SimpleDateFormat(strFormat);
+ String dateTimeValue = getDateByFormat(new Date(), strFormat);
+
+ Date returnDate = null;
+ try {
+ returnDate = dtFormat.parse(dateTimeValue);
+ } catch (Exception err) {
+
+ }
+
+ return returnDate;
+ }
+ public static Date addSecond(Date nowTime,Integer seconds) {
+ long getTime = nowTime.getTime();
+ long diffTime = seconds * 1000L;
+ long newTime = getTime+diffTime;
+ String strTime = getDateByFormatUseTimeStamp(newTime,"yyyy-MM-dd HH:mm:ss");
+ return getDateFromString(strTime,"yyyy-MM-dd HH:mm:ss");
+ }
+ public static Date addMin(Date nowTime,Integer mins) {
+ long getTime = nowTime.getTime();
+ long diffTime = mins * 60000L;
+ long newTime = getTime+diffTime;
+ String strTime = getDateByFormatUseTimeStamp(newTime,"yyyy-MM-dd HH:mm:ss");
+ return getDateFromString(strTime,"yyyy-MM-dd HH:mm:ss");
+ }
+ public static Date addDay(Date currdate,Integer days) {
+ Calendar ca = Calendar.getInstance();
+ ca.setTime(currdate);
+ ca.add(Calendar.DATE, days);
+ Date newTime = ca.getTime();
+ return newTime;
+ }
+ public static Date addMonth(Date currdate,Integer months) {
+ Calendar ca = Calendar.getInstance();
+ ca.setTime(currdate);
+ ca.add(Calendar.MONTH, months);
+ Date newTime = ca.getTime();
+ return newTime;
+ }
+ public static Date addYear(Date currdate,Integer years) {
+ Calendar ca = Calendar.getInstance();
+ ca.setTime(currdate);
+ ca.add(Calendar.YEAR, years);
+ Date newTime = ca.getTime();
+ return newTime;
+ }
+ public static String getDateByFormatUseTimeStamp(Long timeStamp, String strFormat) {
+
+ String returnDate = null;
+
+ if (timeStamp != null) {
+ try {
+ SimpleDateFormat formatter;
+ formatter = new SimpleDateFormat(strFormat);
+ returnDate = formatter.format(timeStamp);
+ } catch (Exception err) {
+
+ }
+ }
+ return returnDate;
+ }
+
+ public static String getDiffDate(Date DateStart, Date DateEnd, String dateType) {
+ String returnDate = null;
+ try {
+
+ Instant instant1 = DateStart.toInstant();
+ Instant instant2 = DateEnd.toInstant();
+
+ Duration duration = Duration.between(instant1, instant2);
+
+ //long diffTime = DateEnd.getTime() - DateStart.getTime();
+ if(dateType == "day") {
+ //returnDate = Long.toString(diffTime / 1000 / (60 * 60 * 24));
+ long days = duration.toDays();
+ returnDate = Long.toString(days);
+ }else if(dateType == "hour") {
+ //returnDate = Long.toString(diffTime / 1000 / (60 * 60));
+ long hours = duration.toHours();
+ returnDate = Long.toString(hours);
+ }else { //minute
+ long minutes = duration.toMinutes();
+ returnDate = Long.toString(minutes);
+ }
+ } catch (Exception err) {
+
+ }
+ return returnDate;
+ }
+ public static boolean isValidDate(String dateStr) {
+ try {
+ dateStr=dateStr.split(" ")[0];
+ String[] parts = dateStr.split("-");
+
+ int year = Integer.parseInt(parts[0]);
+ int month = Integer.parseInt(parts[1]);
+ int day = Integer.parseInt(parts[2]);
+
+
+ if (month < 1 || month > 12) {
+ return false;
+ }
+
+ YearMonth yearMonth = YearMonth.of(year, month);
+ int maxDay = yearMonth.lengthOfMonth();
+
+ boolean isLeapYear = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
+ if (month == 2 && isLeapYear) {
+ maxDay = 29;
+ } else if (month == 2) {
+ maxDay = 28;
+ }
+
+ if (day < 1 || day > maxDay) {
+ return false;
+ }
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ LocalDate.parse(dateStr, formatter);
+ return true;
+ } catch (DateTimeParseException e) {
+ return false;
+ }
+ }
+
+
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/utils/HttpClientUtil.java b/src/main/java/com/sjmresorts/opsapp/common/utils/HttpClientUtil.java
new file mode 100644
index 0000000..b6a0f6e
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/utils/HttpClientUtil.java
@@ -0,0 +1,284 @@
+package com.sjmresorts.opsapp.common.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.security.cert.CertificateException;
+
+import com.sjmresorts.opsapp.common.dtos.httpclient.HttpRequestDefine;
+import com.sjmresorts.opsapp.common.dtos.httpclient.HttpResponseResult;
+import jakarta.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hc.client5.http.classic.methods.HttpDelete;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.classic.methods.HttpPut;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
+import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
+import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
+import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
+import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner;
+import org.apache.hc.client5.http.ssl.TrustAllStrategy;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.config.Registry;
+import org.apache.hc.core5.http.config.RegistryBuilder;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.ssl.SSLContextBuilder;
+import org.apache.hc.core5.util.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.net.ProxySelector;
+
+
+public class HttpClientUtil {
+ private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
+ //-----------
+ public static HttpResponseResult processHttpRequest(HttpRequestDefine request) {
+ HttpResponseResult responseResult = new HttpResponseResult();
+ CloseableHttpClient httpclient=null;
+ try {
+ //--
+ RequestConfig.Builder configBuilder = RequestConfig.custom();
+ if(request.getTimeout()>0) {
+ int timeout = request.getTimeout();
+ configBuilder.setConnectionRequestTimeout(Timeout.ofMicroseconds(timeout));
+ }
+ RequestConfig requestConfig = configBuilder.build();
+ //--
+ String requestUrl =request.getRequestUrl();
+ HttpClientBuilder clientBuilder = HttpClients.custom()
+ .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault()));
+ if(requestUrl!=null && requestUrl.startsWith("https://")) {
+ SSLContext sslContext = null;
+ try {
+ sslContext = SSLContextBuilder.create().loadTrustMaterial(TrustAllStrategy.INSTANCE).build();
+ } catch (Exception e) {
+ logger.error("Create Http client SSL context failed:{}",e.getMessage());
+ }
+ if(sslContext==null){
+ responseResult.setStatusCode(-1);
+ responseResult.setErrorMessage("Init http client SSL failed");
+ return responseResult;
+ }else{
+ NoopHostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
+ DefaultClientTlsStrategy tlsStrategy = new DefaultClientTlsStrategy(sslContext, hostnameVerifier);
+ PoolingHttpClientConnectionManager connectionManager =
+ PoolingHttpClientConnectionManagerBuilder.create()
+ .setTlsSocketStrategy(tlsStrategy)
+ .setMaxConnTotal(100)
+ .setMaxConnPerRoute(10)
+ .build();
+ HttpClientBuilder httpClientBuilder = HttpClients.custom()
+ .setConnectionManager(connectionManager)
+ .setDefaultRequestConfig(requestConfig)
+ .setRoutePlanner(new SystemDefaultRoutePlanner(ProxySelector.getDefault()));
+ httpclient = httpClientBuilder.build();
+ }
+ }else {
+ httpclient = clientBuilder.build();
+ }
+
+ HttpClientResponseHandler responseHandler = (ClassicHttpResponse responseObject) -> {
+ try {
+ responseResult.setStatusCode(responseObject.getCode());
+ HttpEntity responseEntity = responseObject.getEntity();
+ if (responseEntity != null) {
+ String responseBody = EntityUtils.toString(responseEntity, Charset.forName(request.getResponseCharSet()));
+ EntityUtils.consume(responseEntity);
+ responseResult.setResponseContent(responseBody);
+ }
+ }catch(Exception err){
+ responseResult.setStatusCode(-1);
+ responseResult.setErrorMessage(err.getMessage());
+ }
+ return responseResult;
+ };
+ //--
+ responseResult.setRequestUrl(requestUrl);
+ if("POST".equalsIgnoreCase(request.getRequestMethod())) {
+ HttpPost httppost = new HttpPost(requestUrl);
+ httppost.setConfig(requestConfig);
+
+ if(!request.isUpload()) {
+ if(request.getHeaders()!=null) {
+ Map headermap = request.getHeaders();
+ for (Map.Entry entry : headermap.entrySet()) {
+ if(!"Content-Length".equalsIgnoreCase(entry.getKey())){
+ httppost.addHeader(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ httppost.setEntity(new StringEntity(
+ request.getEntityContent(),
+ resolveEntityContentType(request.getHeaders())));
+ responseResult.setRequestContent(request.getEntityContent());
+ httpclient.execute(httppost,responseHandler);
+ }else {
+ if(request.getHeaders()!=null) {
+ Map headermap = request.getHeaders();
+ for (Map.Entry entry : headermap.entrySet()) {
+ httppost.addHeader(entry.getKey(), entry.getValue());
+ }
+ }
+ MultipartFile processFile = request.getUploadFile();
+ MultipartEntityBuilder builder = MultipartEntityBuilder.create();
+ builder.addBinaryBody(request.getFileParaName(), processFile.getInputStream(), ContentType.MULTIPART_FORM_DATA, processFile.getName());
+ HttpEntity entity = builder.build();
+ httppost.setEntity(entity);
+ httpclient.execute(httppost,responseHandler);
+ }
+
+ }else if("PUT".equalsIgnoreCase(request.getRequestMethod())){
+ HttpPut httpput = new HttpPut(requestUrl);
+ httpput.setConfig(requestConfig);
+ if(request.getHeaders()!=null) {
+ Map headermap = request.getHeaders();
+ for (Map.Entry entry : headermap.entrySet()) {
+ if(!"Content-Length".equalsIgnoreCase(entry.getKey())){
+ httpput.addHeader(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ httpput.setEntity(new StringEntity(
+ request.getEntityContent(),
+ resolveEntityContentType(request.getHeaders())));
+ responseResult.setRequestContent(request.getEntityContent());
+ httpclient.execute(httpput,responseHandler);
+ }else if("DELETE".equalsIgnoreCase(request.getRequestMethod())){
+ HttpDelete httpDelete = new HttpDelete(requestUrl);
+ httpDelete.setConfig(requestConfig);
+ httpclient.execute(httpDelete,responseHandler);
+ }else {
+ //-- Get
+ HttpGet httpget = new HttpGet(requestUrl);
+ httpget.setConfig(requestConfig);
+ if(request.getHeaders()!=null) {
+ Map headermap = request.getHeaders();
+ for (Map.Entry entry : headermap.entrySet()) {
+ if(!"Content-Length".equalsIgnoreCase(entry.getKey())){
+ httpget.addHeader(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ httpclient.execute(httpget,responseHandler);
+ }
+ //--
+ }catch(Exception err) {
+ responseResult.setStatusCode(-1);
+ responseResult.setErrorMessage(err.getMessage());
+ logger.error("Request HTTP[URL:{}][Method:{}] error:{}",request.getRequestUrl(),request.getRequestMethod(),err.getMessage());
+ }finally {
+ try {
+ if(httpclient!=null) {
+ httpclient.close();
+ }
+ }catch(Exception err) {
+
+ }
+ }
+ return responseResult;
+ }
+ //-----------
+ public static SSLContext getSSLContext() {
+ try {
+ // 这里可以填两种值 TLS和LLS , 具体差别可以自行搜索
+ SSLContext sc = SSLContext.getInstance("TLS");
+ // 构建新对象
+ X509TrustManager manager = new X509TrustManager() {
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String arg1) throws CertificateException {
+ if (chain == null || chain.length == 0) {
+ throw new IllegalArgumentException("No server certificates provided");
+ }
+ for (X509Certificate cert : chain) {
+ cert.checkValidity();
+ }
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String arg1) throws CertificateException {
+ if (chain == null || chain.length == 0) {
+ throw new CertificateException("No client certificates provided");
+ }
+ for (X509Certificate cert : chain) {
+ cert.checkValidity(); // 检查证书是否在有效期内
+ }
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[] {};
+ }
+
+ };
+ sc.init(null, new TrustManager[]{manager}, null);
+ return sc;
+ } catch (Exception err) {
+ logger.error("Init httpclient SSL error:{}",err.getMessage());
+ }
+ return null;
+ }
+
+
+ public static String readRequestBody(HttpServletRequest request) {
+ BufferedReader br = null;
+ StringBuilder sb = new StringBuilder("");
+ try {
+ br = request.getReader();
+ String str;
+ while ((str = br.readLine()) != null) {
+ sb.append(str);
+ }
+ br.close();
+ } catch (IOException err) {
+ logger.error("Read httpclient body error:{}",err.getMessage());
+ } finally {
+ if (null != br) {
+ try {
+ br.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ /** 与请求头 Content-Type 一致,避免 StringEntity 默认 text/plain 导致 OHIP 415。 */
+ private static ContentType resolveEntityContentType(Map headers) {
+ if (headers != null) {
+ for (Map.Entry entry : headers.entrySet()) {
+ if ("Content-Type".equalsIgnoreCase(entry.getKey())
+ && StringUtils.isNotBlank(entry.getValue())) {
+ try {
+ return ContentType.parse(entry.getValue().trim());
+ } catch (Exception ignored) {
+ // fall through
+ }
+ }
+ }
+ }
+ return ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8);
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/utils/JwtTokenUtil.java b/src/main/java/com/sjmresorts/opsapp/common/utils/JwtTokenUtil.java
new file mode 100644
index 0000000..b808a9f
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/utils/JwtTokenUtil.java
@@ -0,0 +1,268 @@
+package com.sjmresorts.opsapp.common.utils;
+
+import com.alibaba.fastjson2.JSON;
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
+import com.sjmresorts.opsapp.authentication.models.UserToken;
+import com.sjmresorts.opsapp.common.constants.AppConstants;
+import com.sjmresorts.opsapp.common.dtos.UserCredentialsDto;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.StringUtils;
+
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+public class JwtTokenUtil {
+
+ @Setter
+ @Getter
+ private static String secretKey;
+
+ @Setter
+ @Getter
+ private static String appLoginAuthorizationCode;
+
+ @Setter
+ @Getter
+ private static long accessTokenExpiration=5;
+
+ @Setter
+ @Getter
+ private static long appSessionExpiration=30;
+
+ @Setter
+ @Getter
+ private static long cmsSessionExpiration=30;
+
+ @Setter
+ @Getter
+ private static String thirdPartySecretKey;
+
+ @Setter
+ @Getter
+ private static long thirdPartyExpiration=30;
+
+
+ public static JWTVerifier getJwtVerifier(){
+ return JWT.require(Algorithm.HMAC256(secretKey)).build();
+ }
+
+ public static UserToken generateTokenByLoginUser(String tokenType, LoginUserInfo user) {
+ UserToken userToken = new UserToken();
+ long expiryVal = accessTokenExpiration;
+ String issueSource = user.getLoginSource();
+ if(issueSource==null){
+ issueSource="app_login";
+ }
+ if(AppConstants.CMS_REFRESH_TOKEN_TYPE.equalsIgnoreCase(tokenType)){
+ expiryVal = cmsSessionExpiration;
+ }else if(AppConstants.APP_REFRESH_TOKEN_TYPE.equalsIgnoreCase(tokenType)){
+ expiryVal = appSessionExpiration;
+ }else if(AppConstants.THIRD_PARTY_TOKEN_TYPE.equalsIgnoreCase(tokenType)){
+ expiryVal = thirdPartyExpiration;
+ }
+ expiryVal = expiryVal*60*1000;
+ Date now = new Date();
+ Date expDate = new Date(now.getTime() + expiryVal);
+ String uuid = UUID.randomUUID().toString();
+
+ String tokenValue = JWT.create()
+ .withIssuer(issueSource)
+ .withIssuedAt(now).withNotBefore(now).withExpiresAt(expDate).withJWTId(uuid)
+ .withClaim("employeeId", user.getEmployeeId())
+ .withClaim("group", user.getAdGroup()==null?"":user.getAdGroup())
+ .withClaim("userName", user.getUserName()==null?"":user.getUserName())
+ .withClaim("appVersion", user.getAppVersion()==null?"":user.getAppVersion())
+ .withSubject(user.getEmployeeId())
+ .sign(Algorithm.HMAC256(secretKey));
+
+ userToken.setTokenType(tokenType);
+ userToken.setTokenUniqueId(uuid);
+ userToken.setUserCode(user.getUserCode());
+ userToken.setTokenValue(tokenValue);
+ userToken.setCreatedAt(Instant.now());
+ userToken.setExpiryTime(expDate);
+
+ return userToken;
+ }
+
+ /**
+ * JWT加密用户名和密码
+ * 将用户名和密码加密为JWT token
+ *
+ * @param username 用户名
+ * @param password 密码
+ * @param expirationMinutes 过期时间(分钟),默认30分钟
+ * @return JWT加密后的token字符串
+ */
+ public static String encryptCredentials(String username, String password, long expirationMinutes) {
+ if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
+ return null;
+ }
+ try {
+ Date now = new Date();
+ long expirationMillis = expirationMinutes * 60 * 1000;
+ Date expDate = new Date(now.getTime() + expirationMillis);
+
+ String token = JWT.create()
+ .withIssuer("credentials_encryption")
+ .withIssuedAt(now)
+ .withNotBefore(now)
+ .withExpiresAt(expDate)
+ .withClaim("username", username)
+ .withClaim("password", password)
+ .withSubject("encrypted_credentials")
+ .sign(Algorithm.HMAC256(secretKey));
+
+ return token;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static LoginUserInfo parseUserFromRequest(HttpServletRequest request) {
+ LoginUserInfo loginUser = null;
+ String token = request.getHeader("Authorization");
+ if(StringUtils.isNotBlank(token) && token.startsWith("Bearer ")){
+ token = token.substring(7);
+ }
+ if(StringUtils.isNotBlank(token)) {
+ loginUser = parseUserFromToken(token);
+ }
+ return loginUser;
+ }
+
+ public static LoginUserInfo parseUserFromToken(String tokenContent) {
+ LoginUserInfo loginUser = null;
+ if(StringUtils.isNotBlank(tokenContent)) {
+ try {
+ DecodedJWT decodeData = JWT.decode(tokenContent);
+ loginUser = new LoginUserInfo();
+ loginUser.setEmployeeId(decodeData.getClaim("employeeId").asString());
+ loginUser.setAdGroup(decodeData.getClaim("group").asString());
+ loginUser.setUserName(decodeData.getClaim("userName").asString());
+ loginUser.setAppVersion(decodeData.getClaim("appVersion").asString());
+ loginUser.setLoginSource(decodeData.getIssuer());
+ loginUser.setTokenUuid(decodeData.getId());
+ loginUser.setUserCode(loginUser.getEmployeeId());
+ }catch(Exception err) {
+ //System.out.println("Parse Token failed:"+err.getMessage());
+ loginUser =null;
+ }
+ }
+ return loginUser;
+ }
+
+ /**
+ * JWT加密用户名和密码(使用默认过期时间30分钟)
+ *
+ * @param username 用户名
+ * @param password 密码
+ * @return JWT加密后的token字符串
+ */
+ public static String encryptCredentials(String username, String password) {
+ return encryptCredentials(username, password, 30);
+ }
+
+ /**
+ * JWT解密用户名和密码
+ * 从JWT token中解密出用户名和密码
+ *
+ * @param encryptedToken JWT加密的token字符串
+ * @return 解密后的用户凭证对象,如果解密失败返回null
+ * 注意:此方法不验证token的过期时间,只解码token内容
+ */
+ public static UserCredentialsDto decryptCredentials(String encryptedToken) {
+ if (StringUtils.isBlank(encryptedToken)) {
+ return null;
+ }
+ // 解码JWT token(不验证过期时间)
+ DecodedJWT decodedJWT = JWT.decode(encryptedToken);
+ // 提取用户名和密码
+ String username = decodedJWT.getClaim("username").asString();
+ String password = null;//decodedJWT.getClaim("password").asString();
+ String adGroup = decodedJWT.getClaim("ad-group").asString();
+ // 解析JSON数组,格式如:["APP ADMIN,CMS ADMIN"]
+ List adGroupList = JSON.parseArray(adGroup, String.class);
+ // 将逗号分隔的字符串拆分成独立的列表项
+ List adGroups = adGroupList.stream()
+ .flatMap(group -> Arrays.stream(group.split(",")))
+ .map(String::trim)
+ .filter(s -> !s.isEmpty())
+ .collect(Collectors.toList());
+
+ if (adGroups == null || adGroups.isEmpty()) {
+ return null;
+ }
+ return new UserCredentialsDto(username, password, adGroups);
+ }
+
+ /**
+ * 刷新token
+ * 从现有token中提取信息,生成新的token(包含ad-group信息)
+ *
+ * @param token 现有的token(可以是已过期或未过期的)
+ * @param expirationMinutes 新token的过期时间(分钟)
+ * @return 新的token字符串,如果刷新失败返回null
+ */
+ public static String refreshToken(String token, long expirationMinutes) {
+ if (StringUtils.isBlank(token)) {
+ return null;
+ }
+
+ try {
+ // 尝试解码token(不验证过期时间)
+ DecodedJWT decodedJWT = JWT.decode(token);
+
+ // 提取token中的信息
+ String username = decodedJWT.getClaim("username").asString();
+ String password = decodedJWT.getClaim("password").asString();
+ String adGroup = decodedJWT.getClaim("ad-group").asString();
+
+ if (StringUtils.isBlank(adGroup)) {
+ return null;
+ }
+
+ // 生成新的token
+ Date now = new Date();
+ long expirationMillis = expirationMinutes * 60 * 1000;
+ Date expDate = new Date(now.getTime() + expirationMillis);
+
+ String newToken = JWT.create()
+ .withIssuer("credentials_encryption")
+ .withIssuedAt(now)
+ .withNotBefore(now)
+ .withExpiresAt(expDate)
+ .withClaim("username", username)
+ .withClaim("password", password != null ? password : "")
+ .withClaim("ad-group", adGroup)
+ .withSubject("encrypted_credentials")
+ .sign(Algorithm.HMAC256(secretKey));
+
+ return newToken;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * 刷新token(使用默认过期时间30分钟)
+ *
+ * @param token 现有的token(可以是已过期或未过期的)
+ * @return 新的token字符串,如果刷新失败返回null
+ */
+ public static String refreshToken(String token) {
+ return refreshToken(token, 30);
+ }
+}
diff --git a/src/main/java/com/sjmresorts/opsapp/common/utils/JwtTokenUtilTest.java b/src/main/java/com/sjmresorts/opsapp/common/utils/JwtTokenUtilTest.java
new file mode 100644
index 0000000..01a0c39
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/utils/JwtTokenUtilTest.java
@@ -0,0 +1,103 @@
+package com.sjmresorts.opsapp.common.utils;
+
+import com.sjmresorts.opsapp.common.dtos.UserCredentialsDto;
+
+/**
+ * JWT加密解密测试类
+ * 用于测试用户名和密码的JWT加密和解密功能
+ */
+public class JwtTokenUtilTest {
+
+ public static void main(String[] args) {
+ System.out.println("========== JWT加密解密测试 ==========");
+
+ // 设置测试用的secretKey(与配置文件中的保持一致)
+ String testSecretKey = "SecretKeyWhichShouldBeLongEnoughToBeSecure67848563";
+ JwtTokenUtil.setSecretKey(testSecretKey);
+
+ // 测试数据
+ String testUsername = "testuser";
+ String testPassword = "testpassword123";
+
+ System.out.println("\n【测试1:基本加密解密】");
+ System.out.println("原始用户名: " + testUsername);
+ System.out.println("原始密码: " + testPassword);
+
+ // 测试加密(使用默认过期时间30分钟)
+ String encryptedToken = JwtTokenUtil.encryptCredentials(testUsername, testPassword);
+ if (encryptedToken != null) {
+ System.out.println("加密后的Token: " + encryptedToken);
+ System.out.println("Token长度: " + encryptedToken.length());
+
+ // 测试解密
+ UserCredentialsDto decryptedCredentials = JwtTokenUtil.decryptCredentials(encryptedToken);
+ if (decryptedCredentials != null) {
+ System.out.println("解密后的用户名: " + decryptedCredentials.getUsername());
+ System.out.println("解密后的密码: " + decryptedCredentials.getPassword());
+
+ // 验证结果
+ boolean usernameMatch = testUsername.equals(decryptedCredentials.getUsername());
+ boolean passwordMatch = testPassword.equals(decryptedCredentials.getPassword());
+
+ if (usernameMatch && passwordMatch) {
+ System.out.println("✓ 测试通过:加密和解密成功,数据一致!");
+ } else {
+ System.out.println("✗ 测试失败:解密后的数据与原始数据不一致!");
+ System.out.println(" 用户名匹配: " + usernameMatch);
+ System.out.println(" 密码匹配: " + passwordMatch);
+ }
+ } else {
+ System.out.println("✗ 解密失败!");
+ }
+ } else {
+ System.out.println("✗ 加密失败!");
+ }
+
+ System.out.println("\n【测试2:指定过期时间的加密】");
+ String testUsername2 = "admin";
+ String testPassword2 = "admin@123";
+ long expirationMinutes = 60; // 60分钟过期
+
+ String encryptedToken2 = JwtTokenUtil.encryptCredentials(testUsername2, testPassword2, expirationMinutes);
+ if (encryptedToken2 != null) {
+ System.out.println("加密后的Token: " + encryptedToken2);
+ UserCredentialsDto decryptedCredentials2 = JwtTokenUtil.decryptCredentials(encryptedToken2);
+ if (decryptedCredentials2 != null) {
+ System.out.println("解密后的用户名: " + decryptedCredentials2.getUsername());
+ System.out.println("解密后的密码: " + decryptedCredentials2.getPassword());
+ System.out.println("✓ 测试通过:指定过期时间的加密解密成功!");
+ } else {
+ System.out.println("✗ 解密失败!");
+ }
+ } else {
+ System.out.println("✗ 加密失败!");
+ }
+
+ System.out.println("\n【测试3:空值处理】");
+ String emptyToken = JwtTokenUtil.encryptCredentials("", "");
+ if (emptyToken == null) {
+ System.out.println("✓ 测试通过:空值加密返回null(符合预期)");
+ } else {
+ System.out.println("✗ 测试失败:空值加密应该返回null");
+ }
+
+ UserCredentialsDto emptyDecrypt = JwtTokenUtil.decryptCredentials("");
+ if (emptyDecrypt == null) {
+ System.out.println("✓ 测试通过:空值解密返回null(符合预期)");
+ } else {
+ System.out.println("✗ 测试失败:空值解密应该返回null");
+ }
+
+ System.out.println("\n【测试4:无效Token处理】");
+ String invalidToken = "invalid.jwt.token.here";
+ UserCredentialsDto invalidDecrypt = JwtTokenUtil.decryptCredentials(invalidToken);
+ if (invalidDecrypt == null) {
+ System.out.println("✓ 测试通过:无效Token解密返回null(符合预期)");
+ } else {
+ System.out.println("✗ 测试失败:无效Token解密应该返回null");
+ }
+
+ System.out.println("\n========== 测试完成 ==========");
+ }
+}
+
diff --git a/src/main/java/com/sjmresorts/opsapp/common/utils/LDAPUtil.java b/src/main/java/com/sjmresorts/opsapp/common/utils/LDAPUtil.java
new file mode 100644
index 0000000..3adcc5d
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/utils/LDAPUtil.java
@@ -0,0 +1,108 @@
+package com.sjmresorts.opsapp.common.utils;
+
+import java.util.List;
+
+import javax.naming.directory.SearchControls;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.ldap.core.AttributesMapper;
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.ldap.filter.AndFilter;
+import org.springframework.ldap.filter.EqualsFilter;
+import org.springframework.ldap.query.LdapQuery;
+import org.springframework.ldap.query.LdapQueryBuilder;
+import org.springframework.stereotype.Component;
+
+import com.sjmresorts.opsapp.common.dtos.LDAP.ADAccountInfo;
+
+
+@Component
+@Slf4j
+public class LDAPUtil {
+ private static final int ACCOUNT_DISABLED_FLAG = 0x0002;
+
+ @Autowired
+ private LdapTemplate ldapTemplate;
+
+ public boolean authenticate(String username, String password) {
+ log.info("===TEST:LDAP===Starting user authentication: {}", username);
+ AndFilter filter = new AndFilter()
+ .and(new EqualsFilter("sAMAccountName", username)); // AD域属性
+ LdapQuery query = LdapQueryBuilder.query()
+ .where("sAMAccountName").is(username);
+
+ try {
+ // 1. 查询账户状态属性
+ DirContextOperations ctx = ldapTemplate.searchForContext(query);
+
+ // 2. 检查账户禁用状态
+ int userAccountControl = Integer.parseInt(
+ ctx.getStringAttribute("userAccountControl"));
+ log.info("===TEST:LDAP===User: {} account status: {}", username, userAccountControl);
+ if (isAccountDisabled(userAccountControl)) {
+ log.info("===TEST:LDAP===User: {} account is disabled", username);
+ return false;
+ }
+
+ // 3. 执行标准认证
+ return ldapTemplate.authenticate("", filter.encode(), password);
+
+ } catch (Exception e) {
+ log.error("===TEST:LDAP===User: {} authentication failed: {} exception: ", username, e.getMessage(), e);
+ return false;
+ }
+ }
+
+ public ADAccountInfo getAccountInfo(String username) {
+ AndFilter filter = new AndFilter()
+ .and(new EqualsFilter("sAMAccountName", username));
+
+ List users = ldapTemplate.search(
+ "",
+ filter.encode(),
+ (AttributesMapper) attrs -> {
+ ADAccountInfo account = new ADAccountInfo();
+ if(attrs.get("cn") != null)
+ account.setDisplayName(attrs.get("cn").get().toString());
+ if(attrs.get("mail") != null)
+ account.setMail(attrs.get("mail").get().toString());
+ if(attrs.get("department") != null)
+ account.setDepartment(attrs.get("department").get().toString());
+ if(attrs.get("userAccountControl") != null)
+ account.setUserAccountControl(attrs.get("userAccountControl").get().toString());
+
+ account.setDirectGroups(getUserGroups(account.getDisplayName()));
+ return account;
+ });
+
+ return users.isEmpty() ? null : users.getFirst();
+ }
+
+ public List getUserGroups(String username) {
+ // 显式指定ContextMapper返回类型避免歧义
+ SearchControls controls = new SearchControls();
+ controls.setReturningAttributes(new String[]{"cn"});
+ controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+ Object userDn = ldapTemplate.searchForObject(
+ LdapQueryBuilder.query()
+ .where("sAMAccountName").is(username)
+ .and("objectclass").is("person"),
+ ctx -> ((DirContextOperations) ctx).getNameInNamespace());
+
+ // 精确匹配member属性(兼容AD的memberOf语法)
+ return ldapTemplate.search(
+ LdapQueryBuilder.query()
+ .base("") // 明确组查询基准
+ .where("member").is(userDn.toString()), // 显式toString()
+ (AttributesMapper) attrs -> attrs.get("cn").get().toString()
+ );
+ }
+
+
+ private boolean isAccountDisabled(int userAccountControl) {
+ return (userAccountControl & ACCOUNT_DISABLED_FLAG) != 0;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/sjmresorts/opsapp/common/utils/NetworkUtil.java b/src/main/java/com/sjmresorts/opsapp/common/utils/NetworkUtil.java
new file mode 100644
index 0000000..c7ff3f3
--- /dev/null
+++ b/src/main/java/com/sjmresorts/opsapp/common/utils/NetworkUtil.java
@@ -0,0 +1,276 @@
+package com.sjmresorts.opsapp.common.utils;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.regex.Pattern;
+
+
+public class NetworkUtil {
+ private static final Logger logger = LoggerFactory.getLogger(NetworkUtil.class);
+ public static List validProxyIps = new ArrayList();
+ public static List allowOriginSource = new ArrayList();
+ private static final Pattern IPV4_PATTERN =
+ Pattern.compile("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
+ private static final List INTERNAL_IP_PREFIXES = Arrays.asList(
+ "10.", "192.168.", "172.16.", "172.17.", "172.18.", "172.19.",
+ "172.20.", "172.21.", "172.22.", "172.23.", "172.24.", "172.25.",
+ "172.26.", "172.27.", "172.28.", "172.29.", "172.30.", "172.31."
+ );
+
+ public static String getClientIpByRequest(HttpServletRequest request) {
+ String ip = getIpFromHeaders(request);
+ if(ip==null){
+ ip = request.getRemoteAddr();
+ }
+ return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
+ }
+
+ private static String getIpFromHeaders(HttpServletRequest request) {
+ String[] headerNames = {
+ "X-Real-IP",
+ "X-Forwarded-For",
+ "Proxy-Client-IP",
+ "WL-Proxy-Client-IP",
+ "HTTP_CLIENT_IP",
+ "HTTP_X_FORWARDED_FOR"
+ };
+ boolean isContainForwardHeader=false;
+ for (String headerName : headerNames) {
+ String headerValue = request.getHeader(headerName);
+ if (headerValue != null && !headerValue.isEmpty()) {
+ isContainForwardHeader=true;
+ //logger.info("Check header[{}] of value[{}]",headerName,headerValue);
+ String ip = extractValidIp(headerValue);
+ if (ip != null) {
+ return ip;
+ }else{
+ logger.info("Header is not empty but not valid IP");
+ }
+ }
+ }
+ return (isContainForwardHeader?"INVALID_IP":null);
+ }
+
+ private static String extractValidIp(String headerValue) {
+ if (headerValue.contains(",")) {
+ String[] ips = headerValue.split(",");
+ for(int i=ips.length-1;i>=0;i--){
+ String ip = ips[i].trim();
+ if (isValidPublicIp(ip)) {
+ return ip;
+ }
+ }
+ return isValidIp(ips[ips.length - 1].trim())?ips[ips.length - 1].trim():null;
+ } else {
+ String ip = headerValue.trim();
+ return isValidIp(ip) ? ip : null;
+ }
+ }
+
+ private static boolean isValidPublicIp(String ip) {
+ return isValidIp(ip) && !isInternalIp(ip);
+ }
+
+ private static boolean isValidIp(String ip) {
+ return ip != null && !ip.isEmpty() &&
+ !"unknown".equalsIgnoreCase(ip) &&
+ IPV4_PATTERN.matcher(ip).matches();
+ }
+
+ private static boolean isInternalIp(String ip) {
+ if (ip == null) return false;
+ if (ip.equals("127.0.0.1") || ip.equals("0:0:0:0:0:0:0:1")) {
+ return true;
+ }
+ for (String prefix : INTERNAL_IP_PREFIXES) {
+ if (ip.startsWith(prefix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void checkAndSetAllowOrigin(HttpServletRequest request, HttpServletResponse response){
+ String origin = request.getHeader("Origin");
+ if(origin==null){
+ response.setHeader("Access-Control-Allow-Origin","");
+ }else{
+ if(isAllowOriginSource(origin)){
+ response.setHeader("Access-Control-Allow-Origin",origin);
+ }else{
+ logger.warn("Origin of [{}] is not allowed",origin);
+ }
+ }
+ }
+
+ public static void setSecurityHeaders(HttpServletRequest request, HttpServletResponse response){
+ response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
+ response.addHeader("Access-Control-Allow-Headers",
+ "Content-Type, Authorization, clientId, X-CMS-Operator, X-Client-Type, X-Requested-With, Accept, Origin");
+ response.addHeader("Access-Control-Allow-Credentials", "true");
+ response.addHeader("Access-Control-Max-Age", "3600");
+
+ if (request.isSecure()) {
+ response.setHeader("Strict-Transport-Security",
+ "max-age=86400; includeSubDomains; preload");
+ }
+ response.setHeader("X-Frame-Options", "SAMEORIGIN");
+ response.setHeader("X-Content-Type-Options", "nosniff");
+ response.setHeader("Content-Security-Policy",
+ "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
+ "style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; " +
+ "connect-src 'self'; media-src 'self'; object-src 'none'; frame-src 'self'");
+ response.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
+ response.setHeader("X-XSS-Protection", "1; mode=block");
+ }
+
+ public static List