feat:Init project

This commit is contained in:
蔡旻峰
2026-09-21 15:44:56 +08:00
parent 11eaa9b007
commit 6fa2365b87
328 changed files with 0 additions and 29369 deletions
@@ -1,94 +0,0 @@
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<AdminDevicesInfoPageResponse> getAppDevicesByPage(HttpServletRequest request,
@RequestBody(required = true) AdminDevicesInfoPageRequest pageRequest) {
DataTableResult<ClientInfosDto> 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<BaseApiResponse> 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<AdminGetClientInfosResponse> 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);
}
}
@@ -1,158 +0,0 @@
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<AdminLoginResponse> 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<String> verifyToken(
HttpServletRequest request,
HttpServletResponse response) {
return ResponseEntity.status(HttpStatus.OK).body("Token Verified");
}
@PostMapping(value="/logout",produces = "application/json;charset=UTF-8")
public ResponseEntity<AdminLogoutResponse> 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<RefreshTokenResponse> 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);
}
}
}
@@ -1,48 +0,0 @@
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<AdminMenusListResponse> 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<AdminMenuStatusResponse> 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));
}
}
@@ -1,101 +0,0 @@
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<AdminGetRoleUsersResponse> cmsGetRoleUserStatus(
HttpServletRequest request,
@RequestBody(required = true) AdminGetRoleUsersRequest pageRequest) {
AdminGetRoleUsersResponse Requestresult = new AdminGetRoleUsersResponse();
Map<String, List<User>> serviceResult = new HashMap<>();
List<User> assignUsers = adminUserService.listUser(pageRequest,"USER_ROLE");
List<User> 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<AdminAssignGroupPermissionResponse> 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);
}
}
}
@@ -1,203 +0,0 @@
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<AdminGetRolesResponse> cmsGetRoles(
HttpServletRequest request,
@RequestBody(required = true) AdminGetRolesRequest pageRequest) {
AdminGetRolesResponse Requestresult = new AdminGetRolesResponse();
DataTableResult<AdminRoleDto> 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<BaseApiResponse> 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<BaseApiResponse> 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<BaseApiResponse> 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<AdminGetRoleByIdResponse> 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<AdminRoleAssignUsersResponse> 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<AdminListPermissionSettingResponse> 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);
}
}
}
@@ -1,137 +0,0 @@
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<CmsUserInfoResponse> getAppUserByPage(
HttpServletRequest request,
@RequestBody(required = true) CmsAppUserPageRequest pageRequest) {
DataTableResult<CmsUserDto> 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<BaseApiResponse> 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<BaseApiResponse> 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<BaseApiResponse> 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<GetCmsUserResponse> 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);
}
}
@@ -1,17 +0,0 @@
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 {
}
@@ -1,18 +0,0 @@
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<ClientInfosDto> data;
}
@@ -1,19 +0,0 @@
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;
}
@@ -1,17 +0,0 @@
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;
}
@@ -1,26 +0,0 @@
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;
}
@@ -1,21 +0,0 @@
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<String> appSettingKeys;
}
@@ -1,20 +0,0 @@
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;
}
@@ -1,16 +0,0 @@
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;
}
@@ -1,18 +0,0 @@
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<UserRouterDto> userRouters = new ArrayList<>();
}
@@ -1,16 +0,0 @@
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<AdminMenuItemDto> data = new ArrayList<>();
}
@@ -1,22 +0,0 @@
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<RouterPermission> assignRouterOperation;
}
@@ -1,27 +0,0 @@
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;
}
@@ -1,17 +0,0 @@
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;
}
@@ -1,27 +0,0 @@
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;
}
@@ -1,21 +0,0 @@
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;
}
@@ -1,30 +0,0 @@
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<String, List<User>> data;
}
@@ -1,13 +0,0 @@
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;
}
@@ -1,17 +0,0 @@
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;
}
@@ -1,27 +0,0 @@
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;
}
@@ -1,19 +0,0 @@
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;
}
@@ -1,27 +0,0 @@
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;
}
@@ -1,17 +0,0 @@
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;
}
@@ -1,27 +0,0 @@
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;
}
@@ -1,19 +0,0 @@
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;
}
@@ -1,27 +0,0 @@
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<AdminRoleDto> data;
}
@@ -1,19 +0,0 @@
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;
}
@@ -1,27 +0,0 @@
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;
}
@@ -1,21 +0,0 @@
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<String> employeeIds;
}
@@ -1,27 +0,0 @@
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;
}
@@ -1,11 +0,0 @@
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;
}
@@ -1,25 +0,0 @@
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;
}
@@ -1,28 +0,0 @@
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;
}
@@ -1,25 +0,0 @@
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;
}
@@ -1,25 +0,0 @@
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<UserRouterDto> userRouters;
private Map<String, List<RouterPermissionDto>> userPermissions;
}
@@ -1,16 +0,0 @@
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;
}
@@ -1,24 +0,0 @@
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<UserRouterDto> sub;
}
@@ -1,14 +0,0 @@
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;
}
@@ -1,19 +0,0 @@
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;
}
@@ -1,17 +0,0 @@
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<CmsUserDto> data;
}
@@ -1,15 +0,0 @@
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;
}
@@ -1,17 +0,0 @@
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<ClientInfosDto> toClientInfosDtoList(List<ClientInfo> content);
}
@@ -1,41 +0,0 @@
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<UserRouterDto> toRouterDtoListWithChildren(List<SysRouter> sysRouters) {
List<UserRouterDto> routerDtos = new ArrayList<UserRouterDto>();
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<String, List<RouterPermissionDto>> toRouterPermissionMap(Map<String, List<RouterPermission>> userPermissions) {
Map<String, List<RouterPermissionDto>> permissionMap = new HashMap<String, List<RouterPermissionDto>>();
userPermissions.forEach((key, value) -> {
permissionMap.put(key, routerPermissionMapper.toRouterPermissionDtoList(value));
});
return permissionMap;
}
}
@@ -1,15 +0,0 @@
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<RouterPermissionDto> toRouterPermissionDtoList(List<RouterPermission> routerPermissions);
}
@@ -1,17 +0,0 @@
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<UserRouterDto> toUserRouterDtoList(List<SysRouter> sysRouters);
}
@@ -1,17 +0,0 @@
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<SysRouter> groupRouterSetting;
}
@@ -1,18 +0,0 @@
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<RoleUsers, Long> {
List<RoleUsers> findAllByRoleId(Long roleId);
@Query(value = "select roleUser.roleId from RoleUsers roleUser where employeeId=?1 ")
List<Long> findRoleIdListByEmployeeId(String employeeId);
}
@@ -1,23 +0,0 @@
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<Role, Long> {
Page<Role> findAll(Specification<Role> listSpec, Pageable pageable);
Role getRoleByRoleId(Long roleId);
@Query(value="select r.roleId from Role r where r.roleName = ?1")
List<Long> queryRoleIdByRoleName(String roleName);
}
@@ -1,108 +0,0 @@
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<ClientInfosDto> getDevicesInfoListByPage(AdminDevicesInfoPageRequest pageRequest) {
Specification<ClientInfo> 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<ClientInfo> resultPage = deviceInfoRepository.findAll(listSpec, pageable);
List<ClientInfosDto> resultList = clientInfosMapper.toClientInfosDtoList(resultPage.getContent());
DataTableResult<ClientInfosDto> dataResult = new DataTableResult<>();
dataResult.setTotalRecords(resultPage.getTotalElements());
dataResult.setPageSize(pageSize);
if (resultList == null) {
resultList = new ArrayList<>();
}
dataResult.setRowData(resultList);
return dataResult;
}
private Specification<ClientInfo> buildSpecificationByPageRequest(BasePageRequest pageRequest) {
return ((root, criteriaQuery, cb) -> {
List<Predicate> 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;
}
}
@@ -1,163 +0,0 @@
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<String, List<RouterPermission>> userPermissions = new HashMap<String, List<RouterPermission>>();
userPermissions = rolePermissionService.getFullRouterPermissions();
List<SysRouter> 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<String, List<RouterPermission>> userPermissions = new HashMap<String, List<RouterPermission>>();
List<Long> userRoleIds = getUserRoleIds(adResult.getDirectGroups());
userPermissions = rolePermissionService.getRouterPermissionsByGroupIds(userRoleIds);
List<SysRouter> 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<Long> getUserRoleIds(List<String> groupName) {
List<Long> roleIdList = new ArrayList<>();
if(groupName!=null&&!groupName.isEmpty()){
for(String roleName : groupName){
List<Long> roleIds = rolesRepository.queryRoleIdByRoleName(roleName);
roleIdList.addAll(roleIds);
}
}
return roleIdList;
}
}
@@ -1,113 +0,0 @@
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<String> 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<SysRouter> 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<UserRouterDto> buildUserRouters(LoginUserInfo loginUser) {
if (loginUser == null || StringUtils.isBlank(loginUser.getEmployeeId())) {
return List.of();
}
Map<String, List<RouterPermission>> userPermissions;
if (AppConstants.SUPER_ADMIN_ID.equalsIgnoreCase(loginUser.getEmployeeId())) {
userPermissions = rolePermissionService.getFullRouterPermissions();
} else {
List<Long> roleIds = roleUsersRepository.findRoleIdListByEmployeeId(loginUser.getEmployeeId());
userPermissions = rolePermissionService.getRouterPermissionsByGroupIds(
roleIds != null ? roleIds : List.of());
}
List<SysRouter> routers = sysRouterService.getRoutersByPermission("0", userPermissions);
return cmsLoginRouterMapper.toRouterDtoListWithChildren(routers);
}
}
@@ -1,109 +0,0 @@
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<SysRouter> listRouter = sysRouterRepository.getAllChildrenRoutes();
List<RouterPermission> listPermission = routerPermissionRepository.findAll();
List<RolePermission> listRolePermission = rolePermissionRepository.getAllPermissionByRoleId(request.getRoleId());
// 判断权限组是否有权限,并将selectAble属性加入返回数据
if (listRolePermission.size() > 0) {
for(int i=0; i<listPermission.size(); i++) {
RouterPermission rolePermission = listPermission.get(i);
for (RolePermission permission : listRolePermission) {
Long permissionId = permission.getPermissionId();
if(permissionId==rolePermission.getId()){
// 如果权限组有对应权限将selected设置为true
rolePermission.setSelected(true);
listRolePermission.remove(permission);
break;
}
}
}
}
for (int i=0;i<listRouter.size();i++) {
SysRouter sysRouter = listRouter.get(i);
for (int j=0;j<listPermission.size();j++) {
RouterPermission permission = listPermission.get(j);
// 将权限信息放入返回数据中
if(StringUtils.equals(sysRouter.getId(),permission.getRouterId())) {
sysRouter.getRoutePermissions().add(permission);
listPermission.remove(j);
j--;
}
}
}
newObject.setGroupId(request.getRoleId());
newObject.setGroupRouterSetting(listRouter);
result.setData(newObject);
result.setSuccess(true);
result.setCode("200");
result.setMessage("Get List Permission By Id Successfully");
return result;
}
public Boolean assginPermission(AdminAssignGroupPermissionRequest request) {
AdminAssignGroupPermissionResponse result = new AdminAssignGroupPermissionResponse();
List<RolePermission> permissionToDelete = rolePermissionRepository.getAllPermissionByRoleId(request.getRoleId());
if(request.getAssignRouterOperation()!=null &&request.getAssignRouterOperation().size()>0){
List<RouterPermission> assignRouterOperation = request.getAssignRouterOperation();
List<RolePermission>saveData = new ArrayList<>();
for(int i =0;i<assignRouterOperation.size();i++){
RouterPermission permission = assignRouterOperation.get(i);
boolean existItem = false;
for(int j=0;j<permissionToDelete.size();j++){
RolePermission rolePermission = permissionToDelete.get(j);
if(permission.getId()==rolePermission.getPermissionId()){
existItem = true;
permissionToDelete.remove(j);
break;
}
}
if(!existItem){
RolePermission rolePermission = new RolePermission();
rolePermission.setRoleId(request.getRoleId());
rolePermission.setPermissionId(permission.getId());
saveData.add(rolePermission);
}
}
if(!saveData.isEmpty()){
rolePermissionRepository.saveAll(saveData);
}
if(!permissionToDelete.isEmpty()){
rolePermissionRepository.deleteAll(permissionToDelete);
}
}
return true;
}
}
@@ -1,14 +0,0 @@
package com.sjmresorts.opsapp.admin.services;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class AdminRoleUsersService {
}
@@ -1,174 +0,0 @@
package com.sjmresorts.opsapp.admin.services;
import com.sjmresorts.opsapp.admin.dtos.roles.*;
import com.sjmresorts.opsapp.admin.repositories.RoleUsersRepository;
import com.sjmresorts.opsapp.admin.repositories.RolesRepository;
import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
import com.sjmresorts.opsapp.authentication.mappers.RoleMapper;
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 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.Predicate;
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 AdminRolesService {
private final RolesRepository rolesRepository;
private final AuditLogService auditLogService;
private final RoleUsersRepository roleUsersRepository;
private final RoleMapper roleMapper;
private final SysRouterRepository sysRouterRepository;
private final RouterPermissionRepository routerPermissionRepository;
private final RolePermissionRepository rolePermissionRepository;
public List<Role> getAllRoles() {
return rolesRepository.findAll();
}
public DataTableResult<AdminRoleDto> getRoleInfoListByPage(AdminGetRolesRequest pageRequest) {
Specification<Role> 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<Role> resultPage = rolesRepository.findAll(listSpec,pageable);
List<AdminRoleDto> resultList = roleMapper.toAdminRoleDto(resultPage.getContent());
DataTableResult<AdminRoleDto> 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<Role> buildSpecificationByPageRequest(AdminGetRolesRequest pageRequest) {
return ((root, criteriaQuery, cb) -> {
List<Predicate> 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<String> employeeIdList = request.getEmployeeIds();
List<RoleUsers> 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<RoleUsers> saveData = new ArrayList<>();
for(int i=0; i<employeeIdList.size();i++) {
String employeeId = employeeIdList.get(i);
boolean isExist = false;
for(int j=0;j<dataToDelete.size();j++) {
RoleUsers existItem = dataToDelete.get(j);
if(existItem.getEmployeeId().equals(employeeId)) {
isExist = true;
dataToDelete.remove(j);
break;
}
}
if(isExist==false) {
RoleUsers userItem = new RoleUsers();
userItem.setRoleId(roleId);
userItem.setEmployeeId(employeeId);
saveData.add(userItem);
}
}
if(saveData.size()>0) {
roleUsersRepository.saveAll(saveData);
}
}
if(dataToDelete.size()>0) {
roleUsersRepository.deleteAll(dataToDelete);
}
auditLogService.setLogNewValue(auditLog,request);
auditLogService.saveAuditLog(auditLog);
return true;
}
}
@@ -1,180 +0,0 @@
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<CmsUserDto> getUserInfoListByPage(CmsAppUserPageRequest pageRequest) {
Specification<User> 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<User> resultPage = userRepository.findAll(listSpec, pageable);
List<CmsUserDto> resultList = userMapper.toCmsUserDtoList(resultPage.getContent());
DataTableResult<CmsUserDto> dataResult = new DataTableResult<>();
dataResult.setTotalRecords(resultPage.getTotalElements());
dataResult.setPageSize(pageSize);
if (resultList == null) {
resultList = new ArrayList<>();
}
dataResult.setRowData(resultList);
return dataResult;
}
private Specification<User> buildSpecificationByPageRequest(CmsAppUserPageRequest pageRequest) {
return ((root, criteriaQuery, cb) -> {
List<Predicate> 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<User> listUser(AdminGetRoleUsersRequest request, String searchType) {
Specification<User> listSpec = buildSpecificationByRequest(request,searchType);
Sort sort = DaoUtil.buildSortByString(request.getSortOrder(), "employeeId");
List<User> resultList = userRepository.findAll(listSpec,sort);
return resultList;
}
private Specification<User> buildSpecificationByRequest(AdminGetRoleUsersRequest request, String searchType) {
Specification<User> SysUserpec = new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
List<Predicate> list = new ArrayList<>();
if (StringUtils.equals(searchType, "USER_ROLE") && request.getSearchRoleId() != null) {
Subquery<String> groupSubQuery = criteriaQuery.subquery(String.class);
Root<RoleUsers> subQueryRoot = groupSubQuery.from(RoleUsers.class);
groupSubQuery.select(subQueryRoot.get("employeeId"));
List<Predicate> 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<Long> groupSubQuery = criteriaQuery.subquery(Long.class);
Root<RoleUsers> subQueryRoot = groupSubQuery.from(RoleUsers.class);
groupSubQuery.select(cb.literal(1L));
List<Predicate> 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;
}
}
@@ -1,147 +0,0 @@
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<AppLogoutResponse> 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<RegisterTrackInfoResponse> 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<GetAuthTokenResponse> 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<RefreshTokenResponse> 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<BaseApiResponse> 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);
}
}
}
@@ -1,14 +0,0 @@
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<AdminRoleDto> toAdminRoleDto(List<Role> roles);
}
@@ -1,31 +0,0 @@
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<CmsUserDto> toCmsUserDtoList(List<User> 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);
}
@@ -1,174 +0,0 @@
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<String> 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<String> 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<String> getUserPermissionGroup(List<String> groupName) {
Set<String> permissionGroup = new HashSet<>();
if(groupName!=null&&!groupName.isEmpty()){
for(String roleName : groupName){
List<Long> roleIds = rolesRepository.queryRoleIdByRoleName(roleName);
List<String> appRouterIds = sysRouterRepository.queryIdsByHeading(AppConstants.APP_ROUTER_NAME);
if(!roleIds.isEmpty() && !appRouterIds.isEmpty()){
List<RouterPermission> permissions = routerPermissionRepository.queryPermissionByRoleAndRouterIds(roleIds,appRouterIds);
if(!permissions.isEmpty()){
Set<String> names = permissions.stream()
.map(RouterPermission::getPermissionName)
.collect(Collectors.toSet());
permissionGroup.addAll(names);
}
}
}
}
return permissionGroup;
}
}
@@ -55,7 +55,6 @@ public class OpsAppDbConfig {
@Primary @Primary
@Bean(name = "opsAppEntityManagerFactory") @Bean(name = "opsAppEntityManagerFactory")
@org.springframework.context.annotation.DependsOn({"esignCaptureSchemaMigrator", "esignCmsSchemaMigrator"})
public LocalContainerEntityManagerFactoryBean opsAppEntityManagerFactory( public LocalContainerEntityManagerFactoryBean opsAppEntityManagerFactory(
EntityManagerFactoryBuilder builder, EntityManagerFactoryBuilder builder,
DbDialect dbDialect) { DbDialect dbDialect) {
@@ -1,65 +0,0 @@
package com.sjmresorts.opsapp.esign.audit.controllers;
import com.sjmresorts.opsapp.esign.audit.dtos.EsignAuditLogResponse;
import com.sjmresorts.opsapp.esign.printer.dtos.EsignClientTransportLogRequest;
import com.sjmresorts.opsapp.esign.common.dtos.EsignSimpleResponse;
import com.sjmresorts.opsapp.esign.audit.services.EsignAuditLogService;
import com.sjmresorts.opsapp.esign.auth.services.EsignUserPresenceService;
import com.sjmresorts.opsapp.common.utils.NetworkUtil;
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 org.apache.commons.lang3.StringUtils;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "Esign Audit Logs")
@RestController
@RequestMapping({"/api/v1/esign", "/api/v1/sinosecu"})
public class EsignAuditLogController {
@Resource(name = "esignAuditLogService")
private EsignAuditLogService auditLogService;
@Resource
private EsignUserPresenceService userPresenceService;
@Operation(summary = "審計日誌", description = "免 JWT。category=OPERATION|THIRD_PARTY|CONNECTION")
@GetMapping(value = "/audit-logs", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignAuditLogResponse> listAuditLogs(
@RequestParam(name = "category", defaultValue = "OPERATION") String category,
@RequestParam(name = "eventType", required = false) String eventType,
@RequestParam(name = "page", defaultValue = "0") int page,
@RequestParam(name = "size", defaultValue = "50") int size) {
return ResponseEntity.ok(auditLogService.listLogs(category, page, size, eventType));
}
@Operation(summary = "App 上報連接/輪詢事件", description = "免 JWT。WebSocket 斷開、輪詢失敗等。")
@PostMapping(value = "/client-transport-log", consumes = "application/json;charset=UTF-8", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignSimpleResponse> clientTransportLog(
@RequestBody EsignClientTransportLogRequest request,
HttpServletRequest httpRequest) {
if (request == null || StringUtils.isBlank(request.getEventType())) {
return ResponseEntity.badRequest()
.body(EsignSimpleResponse.fail("400", "eventType required"));
}
String clientIp = EsignUserPresenceService.normalizeClientIp(NetworkUtil.getClientIpByRequest(httpRequest));
Long userId = request.getUserId();
String eventType = request.getEventType().trim().toUpperCase();
String message = StringUtils.defaultIfBlank(request.getMessage(), eventType);
auditLogService.appendConnection(
eventType,
userId,
message,
request.getTransport(),
clientIp,
null);
return ResponseEntity.ok(EsignSimpleResponse.ok("logged"));
}
}
@@ -1,177 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.controllers;
import com.sjmresorts.opsapp.esign.printer.dtos.LocalExeLaunchResult;
import com.sjmresorts.opsapp.esign.printer.services.LocalExeLaunchService;
import com.sjmresorts.opsapp.esign.reservation.support.OperaFlexLinkPageBuilder;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.net.URI;
import java.util.Map;
@Tag(name = "App Deep Link", description = "唤起桌面 App / exe(免 JWT)")
@RestController
@RequestMapping({"/api/v1/esign/app", "/api/v1/sinosecu/app"})
@CrossOrigin(origins = "*")
@RequiredArgsConstructor
public class AppDeepLinkController {
private final LocalExeLaunchService localExeLaunchService;
@Value("${ops-app.deeplink.url:myapp://open}")
private String deepLinkUrl;
@Value("${ops-app.deeplink.opera.close-popup:true}")
private boolean operaClosePopup;
@Value("${ops-app.deeplink.opera.parent-origin:}")
private String operaParentOrigin;
@Value("${ops-app.deeplink.opera.ext-app-id:0}")
private long operaExtAppId;
@Value("${ops-app.deeplink.opera.close-delay-ms:100}")
private int operaCloseDelayMs;
@Value("${ops-app.deeplink.opera.open-delay-ms:1200}")
private int operaOpenDelayMs;
@Value("${ops-app.deeplink.opera.debug-page:true}")
private boolean operaDebugPage;
@Value("${ops-app.deeplink.opera.popup-width:100%}")
private String operaPopupWidth;
@Value("${ops-app.deeplink.opera.popup-height:800px}")
private String operaPopupHeight;
@Value("${ops-app.deeplink.opera.launch-mode:new-window}")
private String operaLaunchMode;
@Value("${ops-app.deeplink.opera.close-modal-message:true}")
private boolean operaCloseModalMessage;
@Value("${ops-app.deeplink.opera.flex-link-origin:http://127.0.0.1:8066}")
private String operaFlexLinkOrigin;
@Operation(
summary = "HTML 页唤起 exe",
description = "免 JWT。Opera iframe 内只发 CLOSE_SHELL 信号;外层代理脚本负责销毁 Pop-up。独立标签走 myapp:// + window.close()。"
)
@ApiResponse(responseCode = "200", description = "HTML 页面")
@GetMapping(value = "/launch", produces = "text/html;charset=UTF-8")
public ResponseEntity<String> launchApp() {
return htmlPage();
}
@Operation(summary = "本机启动 exe(JSON,便于调试)")
@GetMapping("/launch/trigger")
public ResponseEntity<Map<String, Object>> launchTrigger() {
LocalExeLaunchResult result = localExeLaunchService.launchIfEnabled();
return ResponseEntity.ok(Map.of(
"launched", result.isLaunched(),
"skipped", result.isSkipped(),
"message", result.getMessage()));
}
@Operation(summary = "302 重定向到自定义协议")
@GetMapping("/redirect")
public ResponseEntity<Void> redirectApp() {
return ResponseEntity.status(HttpStatus.FOUND)
.location(URI.create(deepLinkUrl))
.header(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate")
.build();
}
@Operation(summary = "HTML 页(同 /launch)")
@GetMapping(value = "/open", produces = "text/html;charset=UTF-8")
public ResponseEntity<String> openAppPage() {
return htmlPage();
}
@Operation(
summary = "Opera 外层代理脚本(Tampermonkey / Console 注入)",
description = "安装在 Opera 主页面。内层 /launch iframe 发 CLOSE_SHELL,此外层全权销毁 Pop-up 外壳。"
)
@GetMapping(value = "/opera-parent-close-listener.js", produces = "application/javascript;charset=UTF-8")
public ResponseEntity<String> operaParentCloseListener() {
String script = OperaFlexLinkPageBuilder.buildOperaParentCloseListenerScript(operaFlexLinkOrigin);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/javascript;charset=UTF-8"))
.header(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate")
.body(script);
}
@Operation(summary = "Tampermonkey 脚本(自动在 Opera 域注入 closeModal 监听器)")
@GetMapping(value = "/opera-parent-close-listener.user.js", produces = "text/plain;charset=UTF-8")
public ResponseEntity<String> operaParentCloseListenerUserScript() {
String script = OperaFlexLinkPageBuilder.buildOperaParentCloseListenerUserScript(operaFlexLinkOrigin);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("text/plain;charset=UTF-8"))
.header(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate")
.body(script);
}
@Operation(summary = "外层代理安装说明页")
@GetMapping(value = "/opera-proxy-install", produces = "text/html;charset=UTF-8")
public ResponseEntity<String> operaProxyInstallPage() {
String html = OperaFlexLinkPageBuilder.buildOperaProxyInstallPage(operaFlexLinkOrigin);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("text/html;charset=UTF-8"))
.header(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate")
.body(html);
}
@Operation(summary = "JavaScript 片段")
@GetMapping(value = "/open.js", produces = "application/javascript;charset=UTF-8")
public ResponseEntity<String> openAppScript() {
String script = "window.location.href = \"" + escapeForJsString(deepLinkUrl) + "\";";
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/javascript;charset=UTF-8"))
.header(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate")
.body(script);
}
private ResponseEntity<String> htmlPage() {
LocalExeLaunchResult launchResult = localExeLaunchService.launchIfEnabled();
boolean useBrowserDeepLink = !launchResult.isLaunched();
int openDelay = launchResult.isLaunched() ? 200 : operaOpenDelayMs;
String html = OperaFlexLinkPageBuilder.buildLaunchPage(
deepLinkUrl,
operaLaunchMode,
useBrowserDeepLink,
launchResult.getMessage(),
operaClosePopup,
operaCloseModalMessage,
operaParentOrigin,
operaFlexLinkOrigin,
operaExtAppId,
openDelay,
operaCloseDelayMs,
operaDebugPage,
operaPopupWidth,
operaPopupHeight);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("text/html;charset=UTF-8"))
.header(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate")
.body(html);
}
private static String escapeForJsString(String value) {
return String.valueOf(value)
.replace("\\", "\\\\")
.replace("\"", "\\\"");
}
}
@@ -1,45 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.controllers;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignAppUserResponse;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignAppUserSaveRequest;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignAppUserStatusRequest;
import com.sjmresorts.opsapp.esign.auth.services.EsignAppUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "Esign App Users", description = "CMS 管理平板 App 登录账号(esign_user)")
@RestController
@RequestMapping({"/api/v1/esign", "/api/v1/sinosecu"})
@CrossOrigin(origins = "*")
@RequiredArgsConstructor
public class EsignAppUserController {
private final EsignAppUserService appUserService;
@Operation(summary = "CMS 平板账号列表", description = "免 JWT。")
@GetMapping(value = "/app-users", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignAppUserResponse> listAppUsers() {
return ResponseEntity.ok(appUserService.listAll());
}
@Operation(summary = "CMS 创建/更新平板账号", description = "免 JWT。密码明文存储,与 App 登录一致。")
@PostMapping(value = "/app-users/save", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignAppUserResponse> saveAppUser(@RequestBody EsignAppUserSaveRequest body) {
return ResponseEntity.ok(appUserService.save(body));
}
@Operation(summary = "CMS 更新平板账号状态", description = "免 JWT。ACTIVE / INACTIVE / LOCKED。")
@PostMapping(value = "/app-users/status", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignAppUserResponse> updateAppUserStatus(
@RequestBody EsignAppUserStatusRequest body) {
return ResponseEntity.ok(appUserService.updateStatus(body));
}
}
@@ -1,115 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.controllers;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignHotelListResponse;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignHotelLogoRequest;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignHotelMutationResponse;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignHotelUpsertRequest;
import com.sjmresorts.opsapp.esign.common.dtos.EsignSimpleResponse;
import com.sjmresorts.opsapp.esign.auth.services.EsignHotelService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "Esign Hotel", description = "旅宿 Hotel 主数据(Electron Hotel 页)")
@RestController
@RequestMapping({"/api/v1/esign/hotels", "/api/v1/sinosecu/hotels"})
@CrossOrigin(origins = "*")
public class EsignHotelController {
@Resource
private EsignHotelService esignHotelService;
@Operation(summary = "Hotel 列表与默认选中", description = "免 JWT。首次访问自动种子化默认酒店。")
@GetMapping(produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignHotelListResponse> listHotels(
@RequestParam(value = "includeLogos", defaultValue = "false") boolean includeLogos) {
return ResponseEntity.ok(esignHotelService.listHotels(includeLogos));
}
@Operation(summary = "新增酒店", description = "免 JWT。id 为 slug(小写字母、数字、连字符)。")
@PostMapping(consumes = "application/json;charset=UTF-8", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignHotelMutationResponse> createHotel(@RequestBody EsignHotelUpsertRequest request) {
EsignHotelMutationResponse body = esignHotelService.createHotel(request);
return mutationResponse(body);
}
@Operation(summary = "更新酒店文字信息", description = "免 JWT。")
@PutMapping(
value = "/{hotelId}",
consumes = "application/json;charset=UTF-8",
produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignHotelMutationResponse> updateHotel(
@PathVariable("hotelId") String hotelId, @RequestBody EsignHotelUpsertRequest request) {
EsignHotelMutationResponse body = esignHotelService.updateHotel(hotelId, request);
return mutationResponse(body);
}
@Operation(summary = "删除酒店(软删除)", description = "免 JWT。至少保留一家启用酒店。")
@DeleteMapping(value = "/{hotelId}", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignSimpleResponse> deleteHotel(@PathVariable("hotelId") String hotelId) {
EsignSimpleResponse body = esignHotelService.deleteHotel(hotelId);
if (!body.isSuccess() && "404".equals(body.getCode())) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body);
}
if (!body.isSuccess()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(body);
}
return ResponseEntity.ok(body);
}
@Operation(summary = "设置默认酒店", description = "免 JWT。")
@PutMapping(value = "/default/{hotelId}", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignSimpleResponse> setDefault(@PathVariable("hotelId") String hotelId) {
EsignSimpleResponse body = esignHotelService.setDefaultHotel(hotelId);
if (!body.isSuccess() && "404".equals(body.getCode())) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body);
}
if (!body.isSuccess()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(body);
}
return ResponseEntity.ok(body);
}
@Operation(summary = "上传或清除 Hotel Logo", description = "免 JWT。body.logoData 为 data:image/* 或空。")
@PutMapping(
value = "/{hotelId}/logo",
consumes = "application/json;charset=UTF-8",
produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignSimpleResponse> updateLogo(
@PathVariable("hotelId") String hotelId, @RequestBody(required = false) EsignHotelLogoRequest request) {
String logoData = request != null ? request.getLogoData() : null;
EsignSimpleResponse body = esignHotelService.updateLogo(hotelId, logoData);
if (!body.isSuccess() && "404".equals(body.getCode())) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body);
}
if (!body.isSuccess()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(body);
}
return ResponseEntity.ok(body);
}
private static ResponseEntity<EsignHotelMutationResponse> mutationResponse(EsignHotelMutationResponse body) {
if (!body.isSuccess() && "404".equals(body.getCode())) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body);
}
if (!body.isSuccess() && "409".equals(body.getCode())) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(body);
}
if (!body.isSuccess()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(body);
}
return ResponseEntity.ok(body);
}
}
@@ -1,92 +0,0 @@
// src/main/java/com/sjmresorts/opsapp/logging/controllers/EsignLoginController.java
package com.sjmresorts.opsapp.esign.auth.controllers;
import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignBindDeviceTokenRequest;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignLoginRequest;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignLoginResponse;
import com.sjmresorts.opsapp.esign.common.dtos.EsignSimpleResponse;
import com.sjmresorts.opsapp.esign.auth.services.EsignLoginService;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
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.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@Tag(name = "Esign Authentication", description = "APIs for Esign user authentication")
@RestController
@RequestMapping({"/api/v1/esign/auth", "/api/v1/sinosecu/auth"})
@CrossOrigin(origins = "*")
@RequiredArgsConstructor
public class EsignLoginController {
private final EsignLoginService loginService;
@Operation(
summary = "Esign User Login",
description = "API for Esign user login with username and password"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Login successful"),
@ApiResponse(responseCode = "401", description = "Invalid username or password"),
@ApiResponse(responseCode = "403", description = "User is inactive or locked")
})
@PostMapping(value = "/login", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignLoginResponse> login(
HttpServletRequest httpRequest,
@Parameter(description = "Login request info", required = true)
@Valid @RequestBody EsignLoginRequest request) {
EsignLoginResponse response = loginService.login(
request, com.sjmresorts.opsapp.common.utils.NetworkUtil.getClientIpByRequest(httpRequest));
if (response.isSuccess()) {
return ResponseEntity.status(HttpStatus.OK).body(response);
} else if ("401".equals(response.getCode())) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
} else if ("403".equals(response.getCode())) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(response);
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
}
@Operation(
summary = "Bind device token for push notification",
description = "Save FCM/APNs token for current Esign user. Requires Bearer accessToken from login."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Token bound"),
@ApiResponse(responseCode = "401", description = "Unauthorized")
})
@PostMapping(value = "/bind-device-token", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignSimpleResponse> bindDeviceToken(
HttpServletRequest request,
@Valid @RequestBody EsignBindDeviceTokenRequest body) {
LoginUserInfo loginUser = JwtTokenUtil.parseUserFromRequest(request);
Long userId = null;
if (loginUser != null && StringUtils.isNotBlank(loginUser.getEmployeeId())) {
try {
userId = Long.parseLong(loginUser.getEmployeeId());
} catch (NumberFormatException ignored) {
userId = null;
}
}
EsignSimpleResponse response = loginService.bindDeviceToken(userId, body);
HttpStatus status = response.isSuccess() ? HttpStatus.OK : HttpStatus.UNAUTHORIZED;
if ("404".equals(response.getCode())) {
status = HttpStatus.NOT_FOUND;
} else if ("403".equals(response.getCode())) {
status = HttpStatus.FORBIDDEN;
}
return ResponseEntity.status(status).body(response);
}
}
@@ -1,48 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.dtos;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class EsignAppUserResponse {
private boolean success = true;
private String code = "200";
private String message;
private EsignAppUserItem data;
private List<EsignAppUserItem> items;
@Data
@NoArgsConstructor
public static class EsignAppUserItem {
private Long id;
private String userName;
private String role;
private String hotelId;
private String status;
private String createdAt;
private String updatedAt;
private String lastLoginAt;
private String lastLoginIp;
private boolean hasDeviceToken;
}
public static EsignAppUserResponse ok(String message) {
EsignAppUserResponse r = new EsignAppUserResponse();
r.setMessage(message);
return r;
}
public static EsignAppUserResponse fail(String code, String message) {
EsignAppUserResponse r = new EsignAppUserResponse();
r.setSuccess(false);
r.setCode(code);
r.setMessage(message);
return r;
}
}
@@ -1,23 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.dtos;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class EsignAppUserSaveRequest {
private Long id;
/** 平板 App 登录账号 */
private String userName;
/** 明文密码(新建必填;编辑时留空表示不修改) */
private String password;
/** 角色,默认 STAFF */
private String role;
/** 绑定酒店(推送等按 hotel_id 过滤) */
private String hotelId;
/** ACTIVE / INACTIVE / LOCKED */
private String status;
}
@@ -1,13 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.dtos;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class EsignAppUserStatusRequest {
private Long id;
/** ACTIVE / INACTIVE / LOCKED */
private String status;
}
@@ -1,28 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.dtos;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@Schema(description = "Bind FCM/APNs device token for Esign user")
public class EsignBindDeviceTokenRequest {
@NotBlank(message = "deviceToken must not be blank")
@Schema(description = "FCM or APNs device token")
private String deviceToken;
@Schema(description = "Device unique id from client")
private String deviceId;
@Schema(description = "Device display name")
private String deviceName;
@Schema(description = "OS version string")
private String deviceVersion;
@Schema(description = "Platform: android | ios")
private String platform;
}
@@ -1,26 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.dtos;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@Schema(description = "旅宿 Hotel 配置项")
public class EsignHotelDto {
@Schema(description = "酒店 ID(slug)")
private String id;
private String area;
private String nameZh;
private String nameEn;
private String address;
private String website;
@Schema(description = "可选 HTTPS Logo 地址")
private String logoUrl;
@Schema(description = "是否已上传本机 Logo(仅存库,列表不返回像素数据)")
private boolean hasLogoData;
}
@@ -1,26 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.dtos;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
@Schema(description = "Hotel 列表与默认选中")
public class EsignHotelListResponse {
private boolean success = true;
private String code = "200";
private String message = "ok";
private EsignHotelListData data = new EsignHotelListData();
@Data
@NoArgsConstructor
public static class EsignHotelListData {
private List<EsignHotelDto> hotels = new ArrayList<>();
private String defaultHotelId;
}
}
@@ -1,14 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.dtos;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@Schema(description = "上传或清除 Hotel Logo(data URL)")
public class EsignHotelLogoRequest {
@Schema(description = "data:image/* Base64;传 null 或空字符串表示清除")
private String logoData;
}
@@ -1,31 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.dtos;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class EsignHotelMutationResponse {
private boolean success;
private String code;
private String message;
private EsignHotelDto data;
public static EsignHotelMutationResponse ok(String message, EsignHotelDto data) {
EsignHotelMutationResponse r = new EsignHotelMutationResponse();
r.setSuccess(true);
r.setCode("200");
r.setMessage(message != null ? message : "ok");
r.setData(data);
return r;
}
public static EsignHotelMutationResponse fail(String code, String message) {
EsignHotelMutationResponse r = new EsignHotelMutationResponse();
r.setSuccess(false);
r.setCode(code);
r.setMessage(message);
return r;
}
}
@@ -1,26 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.dtos;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@Schema(description = "创建/更新 Hotel 文字信息")
public class EsignHotelUpsertRequest {
@Schema(description = "酒店 ID(slug,仅创建时必填),如 venetian、grand-dragon")
private String id;
private String area;
@Schema(description = "中文名称", requiredMode = Schema.RequiredMode.REQUIRED)
private String nameZh;
private String nameEn;
private String address;
private String website;
@Schema(description = "可选 HTTPS Logo 地址(与 logo 上传二选一或并存,上传优先)")
private String logoUrl;
}
@@ -1,31 +0,0 @@
// src/main/java/com/sjmresorts/opsapp/logging/dtos/EsignLoginRequest.java
package com.sjmresorts.opsapp.esign.auth.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 = "Esign User Login Request")
public class EsignLoginRequest {
@NotBlank(message = "userName is required")
@Schema(description = "用户名", example = "collector001", required = true)
private String userName;
@NotBlank(message = "password is required")
@Schema(description = "密码", example = "password123", required = true)
private String password;
@Schema(description = "FCM/APNs device token from mobile client")
private String deviceToken;
@Schema(description = "Client device id")
private String deviceId;
}
@@ -1,61 +0,0 @@
// src/main/java/com/sjmresorts/opsapp/logging/dtos/EsignLoginResponse.java
package com.sjmresorts.opsapp.esign.auth.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 = "Esign User Login Response")
public class EsignLoginResponse {
@Schema(description = "登录是否成功")
private boolean success = false;
@Schema(description = "状态码", example = "200")
private String code;
@Schema(description = "结果描述", example = "Login successful")
private String message;
@Schema(description = "JWT访问令牌")
private String accessToken;
@Schema(description = "用户ID")
private String employeeId;
@Schema(description = "用户名")
private String userName;
@Schema(description = "酒店ID")
private String hotelId;
@Schema(description = "用户角色")
private String role;
public static EsignLoginResponse success(String accessToken, Long userId, String userName, String hotelId, String role) {
EsignLoginResponse response = new EsignLoginResponse();
response.setSuccess(true);
response.setCode("200");
response.setMessage("Login successful");
response.setAccessToken(accessToken);
response.setEmployeeId(String.valueOf(userId));
response.setUserName(userName);
response.setHotelId(hotelId);
response.setRole(role);
return response;
}
public static EsignLoginResponse fail(String code, String message) {
EsignLoginResponse response = new EsignLoginResponse();
response.setSuccess(false);
response.setCode(code);
response.setMessage(message);
return response;
}
}
@@ -1,58 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.models;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.Instant;
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "esign_hotels")
public class EsignHotel {
@Id
@Column(name = "hotel_id", nullable = false, length = 64)
private String hotelId;
@Column(name = "area", length = 128)
private String area;
@Column(name = "name_zh", nullable = false, length = 256)
private String nameZh;
@Column(name = "name_en", length = 512)
private String nameEn;
@Column(name = "address", length = 1000)
private String address;
@Column(name = "website", length = 256)
private String website;
@Column(name = "logo_url", length = 2000)
private String logoUrl;
@Lob
@Column(name = "logo_data")
private String logoData;
@Column(name = "sort_order", nullable = false)
private int sortOrder;
@Column(name = "enabled", nullable = false)
private boolean enabled = true;
@Column(name = "created_at", nullable = false)
private Instant createdAt;
@Column(name = "updated_at", nullable = false)
private Instant updatedAt;
}
@@ -1,31 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.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;
import java.time.Instant;
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "esign_hotel_settings")
public class EsignHotelSetting {
public static final String KEY_DEFAULT_HOTEL_ID = "default_hotel_id";
@Id
@Column(name = "setting_key", nullable = false, length = 64)
private String settingKey;
@Column(name = "setting_value", length = 256)
private String settingValue;
@Column(name = "updated_at", nullable = false)
private Instant updatedAt;
}
@@ -1,63 +0,0 @@
// src/main/java/com/sjmresorts/opsapp/logging/models/EsignUser.java
package com.sjmresorts.opsapp.esign.auth.models;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.Instant;
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "esign_user")
public class EsignUser {
@Id
@Column(name = "id", nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_name", nullable = false, length = 50)
private String userName;
@Column(name = "password", nullable = false, length = 255)
private String password;
@Column(name = "role", nullable = false, length = 20)
private String role;
@Column(name = "hotel_id", length = 64)
private String hotelId;
@Column(name = "status", nullable = false, length = 20)
private String status;
@Column(name = "device_token", length = 512)
private String deviceToken;
@Column(name = "created_at", nullable = false)
private Instant createdAt;
@Column(name = "updated_at", nullable = false)
private Instant updatedAt;
@Column(name = "last_login_at")
private Instant lastLoginAt;
@Column(name = "last_login_ip", length = 64)
private String lastLoginIp;
@PrePersist
protected void onCreate() {
createdAt = Instant.now();
updatedAt = Instant.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = Instant.now();
}
}
@@ -1,15 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.repositories;
import com.sjmresorts.opsapp.esign.auth.models.EsignHotel;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface EsignHotelRepository extends JpaRepository<EsignHotel, String> {
List<EsignHotel> findByEnabledTrueOrderBySortOrderAscHotelIdAsc();
long countByEnabledTrue();
java.util.Optional<EsignHotel> findFirstByEnabledTrueOrderBySortOrderDescHotelIdDesc();
}
@@ -1,6 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.repositories;
import com.sjmresorts.opsapp.esign.auth.models.EsignHotelSetting;
import org.springframework.data.jpa.repository.JpaRepository;
public interface EsignHotelSettingRepository extends JpaRepository<EsignHotelSetting, String> {}
@@ -1,56 +0,0 @@
// src/main/java/com/sjmresorts/opsapp/logging/repositories/EsignUserRepository.java
package com.sjmresorts.opsapp.esign.auth.repositories;
import com.sjmresorts.opsapp.esign.auth.models.EsignUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@Repository
public interface EsignUserRepository extends JpaRepository<EsignUser, Long> {
Optional<EsignUser> findByUserName(String userName);
Optional<EsignUser> findByUserNameAndPassword(String userName, String password);
List<EsignUser> findByIdIn(Collection<Long> ids);
Optional<EsignUser> findByDeviceToken(String deviceToken);
@Query("""
SELECT u FROM EsignUser u
WHERE UPPER(u.status) = UPPER(:status)
AND u.deviceToken IS NOT NULL
AND TRIM(u.deviceToken) <> ''
AND u.hotelId IS NOT NULL
AND TRIM(u.hotelId) <> ''
AND UPPER(TRIM(u.hotelId)) = UPPER(TRIM(:hotelId))
""")
List<EsignUser> findPushRecipientsByHotel(@Param("hotelId") String hotelId, @Param("status") String status);
@Query("""
SELECT u FROM EsignUser u
WHERE UPPER(u.status) = UPPER(:status)
AND u.deviceToken IS NOT NULL
AND TRIM(u.deviceToken) <> ''
""")
List<EsignUser> findActivePushRecipients(@Param("status") String status);
@Query("""
SELECT u FROM EsignUser u
WHERE UPPER(u.status) = UPPER(:status)
ORDER BY u.lastLoginAt DESC
""")
List<EsignUser> findByStatusOrderByLastLoginAtDesc(@Param("status") String status);
List<EsignUser> findAllByOrderByUserNameAsc();
boolean existsByUserNameIgnoreCase(String userName);
boolean existsByUserNameIgnoreCaseAndIdNot(String userName, Long id);
}
@@ -1,179 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.services;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignAppUserResponse;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignAppUserSaveRequest;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignAppUserStatusRequest;
import com.sjmresorts.opsapp.esign.auth.models.EsignUser;
import com.sjmresorts.opsapp.esign.auth.repositories.EsignHotelRepository;
import com.sjmresorts.opsapp.esign.auth.repositories.EsignUserRepository;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
@Slf4j
@Service
public class EsignAppUserService {
private static final String DEFAULT_ROLE = "STAFF";
private static final String DEFAULT_STATUS = "ACTIVE";
private static final Set<String> ALLOWED_STATUSES = Set.of("ACTIVE", "INACTIVE", "LOCKED");
private static final Set<String> ALLOWED_ROLES = Set.of("STAFF", "ADMIN");
@Resource
private EsignUserRepository esignUserRepository;
@Resource
private EsignHotelRepository hotelRepository;
@Transactional(readOnly = true)
public EsignAppUserResponse listAll() {
EsignAppUserResponse resp = EsignAppUserResponse.ok("ok");
resp.setItems(toItems(esignUserRepository.findAllByOrderByUserNameAsc()));
return resp;
}
@Transactional
public EsignAppUserResponse save(EsignAppUserSaveRequest request) {
if (request == null) {
return EsignAppUserResponse.fail("400", "请求体不能为空");
}
String userName = trim(request.getUserName(), 50);
if (StringUtils.isBlank(userName)) {
return EsignAppUserResponse.fail("400", "请填写登录账号");
}
String role = normalizeRole(request.getRole());
String status = normalizeStatus(request.getStatus());
String hotelId = trim(request.getHotelId(), 64);
if (StringUtils.isNotBlank(hotelId) && hotelRepository.findById(hotelId).isEmpty()) {
return EsignAppUserResponse.fail("400", "酒店不存在:" + hotelId);
}
boolean creating = request.getId() == null;
EsignUser row;
if (creating) {
if (esignUserRepository.existsByUserNameIgnoreCase(userName)) {
return EsignAppUserResponse.fail("409", "该登录账号已存在");
}
String password = StringUtils.trimToEmpty(request.getPassword());
if (password.length() < 4) {
return EsignAppUserResponse.fail("400", "密码至少 4 位");
}
row = new EsignUser();
row.setUserName(userName);
row.setPassword(password);
row.setRole(role);
row.setStatus(status);
row.setHotelId(StringUtils.isBlank(hotelId) ? null : hotelId);
} else {
Optional<EsignUser> opt = esignUserRepository.findById(request.getId());
if (opt.isEmpty()) {
return EsignAppUserResponse.fail("404", "账号不存在");
}
row = opt.get();
if (!userName.equalsIgnoreCase(row.getUserName())) {
return EsignAppUserResponse.fail("400", "登录账号创建后不可修改");
}
row.setRole(role);
row.setStatus(status);
row.setHotelId(StringUtils.isBlank(hotelId) ? null : hotelId);
String password = StringUtils.trimToEmpty(request.getPassword());
if (StringUtils.isNotBlank(password)) {
if (password.length() < 4) {
return EsignAppUserResponse.fail("400", "密码至少 4 位");
}
row.setPassword(password);
}
}
row = esignUserRepository.save(row);
log.info("Esign app user {} id={} status={}", creating ? "created" : "updated", row.getId(), row.getStatus());
EsignAppUserResponse resp = EsignAppUserResponse.ok(creating ? "已创建平板账号" : "已更新平板账号");
resp.setData(toItem(row));
return resp;
}
@Transactional
public EsignAppUserResponse updateStatus(EsignAppUserStatusRequest request) {
if (request == null || request.getId() == null) {
return EsignAppUserResponse.fail("400", "请指定账号 ID");
}
String status = normalizeStatus(request.getStatus());
Optional<EsignUser> opt = esignUserRepository.findById(request.getId());
if (opt.isEmpty()) {
return EsignAppUserResponse.fail("404", "账号不存在");
}
EsignUser row = opt.get();
row.setStatus(status);
row = esignUserRepository.save(row);
EsignAppUserResponse resp = EsignAppUserResponse.ok("状态已更新");
resp.setData(toItem(row));
return resp;
}
private List<EsignAppUserResponse.EsignAppUserItem> toItems(List<EsignUser> rows) {
List<EsignAppUserResponse.EsignAppUserItem> items = new ArrayList<>();
if (rows == null) {
return items;
}
for (EsignUser row : rows) {
items.add(toItem(row));
}
return items;
}
private EsignAppUserResponse.EsignAppUserItem toItem(EsignUser row) {
EsignAppUserResponse.EsignAppUserItem item = new EsignAppUserResponse.EsignAppUserItem();
item.setId(row.getId());
item.setUserName(row.getUserName());
item.setRole(row.getRole());
item.setHotelId(row.getHotelId());
item.setStatus(row.getStatus());
item.setCreatedAt(row.getCreatedAt() != null ? row.getCreatedAt().toString() : null);
item.setUpdatedAt(row.getUpdatedAt() != null ? row.getUpdatedAt().toString() : null);
item.setLastLoginAt(row.getLastLoginAt() != null ? row.getLastLoginAt().toString() : null);
item.setLastLoginIp(row.getLastLoginIp());
item.setHasDeviceToken(StringUtils.isNotBlank(row.getDeviceToken()));
return item;
}
private static String normalizeRole(String raw) {
String role = StringUtils.upperCase(StringUtils.trimToEmpty(raw), Locale.ROOT);
if (StringUtils.isBlank(role)) {
return DEFAULT_ROLE;
}
if (!ALLOWED_ROLES.contains(role)) {
return DEFAULT_ROLE;
}
return role;
}
private static String normalizeStatus(String raw) {
String status = StringUtils.upperCase(StringUtils.trimToEmpty(raw), Locale.ROOT);
if (StringUtils.isBlank(status)) {
return DEFAULT_STATUS;
}
if (!ALLOWED_STATUSES.contains(status)) {
return DEFAULT_STATUS;
}
return status;
}
private static String trim(String raw, int maxLen) {
if (raw == null) {
return "";
}
String s = raw.trim();
if (s.length() > maxLen) {
return s.substring(0, maxLen);
}
return s;
}
}
@@ -1,419 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.services;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignHotelDto;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignHotelListResponse;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignHotelMutationResponse;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignHotelUpsertRequest;
import com.sjmresorts.opsapp.esign.common.dtos.EsignSimpleResponse;
import com.sjmresorts.opsapp.esign.auth.models.EsignHotel;
import com.sjmresorts.opsapp.esign.auth.models.EsignHotelSetting;
import com.sjmresorts.opsapp.esign.auth.repositories.EsignHotelRepository;
import com.sjmresorts.opsapp.esign.auth.repositories.EsignHotelSettingRepository;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.regex.Pattern;
@Service
public class EsignHotelService {
public static final int LOGO_DATA_MAX_LEN = 700_000;
private static final Pattern HOTEL_ID_PATTERN = Pattern.compile("^[a-z0-9]+(?:-[a-z0-9]+)*$");
private final EsignHotelRepository hotelRepository;
private final EsignHotelSettingRepository settingRepository;
public EsignHotelService(
EsignHotelRepository hotelRepository,
EsignHotelSettingRepository settingRepository) {
this.hotelRepository = hotelRepository;
this.settingRepository = settingRepository;
}
private void ensureSeeded() {
if (hotelRepository.count() > 0) {
return;
}
Instant now = Instant.now();
int order = 0;
for (SeedHotel s : defaultSeedHotels()) {
EsignHotel h = new EsignHotel();
h.setHotelId(s.id);
h.setArea(s.area);
h.setNameZh(s.nameZh);
h.setNameEn(s.nameEn);
h.setAddress(s.address);
h.setWebsite(s.website);
h.setSortOrder(order++);
h.setEnabled(true);
h.setCreatedAt(now);
h.setUpdatedAt(now);
hotelRepository.save(h);
}
EsignHotelSetting setting = new EsignHotelSetting();
setting.setSettingKey(EsignHotelSetting.KEY_DEFAULT_HOTEL_ID);
setting.setSettingValue("venetian");
setting.setUpdatedAt(now);
settingRepository.save(setting);
}
@Transactional
public EsignHotelListResponse listHotels(boolean includeLogos) {
ensureSeeded();
EsignHotelListResponse resp = new EsignHotelListResponse();
EsignHotelListResponse.EsignHotelListData data = resp.getData();
List<EsignHotelDto> dtos = new ArrayList<>();
for (EsignHotel row : hotelRepository.findByEnabledTrueOrderBySortOrderAscHotelIdAsc()) {
dtos.add(toDto(row, includeLogos));
}
data.setHotels(dtos);
data.setDefaultHotelId(resolveDefaultHotelId(dtos));
return resp;
}
/** 详情/採集:含 logo_data 解析后的 logoUrl */
@Transactional(readOnly = true)
public EsignHotelDto getHotelForPayload(String hotelId) {
if (StringUtils.isBlank(hotelId)) {
return null;
}
return hotelRepository.findById(hotelId.trim()).filter(EsignHotel::isEnabled).map(this::toDtoWithLogo).orElse(null);
}
@Transactional(readOnly = true)
public EsignHotelDto getDefaultHotelForPayload() {
EsignHotelListResponse list = listHotels(true);
String id = list.getData().getDefaultHotelId();
if (StringUtils.isBlank(id)) {
return null;
}
return getHotelForPayload(id);
}
@Transactional
public EsignSimpleResponse setDefaultHotel(String hotelId) {
if (StringUtils.isBlank(hotelId)) {
return EsignSimpleResponse.fail("400", "hotelId required");
}
String id = hotelId.trim();
if (!hotelRepository.existsById(id)) {
return EsignSimpleResponse.fail("404", "Hotel not found");
}
EsignHotel row = hotelRepository.findById(id).orElse(null);
if (row == null || !row.isEnabled()) {
return EsignSimpleResponse.fail("404", "Hotel not found");
}
Instant now = Instant.now();
EsignHotelSetting setting =
settingRepository
.findById(EsignHotelSetting.KEY_DEFAULT_HOTEL_ID)
.orElseGet(
() -> {
EsignHotelSetting s = new EsignHotelSetting();
s.setSettingKey(EsignHotelSetting.KEY_DEFAULT_HOTEL_ID);
return s;
});
setting.setSettingValue(id);
setting.setUpdatedAt(now);
settingRepository.save(setting);
return EsignSimpleResponse.ok("default hotel updated");
}
@Transactional
public EsignHotelMutationResponse createHotel(EsignHotelUpsertRequest request) {
if (request == null) {
return EsignHotelMutationResponse.fail("400", "request body required");
}
String id = normalizeHotelId(request.getId());
if (StringUtils.isBlank(id)) {
return EsignHotelMutationResponse.fail("400", "id required (slug: a-z, 0-9, hyphen)");
}
if (!HOTEL_ID_PATTERN.matcher(id).matches()) {
return EsignHotelMutationResponse.fail("400", "invalid id format");
}
String nameZh = trimToNull(request.getNameZh());
if (StringUtils.isBlank(nameZh)) {
return EsignHotelMutationResponse.fail("400", "nameZh required");
}
Instant now = Instant.now();
Optional<EsignHotel> existing = hotelRepository.findById(id);
EsignHotel h;
if (existing.isPresent()) {
h = existing.get();
if (h.isEnabled()) {
return EsignHotelMutationResponse.fail("409", "hotel id already exists");
}
applyTextFields(h, request, nameZh);
h.setEnabled(true);
h.setUpdatedAt(now);
} else {
h = new EsignHotel();
h.setHotelId(id);
applyTextFields(h, request, nameZh);
h.setSortOrder(nextSortOrder());
h.setEnabled(true);
h.setCreatedAt(now);
h.setUpdatedAt(now);
}
hotelRepository.save(h);
return EsignHotelMutationResponse.ok("hotel created", toDto(h, false));
}
@Transactional
public EsignHotelMutationResponse updateHotel(String hotelId, EsignHotelUpsertRequest request) {
if (StringUtils.isBlank(hotelId)) {
return EsignHotelMutationResponse.fail("400", "hotelId required");
}
if (request == null) {
return EsignHotelMutationResponse.fail("400", "request body required");
}
String id = hotelId.trim();
Optional<EsignHotel> opt = hotelRepository.findById(id);
if (opt.isEmpty() || !opt.get().isEnabled()) {
return EsignHotelMutationResponse.fail("404", "Hotel not found");
}
String nameZh = trimToNull(request.getNameZh());
if (StringUtils.isBlank(nameZh)) {
return EsignHotelMutationResponse.fail("400", "nameZh required");
}
EsignHotel h = opt.get();
applyTextFields(h, request, nameZh);
h.setUpdatedAt(Instant.now());
hotelRepository.save(h);
return EsignHotelMutationResponse.ok("hotel updated", toDto(h, false));
}
@Transactional
public EsignSimpleResponse deleteHotel(String hotelId) {
if (StringUtils.isBlank(hotelId)) {
return EsignSimpleResponse.fail("400", "hotelId required");
}
String id = hotelId.trim();
Optional<EsignHotel> opt = hotelRepository.findById(id);
if (opt.isEmpty() || !opt.get().isEnabled()) {
return EsignSimpleResponse.fail("404", "Hotel not found");
}
if (hotelRepository.countByEnabledTrue() <= 1) {
return EsignSimpleResponse.fail("400", "cannot delete the last hotel");
}
EsignHotel h = opt.get();
h.setEnabled(false);
h.setUpdatedAt(Instant.now());
hotelRepository.save(h);
String defaultId =
settingRepository
.findById(EsignHotelSetting.KEY_DEFAULT_HOTEL_ID)
.map(EsignHotelSetting::getSettingValue)
.orElse(null);
if (id.equals(defaultId)) {
List<EsignHotel> remaining = hotelRepository.findByEnabledTrueOrderBySortOrderAscHotelIdAsc();
if (!remaining.isEmpty()) {
EsignHotelSetting setting =
settingRepository
.findById(EsignHotelSetting.KEY_DEFAULT_HOTEL_ID)
.orElseGet(
() -> {
EsignHotelSetting s = new EsignHotelSetting();
s.setSettingKey(EsignHotelSetting.KEY_DEFAULT_HOTEL_ID);
return s;
});
setting.setSettingValue(remaining.get(0).getHotelId());
setting.setUpdatedAt(Instant.now());
settingRepository.save(setting);
}
}
return EsignSimpleResponse.ok("hotel deleted");
}
@Transactional
public EsignSimpleResponse updateLogo(String hotelId, String logoData) {
if (StringUtils.isBlank(hotelId)) {
return EsignSimpleResponse.fail("400", "hotelId required");
}
Optional<EsignHotel> opt = hotelRepository.findById(hotelId.trim());
if (opt.isEmpty() || !opt.get().isEnabled()) {
return EsignSimpleResponse.fail("404", "Hotel not found");
}
EsignHotel h = opt.get();
if (logoData == null || StringUtils.isBlank(logoData)) {
h.setLogoData(null);
} else {
String s = logoData.trim();
if (!s.regionMatches(true, 0, "data:image/", 0, 11)) {
return EsignSimpleResponse.fail("400", "logoData must be data:image/*");
}
if (s.length() > LOGO_DATA_MAX_LEN) {
return EsignSimpleResponse.fail("400", "logoData too large");
}
h.setLogoData(s);
}
h.setUpdatedAt(Instant.now());
hotelRepository.save(h);
return EsignSimpleResponse.ok("logo updated");
}
private void applyTextFields(EsignHotel h, EsignHotelUpsertRequest request, String nameZh) {
h.setNameZh(nameZh);
h.setArea(trimToNull(request.getArea()));
h.setNameEn(trimToNull(request.getNameEn()));
h.setAddress(trimToNull(request.getAddress()));
h.setWebsite(trimToNull(request.getWebsite()));
String logoUrl = trimToNull(request.getLogoUrl());
if (logoUrl != null && logoUrl.length() > 2000) {
logoUrl = logoUrl.substring(0, 2000);
}
h.setLogoUrl(logoUrl);
}
private int nextSortOrder() {
return hotelRepository
.findFirstByEnabledTrueOrderBySortOrderDescHotelIdDesc()
.map(r -> r.getSortOrder() + 1)
.orElse(0);
}
private static String normalizeHotelId(String raw) {
if (raw == null) {
return null;
}
return raw.trim().toLowerCase(Locale.ROOT);
}
private static String trimToNull(String s) {
if (s == null) {
return null;
}
String t = s.trim();
return t.isEmpty() ? null : t;
}
private String resolveDefaultHotelId(List<EsignHotelDto> hotels) {
String configured =
settingRepository
.findById(EsignHotelSetting.KEY_DEFAULT_HOTEL_ID)
.map(EsignHotelSetting::getSettingValue)
.filter(StringUtils::isNotBlank)
.orElse(null);
if (configured != null && hotels.stream().anyMatch(h -> configured.equals(h.getId()))) {
return configured;
}
return hotels.isEmpty() ? null : hotels.get(0).getId();
}
private EsignHotelDto toDto(EsignHotel row, boolean withResolvedLogoUrl) {
EsignHotelDto dto = new EsignHotelDto();
dto.setId(row.getHotelId());
dto.setArea(row.getArea());
dto.setNameZh(row.getNameZh());
dto.setNameEn(row.getNameEn());
dto.setAddress(row.getAddress());
dto.setWebsite(row.getWebsite());
dto.setHasLogoData(StringUtils.isNotBlank(row.getLogoData()));
if (withResolvedLogoUrl) {
if (StringUtils.isNotBlank(row.getLogoData())) {
dto.setLogoUrl(row.getLogoData().trim());
} else if (StringUtils.isNotBlank(row.getLogoUrl())) {
dto.setLogoUrl(row.getLogoUrl().trim());
} else {
dto.setLogoUrl(faviconFromWebsite(row.getWebsite()));
}
} else {
dto.setLogoUrl(trimToNull(row.getLogoUrl()));
}
return dto;
}
private EsignHotelDto toDtoWithLogo(EsignHotel row) {
return toDto(row, true);
}
private static String faviconFromWebsite(String website) {
if (StringUtils.isBlank(website)) {
return null;
}
String w =
website.trim()
.toLowerCase()
.replaceFirst("^https?://", "")
.replaceFirst("^www\\.", "");
String host = w.split("/")[0].replaceAll(":\\d+$", "");
if (host.isEmpty()) {
return null;
}
return "https://www.google.com/s2/favicons?domain="
+ java.net.URLEncoder.encode(host, java.nio.charset.StandardCharsets.UTF_8)
+ "&sz=96";
}
private static List<SeedHotel> defaultSeedHotels() {
List<SeedHotel> list = new ArrayList<>();
list.add(
new SeedHotel(
"venetian",
"氹仔 Taipa",
"澳門威尼斯人酒店",
"The Venetian Macao (五星 5-Star)",
"澳門望德聖母灣大馬路,路氹金光大道 Estrada da Baía de N. Senhora da Esperança, s/n, Taipa, Macau",
"www.venetianmacao.com"));
list.add(
new SeedHotel(
"grand-dragon",
"氹仔 Taipa",
"駿龍酒店",
"Grand Dragon Hotel (四星 4-Star)",
"澳門氹仔沙維斯街 36 號 Rua de Chaves, n.º 36, Taipa, Macau",
"www.granddragon.com.mo"));
list.add(
new SeedHotel(
"grandview",
"氹仔 Taipa",
"君怡酒店",
"Grandview Hotel (四星 4-Star)",
"氹仔柯維納馬路 142 號和佛山街 16 號 Estrada Governador Albano de Oliveira, n.º 142 e Rua de Fat San, n.º 16, Taipa",
"www.grandview-hotel.com"));
list.add(
new SeedHotel(
"parisian",
"氹仔 Taipa",
"澳門巴黎人酒店",
"The Parisian Macao (三星 3-Star)",
"澳門路氹金光大道連貫公路 Estrada do Istmo, Lote 3, Cotai Strip, Macau",
"www.parisianmacao.com"));
list.add(
new SeedHotel(
"royal",
"澳門 Macao",
"皇都酒店",
"Royal Hotel (五星 5-Star)",
"澳門得勝馬路 2 號-4 號 Estrada da Vitória, nºs. 2-4, Macau",
"www.hotelroyal.com.mo"));
list.add(
new SeedHotel(
"landmark",
"澳門 Macao",
"新東方置地酒店",
"New Orient Landmark Hotel (五星 5-Star)",
"澳門新口岸友誼大馬路 555 號 555 Avenida da Amizade, Macau",
"www.landmarkhotel.com.mo"));
list.add(
new SeedHotel(
"rio",
"澳門 Macao",
"利澳酒店",
"Rio Macau (四星 4-Star)",
"澳門高美士街 33 號及宋玉生廣場 682 號 Rua de Luís Gonzaga Gomes, n.º 33 e Alameda Dr. Carlos D'Assumpção, n.º 682, Macau",
"www.riomacau.com"));
return list;
}
private record SeedHotel(
String id, String area, String nameZh, String nameEn, String address, String website) {}
}
@@ -1,118 +0,0 @@
// src/main/java/com/sjmresorts/opsapp/logging/services/EsignLoginService.java
package com.sjmresorts.opsapp.esign.auth.services;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignBindDeviceTokenRequest;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignLoginRequest;
import com.sjmresorts.opsapp.esign.auth.dtos.EsignLoginResponse;
import com.sjmresorts.opsapp.esign.common.dtos.EsignSimpleResponse;
import com.sjmresorts.opsapp.esign.auth.models.EsignUser;
import com.sjmresorts.opsapp.esign.auth.repositories.EsignUserRepository;
import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.time.Instant;
import java.util.Date;
import java.util.UUID;
@Slf4j
@Service
@RequiredArgsConstructor
public class EsignLoginService {
private final EsignUserRepository userRepository;
@Transactional
public EsignLoginResponse login(EsignLoginRequest request, String clientIp) {
// 根据用户名和密码查找用户(明文验证)
EsignUser user = userRepository.findByUserNameAndPassword(
request.getUserName(), request.getPassword()).orElse(null);
if (user == null) {
return EsignLoginResponse.fail("401", "Invalid username or password");
}
// 检查用户状态
if (!"ACTIVE".equalsIgnoreCase(user.getStatus())) {
if ("INACTIVE".equalsIgnoreCase(user.getStatus())) {
return EsignLoginResponse.fail("403", "User is inactive");
} else if ("LOCKED".equalsIgnoreCase(user.getStatus())) {
return EsignLoginResponse.fail("403", "User is locked");
}
return EsignLoginResponse.fail("403", "User status is invalid");
}
user.setLastLoginAt(Instant.now());
String normalizedIp = EsignUserPresenceService.normalizeClientIp(clientIp);
if (normalizedIp != null) {
user.setLastLoginIp(normalizedIp);
}
boolean tokenUpdated = applyDeviceToken(user, request.getDeviceToken());
userRepository.save(user);
log.info("Esign login user={}, deviceTokenUpdated={}", user.getUserName(), tokenUpdated);
// 生成JWT Token,包含 user_id, username, hotel_id, role
String token = generateToken(user);
return EsignLoginResponse.success(
token,
user.getId(),
user.getUserName(),
user.getHotelId(),
user.getRole()
);
}
@Transactional
public EsignSimpleResponse bindDeviceToken(Long userId, EsignBindDeviceTokenRequest request) {
if (userId == null) {
return EsignSimpleResponse.fail("401", "Unauthorized");
}
EsignUser user = userRepository.findById(userId).orElse(null);
if (user == null) {
return EsignSimpleResponse.fail("404", "User not found");
}
if (!"ACTIVE".equalsIgnoreCase(user.getStatus())) {
return EsignSimpleResponse.fail("403", "User status is invalid");
}
applyDeviceToken(user, request.getDeviceToken());
userRepository.save(user);
return EsignSimpleResponse.ok("Device token bound");
}
private boolean applyDeviceToken(EsignUser user, String deviceToken) {
if (StringUtils.isNotBlank(deviceToken)) {
user.setDeviceToken(deviceToken.trim());
return true;
}
return false;
}
private String generateToken(EsignUser user) {
// 平板 App 长连接:使用 app_session 有效期,避免 5 分钟 accessToken 导致 WS 重连失败
long expiryMinutes = JwtTokenUtil.getAppSessionExpiration();
long expiryMillis = expiryMinutes * 60 * 1000;
Date now = new Date();
Date expDate = new Date(now.getTime() + expiryMillis);
String uuid = UUID.randomUUID().toString();
// 正确的 JWT 生成方式
return JWT.create()
.withIssuer("esign_login")
.withIssuedAt(now)
.withNotBefore(now)
.withExpiresAt(expDate)
.withJWTId(uuid)
.withClaim("employeeId", String.valueOf(user.getId()))
.withClaim("userName", user.getUserName())
.withClaim("hotelId", user.getHotelId() != null ? user.getHotelId() : "")
.withClaim("role", user.getRole())
.withSubject(String.valueOf(user.getId()))
.sign(Algorithm.HMAC256(JwtTokenUtil.getSecretKey()));
}
}
@@ -1,178 +0,0 @@
package com.sjmresorts.opsapp.esign.auth.services;
import com.sjmresorts.opsapp.esign.auth.models.EsignUser;
import com.sjmresorts.opsapp.esign.auth.repositories.EsignUserRepository;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 记录 App 客户端 IP,并按 IP 将多台账号视为同一物理设备(保留最近活跃账号)。
*/
@Slf4j
@Service
public class EsignUserPresenceService {
@Resource
private EsignUserRepository esignUserRepository;
public static String normalizeClientIp(String ip) {
if (StringUtils.isBlank(ip)) {
return null;
}
String t = ip.trim();
if ("0:0:0:0:0:0:0:1".equals(t)) {
return "127.0.0.1";
}
int comma = t.indexOf(',');
if (comma > 0) {
t = t.substring(0, comma).trim();
}
return t.isEmpty() ? null : t;
}
@Transactional
public void touchClientIp(Long userId, String clientIp) {
if (userId == null) {
return;
}
String ip = normalizeClientIp(clientIp);
if (ip == null) {
return;
}
esignUserRepository.findById(userId).ifPresent(user -> {
if (!ip.equals(user.getLastLoginIp())) {
user.setLastLoginIp(ip);
esignUserRepository.save(user);
log.debug("Updated client IP userId={}, ip={}", userId, ip);
}
});
}
@Transactional(readOnly = true)
public LinkedHashSet<Long> dedupeUserIdsByClientIp(Collection<Long> userIds) {
if (userIds == null || userIds.isEmpty()) {
return new LinkedHashSet<>();
}
Set<Long> ids = userIds.stream().filter(Objects::nonNull).collect(Collectors.toCollection(LinkedHashSet::new));
if (ids.isEmpty()) {
return new LinkedHashSet<>();
}
Map<Long, EsignUser> users = loadUsers(ids);
return dedupeUserIdsByClientIp(ids, users::get);
}
@Transactional(readOnly = true)
public List<EsignUser> dedupeUsersByClientIp(List<EsignUser> users) {
if (users == null || users.isEmpty()) {
return List.of();
}
Map<String, EsignUser> bestByIp = new LinkedHashMap<>();
List<EsignUser> withoutIp = new ArrayList<>();
for (EsignUser user : users) {
if (user == null || user.getId() == null) {
continue;
}
String ip = normalizeClientIp(user.getLastLoginIp());
if (ip == null) {
withoutIp.add(user);
continue;
}
EsignUser existing = bestByIp.get(ip);
if (existing == null || isMoreRecentlyActive(user, existing)) {
bestByIp.put(ip, user);
}
}
List<EsignUser> result = new ArrayList<>(bestByIp.values());
result.addAll(withoutIp);
result.sort(Comparator
.comparing(EsignUser::getLastLoginAt, Comparator.nullsLast(Comparator.reverseOrder()))
.thenComparing(EsignUser::getId, Comparator.nullsLast(Comparator.reverseOrder())));
return result;
}
@Transactional(readOnly = true)
public int countUniqueClientIps(Collection<Long> userIds) {
return dedupeUserIdsByClientIp(userIds).size();
}
private LinkedHashSet<Long> dedupeUserIdsByClientIp(
Collection<Long> userIds,
Function<Long, EsignUser> userLoader) {
Map<String, Long> bestUserIdByIp = new LinkedHashMap<>();
LinkedHashSet<Long> withoutIp = new LinkedHashSet<>();
for (Long userId : userIds) {
if (userId == null) {
continue;
}
EsignUser user = userLoader.apply(userId);
String ip = user != null ? normalizeClientIp(user.getLastLoginIp()) : null;
if (ip == null) {
withoutIp.add(userId);
continue;
}
Long existingId = bestUserIdByIp.get(ip);
if (existingId == null) {
bestUserIdByIp.put(ip, userId);
continue;
}
EsignUser existing = userLoader.apply(existingId);
if (isMoreRecentlyActive(user, existing)) {
bestUserIdByIp.put(ip, userId);
}
}
LinkedHashSet<Long> result = new LinkedHashSet<>(bestUserIdByIp.values());
result.addAll(withoutIp);
return result;
}
private Map<Long, EsignUser> loadUsers(Set<Long> userIds) {
Map<Long, EsignUser> map = new LinkedHashMap<>();
for (Long id : userIds) {
esignUserRepository.findById(id).ifPresent(u -> map.put(id, u));
}
return map;
}
private static boolean isMoreRecentlyActive(EsignUser candidate, EsignUser incumbent) {
if (candidate == null) {
return false;
}
if (incumbent == null) {
return true;
}
Instant c = candidate.getLastLoginAt();
Instant i = incumbent.getLastLoginAt();
if (c == null) {
return false;
}
if (i == null) {
return true;
}
int cmp = c.compareTo(i);
if (cmp != 0) {
return cmp > 0;
}
Long cId = candidate.getId();
Long iId = incumbent.getId();
if (cId == null || iId == null) {
return false;
}
return cId > iId;
}
}
@@ -1,153 +0,0 @@
package com.sjmresorts.opsapp.esign.capture.config;
import com.sjmresorts.opsapp.common.db.DbDialect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
/** 证件采集表结构迁移(多库方言;由 EsignCmsSchemaMigrator 在 JPA 启动前调用)。 */
@Component("esignCaptureSchemaMigrator")
public class EsignCaptureSchemaMigrator {
private static final Logger log = LoggerFactory.getLogger(EsignCaptureSchemaMigrator.class);
private final JdbcTemplate jdbc;
private final DbDialect dialect;
public EsignCaptureSchemaMigrator(
@Qualifier("opsAppDataSource") DataSource dataSource, DbDialect dialect) {
this.jdbc = new JdbcTemplate(dataSource);
this.dialect = dialect;
}
public void ensureTables() {
try {
renameLegacyCaptureTablesIfNeeded();
ensureCaptureTasksTable();
ensureCaptureTaskImagesTable();
renameLegacyImageObjectNamesIfNeeded();
} catch (RuntimeException e) {
log.error("Esign capture migrate failed", e);
throw e;
}
if (!dialect.tableExists(jdbc, "esign_capture_tasks")) {
throw new IllegalStateException(
"esign_capture_tasks 仍不存在,请检查库权限或手工执行 scripts 下对应库的建表脚本");
}
log.info("Esign capture migrate: esign_capture_tasks ready ({})", dialect.type());
}
private void renameLegacyCaptureTablesIfNeeded() {
if (!dialect.tableExists(jdbc, "esign_capture_tasks")
&& dialect.tableExists(jdbc, "sinosecu_capture_tasks")) {
log.info("Esign capture migrate: renaming sinosecu_capture_tasks -> esign_capture_tasks");
dialect.renameTable(jdbc, "sinosecu_capture_tasks", "esign_capture_tasks");
}
if (!dialect.tableExists(jdbc, "esign_capture_task_images")
&& dialect.tableExists(jdbc, "sinosecu_capture_task_images")) {
log.info(
"Esign capture migrate: renaming sinosecu_capture_task_images -> esign_capture_task_images");
dialect.renameTable(jdbc, "sinosecu_capture_task_images", "esign_capture_task_images");
}
}
private void renameLegacyImageObjectNamesIfNeeded() {
if (!dialect.tableExists(jdbc, "esign_capture_task_images")) {
return;
}
renameObjectIfExists("uq_sinosecu_img_task_slot", "uq_esign_img_task_slot");
renameObjectIfExists("fk_sinosecu_img_task", "fk_esign_capture_img_task");
renameObjectIfExists("ix_sinosecu_capture_task_images_task", "ix_esign_capture_task_images_task");
}
private void renameObjectIfExists(String oldName, String newName) {
if (!dialect.objectExists(jdbc, oldName) || dialect.objectExists(jdbc, newName)) {
return;
}
log.info("Esign capture migrate: renaming {} -> {}", oldName, newName);
dialect.renameObject(jdbc, oldName, newName);
}
private void ensureCaptureTasksTable() {
if (dialect.tableExists(jdbc, "esign_capture_tasks")) {
add("esign_capture_tasks", "signed_at", dialect.datetime() + " NULL");
add("esign_capture_tasks", "sign_remark", dialect.varchar(2000) + " NULL");
add("esign_capture_tasks", "sign_signature", dialect.text() + " NULL");
return;
}
log.info("Esign capture migrate: creating esign_capture_tasks");
jdbc.execute(
"CREATE TABLE esign_capture_tasks ("
+ "task_id "
+ dialect.varchar(36)
+ " NOT NULL PRIMARY KEY, "
+ "payload_json "
+ dialect.text()
+ " NOT NULL, "
+ "employee_id "
+ dialect.varchar(128)
+ " NULL, "
+ "status "
+ dialect.varchar(32)
+ " NOT NULL, "
+ "created_at "
+ dialect.datetime()
+ " NOT NULL, "
+ "signed_at "
+ dialect.datetime()
+ " NULL, "
+ "sign_remark "
+ dialect.varchar(2000)
+ " NULL, "
+ "sign_signature "
+ dialect.text()
+ " NULL)");
}
private void ensureCaptureTaskImagesTable() {
if (dialect.tableExists(jdbc, "esign_capture_task_images")
|| dialect.tableExists(jdbc, "sinosecu_capture_task_images")) {
return;
}
if (!dialect.tableExists(jdbc, "esign_capture_tasks")) {
return;
}
if (dialect.objectExists(jdbc, "uq_sinosecu_img_task_slot")
|| dialect.objectExists(jdbc, "uq_esign_img_task_slot")) {
log.warn(
"Esign capture migrate: skip creating esign_capture_task_images; "
+ "unique constraint already exists (legacy sinosecu table may need rename)");
return;
}
log.info("Esign capture migrate: creating esign_capture_task_images");
jdbc.execute(
"CREATE TABLE esign_capture_task_images ("
+ dialect.bigintIdentityPrimaryKey("id")
+ ", "
+ "task_id "
+ dialect.varchar(36)
+ " NOT NULL, "
+ "slot_key "
+ dialect.varchar(64)
+ " NOT NULL, "
+ "image_data "
+ dialect.text()
+ " NOT NULL, "
+ "CONSTRAINT uq_esign_img_task_slot UNIQUE (task_id, slot_key), "
+ "CONSTRAINT fk_esign_capture_img_task FOREIGN KEY (task_id) "
+ "REFERENCES esign_capture_tasks (task_id) ON DELETE CASCADE)");
jdbc.execute(
"CREATE INDEX ix_esign_capture_task_images_task ON esign_capture_task_images (task_id)");
}
private void add(String table, String column, String typeSql) {
if (!dialect.columnExists(jdbc, table, column)) {
log.info("Esign capture migrate: adding {}.{}", table, column);
jdbc.execute(dialect.alterAddColumn(table, column, typeSql));
}
}
}
@@ -1,177 +0,0 @@
package com.sjmresorts.opsapp.esign.capture.controllers;
import com.auth0.jwt.JWTVerifier;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
import com.sjmresorts.opsapp.common.utils.NetworkUtil;
import com.sjmresorts.opsapp.esign.capture.dtos.EsignIosCaptureResponse;
import com.sjmresorts.opsapp.esign.printer.dtos.EsignPendingListResponse;
import com.sjmresorts.opsapp.esign.reservation.dtos.EsignReservationTaskCompleteRequest;
import com.sjmresorts.opsapp.esign.printer.dtos.EsignSignTaskRequest;
import com.sjmresorts.opsapp.esign.printer.dtos.EsignTaskDetailResponse;
import com.sjmresorts.opsapp.esign.capture.services.EsignCaptureService;
import com.sjmresorts.opsapp.esign.reservation.services.EsignReservationTaskService;
import com.sjmresorts.opsapp.esign.auth.services.EsignUserPresenceService;
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 org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@Tag(name = "Esign", description = "证件采集与待签任务")
@RestController
@RequestMapping({"/api/v1/esign", "/api/v1/sinosecu"})
@CrossOrigin(origins = "*")
public class EsignCaptureController {
@Resource
private EsignCaptureService esignCaptureService;
@Resource
private EsignReservationTaskService esignReservationTaskService;
@Resource
private EsignUserPresenceService userPresenceService;
private Long resolveUserIdAndTouchClientIp(HttpServletRequest request) {
Long userId = resolveUserIdIfVerifiedJwt(request);
if (userId != null) {
userPresenceService.touchClientIp(userId, NetworkUtil.getClientIpByRequest(request));
}
return userId;
}
@Operation(summary = "提交证件采集结果(待 iOS 签名)", description = "免 JWT;任务进入待签池,由 GET ios-pending-tasks 广播拉取(不按工号过滤)。")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "创建成功"),
@ApiResponse(responseCode = "500", description = "服务器错误")
})
@PostMapping(value = "/ios-capture-submit", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignIosCaptureResponse> submitIosCapture(
HttpServletRequest request,
@RequestBody Map<String, Object> body) {
String employeeId = resolveEmployeeIdIfVerifiedJwt(request);
try {
String taskId = esignCaptureService.createTask(employeeId, body);
EsignIosCaptureResponse ok = new EsignIosCaptureResponse();
ok.setSuccess(true);
ok.setCode("200");
ok.setMessage("Capture task created");
EsignIosCaptureResponse.EsignIosCaptureData data = new EsignIosCaptureResponse.EsignIosCaptureData();
data.setTaskId(taskId);
ok.setData(data);
return ResponseEntity.status(HttpStatus.OK).body(ok);
} catch (JsonProcessingException e) {
EsignIosCaptureResponse err = new EsignIosCaptureResponse();
err.setSuccess(false);
err.setCode("500");
err.setMessage("Failed to serialize payload");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(err);
}
}
@Operation(summary = "待签任务列表", description = "带有效 JWT 时仅返回推送给当前平板账号的虚拟打印机 PDF 待签;无 JWT 时广播证件/预订待签。可通过 hotelId 筛选酒店。")
@GetMapping(value = "/ios-pending-tasks", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignPendingListResponse> listPendingTasks(
HttpServletRequest request,
@RequestParam(value = "hotelId", required = false) String hotelId) {
Long userId = resolveUserIdAndTouchClientIp(request);
String clientIp = NetworkUtil.getClientIpByRequest(request);
EsignPendingListResponse body = esignCaptureService.listTaskBucket("pending", hotelId, userId, clientIp);
return ResponseEntity.status(HttpStatus.OK).body(body);
}
@Operation(summary = "任务列表按桶查询", description = "带有效 JWT 时仅返回当前平板账号的虚拟打印机 PDF;无 JWT 时广播证件/预订。bucket=pending|signed|voided。")
@GetMapping(value = "/ios-task-list", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignPendingListResponse> listTasksByBucket(
HttpServletRequest request,
@RequestParam(value = "bucket", required = false) String bucket,
@RequestParam(value = "hotelId", required = false) String hotelId) {
Long userId = resolveUserIdAndTouchClientIp(request);
String clientIp = NetworkUtil.getClientIpByRequest(request);
EsignPendingListResponse body = esignCaptureService.listTaskBucket(bucket, hotelId, userId, clientIp);
return ResponseEntity.status(HttpStatus.OK).body(body);
}
@Operation(summary = "iOS 签收提交", description = "免 JWT。仅待签收且创建未超 24 小时可提交。")
@PostMapping(value = "/ios-task-sign", consumes = "application/json;charset=UTF-8", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignIosCaptureResponse> signTask(@RequestBody EsignSignTaskRequest request) {
String taskId = request != null ? request.getTaskId() : null;
String remark = request != null ? request.getSignRemark() : null;
String signature = request != null ? request.getSignSignature() : null;
EsignIosCaptureResponse body = esignCaptureService.signTask(taskId, remark, signature);
return ResponseEntity.status(HttpStatus.OK).body(body);
}
@Operation(summary = "预订待处理任务完成", description = "免 JWT。用户打开登记卡后调用,从待签列表移除。")
@PostMapping(value = "/ios-reservation-task-complete", consumes = "application/json;charset=UTF-8", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignIosCaptureResponse> completeReservationTask(
@RequestBody EsignReservationTaskCompleteRequest request) {
String taskId = request != null ? request.getTaskId() : null;
String reservationId = request != null ? request.getReservationId() : null;
String hotelId = request != null ? request.getHotelId() : null;
EsignIosCaptureResponse body =
esignReservationTaskService.completeTask(taskId, reservationId, hotelId);
return ResponseEntity.status(HttpStatus.OK).body(body);
}
@Operation(summary = "待签任务详情", description = "免 JWT。返回落库时的完整 JSON payload,供 App 展示表单。")
@GetMapping(value = "/ios-task/{taskId}", produces = "application/json;charset=UTF-8")
public ResponseEntity<EsignTaskDetailResponse> getTaskDetail(@PathVariable("taskId") String taskId) {
EsignTaskDetailResponse body = esignCaptureService.getTaskDetail(taskId);
if (!body.isSuccess() && "404".equals(body.getCode())) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(body);
}
if (!body.isSuccess() && "500".equals(body.getCode())) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(body);
}
if (!body.isSuccess()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(body);
}
return ResponseEntity.status(HttpStatus.OK).body(body);
}
/** 白名单接口:仅在校验通过后才从 JWT 取 employeeId,避免伪造。 */
private static String resolveEmployeeIdIfVerifiedJwt(HttpServletRequest request) {
Long userId = resolveUserIdIfVerifiedJwt(request);
return userId != null ? String.valueOf(userId) : null;
}
private static Long resolveUserIdIfVerifiedJwt(HttpServletRequest request) {
String auth = request.getHeader("Authorization");
if (StringUtils.isBlank(auth) || !auth.startsWith("Bearer ")) {
return null;
}
String token = auth.substring(7).trim();
if (StringUtils.isBlank(token)) {
return null;
}
try {
JWTVerifier jwtVerifier = JwtTokenUtil.getJwtVerifier();
jwtVerifier.verify(token);
LoginUserInfo user = JwtTokenUtil.parseUserFromToken(token);
if (user == null || StringUtils.isBlank(user.getEmployeeId())) {
return null;
}
return Long.parseLong(user.getEmployeeId());
} catch (Exception e) {
return null;
}
}
}
@@ -1,88 +0,0 @@
package com.sjmresorts.opsapp.esign.capture.controllers;
import com.sjmresorts.opsapp.authentication.dtos.LoginUserInfo;
import com.sjmresorts.opsapp.common.utils.JwtTokenUtil;
import com.sjmresorts.opsapp.esign.capture.dtos.CreateEsignLogRequest;
import com.sjmresorts.opsapp.esign.capture.dtos.CreateEsignLogResponse;
import com.sjmresorts.opsapp.esign.capture.dtos.EsignLogDetailDto;
import com.sjmresorts.opsapp.esign.capture.dtos.EsignLogListDto;
import com.sjmresorts.opsapp.esign.capture.dtos.EsignLogQueryRequest;
import com.sjmresorts.opsapp.esign.capture.models.EsignLog;
import com.sjmresorts.opsapp.esign.capture.services.EsignLogService;
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.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@Tag(name = "Capture Logs", description = "证件采集日志(Capture Log);主路径 /api/v1/esign/capture-logs,兼容 /api/v1/logs")
@RestController
@RequestMapping({"/api/v1/esign/capture-logs", "/api/v1/logs"})
@CrossOrigin(origins = "*")
@RequiredArgsConstructor
public class EsignLogController {
private final EsignLogService esignLogService;
@Operation(
summary = "写入证件采集日志",
description = "POST /api/v1/esign/capture-logs — 记录证件采集流程操作日志(/api/v1/logs 兼容至 2026-09-30)"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "写入成功"),
@ApiResponse(responseCode = "400", description = "请求参数错误"),
@ApiResponse(responseCode = "500", description = "服务器内部错误")
})
@PostMapping(produces = "application/json;charset=UTF-8")
public ResponseEntity<CreateEsignLogResponse> createLog(
@Valid @RequestBody CreateEsignLogRequest request,
HttpServletRequest httpRequest) { // 添加 httpRequest 参数
try {
// 从 token 中获取 operatorId(即 employeeId)
String operatorId = extractOperatorIdFromToken(httpRequest);
EsignLog saved = esignLogService.createLog(request, operatorId);
return ResponseEntity.ok(CreateEsignLogResponse.ok(saved.getId()));
} catch (Exception e) {
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(CreateEsignLogResponse.fail(500, "日志写入失败: " + e.getMessage()));
}
}
// ==================== 分页列表接口 ====================
@Operation(summary = "查询submit日志列表(有效/无效批次)")
@GetMapping("/list")
public ResponseEntity<Page<EsignLogListDto>> list(
@Valid EsignLogQueryRequest request,
@RequestParam(value = "hotelId", required = false) String hotelId) {
return ResponseEntity.ok(esignLogService.querySubmitLogs(request, hotelId));
}
// ==================== 详情接口 ====================
@Operation(summary = "日志详情(左右展示ORC/RFID)")
@GetMapping("/detail/{id}")
public ResponseEntity<EsignLogDetailDto> detail(@PathVariable Long id) {
return ResponseEntity.ok(esignLogService.getLogDetail(id));
}
/**
* 从请求头的 JWT token 中提取 operatorId(即 employeeId)
*/
private String extractOperatorIdFromToken(HttpServletRequest request) {
try {
LoginUserInfo user = JwtTokenUtil.parseUserFromRequest(request);
if (user != null) {
return user.getEmployeeId();
}
} catch (Exception e) {
// token 解析失败,返回 null
}
return null;
}
}
@@ -1,46 +0,0 @@
package com.sjmresorts.opsapp.esign.capture.dtos;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
@Schema(description = "写入 esign_log 请求")
public class CreateEsignLogRequest {
@NotBlank(message = "batch_id 不能为空")
@Schema(description = "业务批次ID (UUID v4)", example = "f47ac10b-58cc-4372-a567-0e02b2c3d479")
private String batchId;
@NotBlank(message = "action_type 不能为空")
@Schema(description = "操作类型: scan_front / scan_back / scan / submit", example = "scan_front")
private String actionType;
// 新字段
@Schema(description = "OCR识别当面完整数据(JSON)")
private String orcPayload;
@Schema(description = "RFID芯片读取数据(JSON),暂未使用")
private String rfidPayload;
@Schema(description = "酒店ID", example = "hotel_001")
private String hotelId;
@Schema(description = "任务ID", example = "task_123")
private String taskId;
@NotBlank(message = "status 不能为空")
@Schema(description = "状态: success / failed", example = "success")
private String status;
@Schema(description = "错误信息")
private String errorMsg;
@Schema(description = "操作员ID", example = "op_001")
private String operatorId;
}
@@ -1,44 +0,0 @@
package com.sjmresorts.opsapp.esign.capture.dtos;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@Schema(description = "写入 esign_log 响应")
public class CreateEsignLogResponse {
@Schema(description = "状态码", example = "200")
private int code;
@Schema(description = "说明", example = "ok")
private String msg;
@Schema(description = "业务数据")
private LogData data;
@Data
@NoArgsConstructor
public static class LogData {
@Schema(description = "日志ID", example = "1")
private Long id;
}
public static CreateEsignLogResponse ok(Long id) {
CreateEsignLogResponse resp = new CreateEsignLogResponse();
resp.setCode(200);
resp.setMsg("ok");
LogData d = new LogData();
d.setId(id);
resp.setData(d);
return resp;
}
public static CreateEsignLogResponse fail(int code, String msg) {
CreateEsignLogResponse resp = new CreateEsignLogResponse();
resp.setCode(code);
resp.setMsg(msg);
return resp;
}
}
@@ -1,49 +0,0 @@
package com.sjmresorts.opsapp.esign.capture.dtos;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@Schema(description = "证件采集提交结果")
public class EsignIosCaptureResponse {
@Schema(description = "是否成功")
private boolean success;
@Schema(description = "状态码", example = "200")
private String code;
@Schema(description = "说明")
private String message;
@Schema(description = "业务数据")
private EsignIosCaptureData data;
@Data
@NoArgsConstructor
@Schema(description = "提交后的任务标识")
public static class EsignIosCaptureData {
@Schema(description = "待签任务 ID,供 iOS 通知或拉取")
private String taskId;
@Schema(description = "从 PDF 解析的 OPERA 确认号")
private String confirmationNumber;
@Schema(description = "OHIP reservation id(linkId)")
private String reservationId;
@Schema(description = "是否已提交至 OPERA fileAttachments")
private Boolean ohipUploaded;
@Schema(description = "签名 PDF 是否已归档至 Java 服务")
private Boolean archived;
@Schema(description = "PDF 签收后自动打开的问卷 ID")
private String nextSurveyId;
@Schema(description = "PDF 签收后自动打开的问卷标题")
private String nextSurveyTitle;
}
}
@@ -1,43 +0,0 @@
package com.sjmresorts.opsapp.esign.capture.dtos;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@Schema(description = "日志详情返回对象")
public class EsignLogDetailDto {
@Schema(description = "日志ID")
private Long id;
@Schema(description = "批次号")
private String batchId;
@Schema(description = "操作类型")
private String actionType;
@Schema(description = "OCR识别原始数据(左侧展示)", example = "{\"name\":\"张三\",\"idNo\":\"110***...\"}")
private String orcPayload;
@Schema(description = "RFID读卡原始数据(右侧展示)", example = "{\"cardNo\":\"CARD***...\"}")
private String rfidPayload;
@Schema(description = "酒店ID")
private String hotelId;
@Schema(description = "任务ID")
private String taskId;
@Schema(description = "状态")
private String status;
@Schema(description = "错误信息")
private String errorMsg;
@Schema(description = "操作人ID")
private String operatorId;
@Schema(description = "创建时间")
private LocalDateTime createdAt;
}
@@ -1,37 +0,0 @@
package com.sjmresorts.opsapp.esign.capture.dtos;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@Schema(description = "日志列表返回对象")
public class EsignLogListDto {
@Schema(description = "日志ID")
private Long id;
@Schema(description = "批次号")
private String batchId;
@Schema(description = "操作类型")
private String actionType;
@Schema(description = "酒店ID")
private String hotelId;
@Schema(description = "任务ID")
private String taskId;
@Schema(description = "状态")
private String status;
@Schema(description = "错误信息")
private String errorMsg;
@Schema(description = "操作人ID")
private String operatorId;
@Schema(description = "创建时间")
private LocalDateTime createdAt;
}
@@ -1,24 +0,0 @@
package com.sjmresorts.opsapp.esign.capture.dtos;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
@Data
@Schema(description = "日志列表查询请求")
public class EsignLogQueryRequest {
@Schema(description = "页码 从1开始", example = "1")
@Min(1)
private int page = 1;
@Schema(description = "每页条数", example = "10")
@Min(1) @Max(100)
private int size = 10;
@Schema(description = "类型:valid-有效批次 success / invalid-无效批次 fail", example = "valid")
private String type; // valid / invalid
private String taskId;
}

Some files were not shown because too many files have changed in this diff Show More