using System.Globalization; using System.Security.Claims; using Amazon.DynamoDBv2.Model; using Asp.Versioning; using MetaAssets; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using ServerCommon; using ServerCommon.UGQ.Models; using Swashbuckle.AspNetCore.Annotations; using UGQApiServer.Controllers.Common; using UGQApiServer.Converter; using UGQApiServer.Models; using UGQApiServer.Auth; using UGQDataAccess.Repository.Models; using UGQDataAccess.Service; using UGQDatabase.Models; using ServerCommon.BusinessLogDomain; using UGQDataAccess.Repository; using MySqlConnector; using ServerBase; namespace UGQApiServer.Controllers.Admin; [ApiController] [ApiVersion("1.0")] [ApiExplorerSettings(GroupName = "admin")] [Route("api/v{version:apiVersion}/Admin/Account")] [ControllerName("Account")] [UGQAdminApi] [Authorize(Roles = "Admin,Service")] public class AdminAccountController : ControllerBase { const int MAX_PAGE_SIZE = 100; AccountService _accountService; QuestEditorService _questEditorService; MetaDataApi _metaDataApi; QuestEditorApi _questEditorApi; DynamoDbClient _dynamoDbClient; AuthSql _authSql; public AdminAccountController( AccountService accountService, QuestEditorService questEditorService, MetaDataApi metaDataApi, QuestEditorApi questEditorApi, DynamoDbClient dynamoDbClient, AuthSql authSql) { _accountService = accountService; _questEditorService = questEditorService; _metaDataApi = metaDataApi; _questEditorApi = questEditorApi; _dynamoDbClient = dynamoDbClient; _authSql = authSql; } string? adminUsernameFromToken() { return User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value; } /// /// 모든 계정 리스트 얻기 /// [HttpGet] [Route("all-accounts")] [Produces("application/json")] public async Task getAllAccounts([FromQuery] int pageNumber, [FromQuery] int pageSize, [FromQuery] string? searchText, [FromQuery] AccountSortType sortType) { pageSize = Math.Clamp(pageSize, 1, MAX_PAGE_SIZE); var result = await _accountService.getAccounts(pageNumber, pageSize, searchText, sortType); long totalCount = await _accountService.getAllAccountCount(); return Results.Ok(new UGQAllAccountItemResponse { TotalCount = totalCount, PageNumber = result.PageNumber, PageSize = result.PageSize, TotalPages = result.TotalPages, Accounts = result.Items.Select(x => x.ToDTO()).ToList() }); } /// /// 계정 정보 얻기 /// [HttpGet] [Route("account")] [Produces("application/json")] public async Task getAccount([FromQuery] string userGuid) { var accountEntity = await _accountService.getAccount(userGuid); if (accountEntity == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); (Result result, MoneyAttrib? moneyAttrib) = await EntitySearchHelper.findUserMoneyByUserGuid(_dynamoDbClient, accountEntity.UserGuid); if (result.isFail()) return Results.BadRequest(ApiResponseFactory.error(result.ErrorCode)); if(moneyAttrib == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UserMoneyDocEmpty)); return Results.Ok(new UGQAccountMoney { Sapphire = moneyAttrib.Sapphire, UserGuid = accountEntity.UserGuid, Nickname = accountEntity.Nickname, AccountId = accountEntity.AccountId ?? "", SlotCount = (MetaHelper.GameConfigMeta.UGQDefaultSlot + accountEntity.AdditionalSlotCount), GradeType = accountEntity.GradeType, CreatorPoint = accountEntity.CreatorPoint, }); } /// /// 계정의 UGQ 슬롯 수 변경 /// [HttpPost] [Route("modify-account-slot")] [Produces("application/json")] public async Task modifyAccountSlot([FromBody] UGQModifyAccountSlot model) { string? adminUsername = adminUsernameFromToken(); if (adminUsername == null) return Results.Unauthorized(); (ServerErrorCode errorCode, var accountEntity) = await _accountService.modifySlotCount(model.UserGuid, model.Count, adminUsername); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (accountEntity == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); return Results.Ok(accountEntity.ToDTO()); } /// /// 계정의 UGQ 등급 변경 예약 /// [HttpPost] [Route("reserve-account-grade")] [Produces("application/json")] public async Task reserveAccountGrade([FromBody] UGQReserveAccountGrade model) { if (DateTime.UtcNow > model.ReserveTime) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidReserveTime)); var accountEntity = await _accountService.getAccount(model.UserGuid); if (accountEntity == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); if(accountEntity.GradeType == model.GradeType) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqSameGradeReserve)); var reserveAccountGradeEntity = await _accountService.getReserveAccountGrade(model.UserGuid); if(reserveAccountGradeEntity != null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqAlreadyExistsReserveGrade)); reserveAccountGradeEntity = await _accountService.reserveAccountGrade(model.UserGuid, accountEntity.GradeType, model.GradeType, model.ReserveTime); if (reserveAccountGradeEntity == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); return Results.Ok(reserveAccountGradeEntity.ToDTO()); } /// /// 계정의 UGQ 등급 변경 예약 수정 /// [HttpPost] [Route("modify-reserve-account-grade")] [Produces("application/json")] public async Task modifyReserveAccountGrade([FromBody] UGQModifyAccountGrade model) { if (DateTime.UtcNow > model.ReserveTime) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidReserveTime)); var reserveAccountGradeEntity = await _accountService.modifyReserveAccountGrade(model.ReserveId, model.GradeType, model.ReserveTime); if (reserveAccountGradeEntity == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); return Results.Ok(reserveAccountGradeEntity.ToDTO()); } /// /// 계정의 UGQ 등급 변경 예약 취소 /// [HttpPost] [Route("delete-reserve-account-grade")] [Produces("application/json")] public async Task deleteReserveAccountGrade([FromQuery] string reserveId) { var errorCode = await _accountService.deleteReserveAccountGrade(reserveId); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); return Results.Ok(); } /// /// 계정의 UGQ 등급 변경 예약 조회 /// [HttpPost] [Route("all-reserve-account-grade")] [Produces("application/json")] public async Task getAllReserveAccountGrade([FromQuery] int pageNumber, [FromQuery] int pageSize, [FromQuery] AccountGradeSortType sortType, [FromQuery] AccountGradeProcessType processType, [FromQuery] string? accountId) { string userGuid = string.Empty; if(accountId != null) { var accountEntity = await _accountService.getAccountByAccountId(accountId); if (accountEntity == null || accountEntity.AccountId == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); userGuid = accountEntity.UserGuid; } var result = await _accountService.getReserveAccountGrades(pageNumber, pageSize, sortType, processType, userGuid); if (result == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); long totalCount = await _accountService.getAllReserveAccountGradeCount(); if (accountId != null) totalCount = result.Items.Count; return Results.Ok(new UGQAllReserveAccountGradeItemResponse { TotalCount = totalCount, PageNumber = result.PageNumber, PageSize = result.PageSize, TotalPages = result.TotalPages, Accounts = result.Items.Select(x => x.ToDTO()).ToList() }); } /// /// 계정의 UGQ 등급 변경 예약 조회 /// [HttpPost] [Route("get-reserve-account-grade")] [Produces("application/json")] public async Task getReserveAccountGrade([FromQuery] string accountId, [FromQuery] AccountGradeProcessType processType) { var accountEntity = await _accountService.getAccountByAccountId(accountId); if(accountEntity == null || accountEntity.AccountId == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); var reserveAccountGradeEntityList = await _accountService.getReserveAccountGrade(accountEntity.UserGuid, processType); if (reserveAccountGradeEntityList == null || reserveAccountGradeEntityList.Count == 0) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); return Results.Ok(new UGQReserveAccountGradeItemResponse { Accounts = reserveAccountGradeEntityList.Select(x => x.ToDTO()).ToList() }); } /// /// 계정의 Creator Point 변경 /// [HttpPost] [Route("modify-creator-point")] [Produces("application/json")] public async Task modifyCreatorPoint([FromBody] UGQModifyCreatorPoint model) { string? adminUsername = adminUsernameFromToken(); if (adminUsername == null) return Results.Unauthorized(); (var errorCode, var accountEntity) = await _accountService.modifyCreatorPoint(model.UserGuid, model.Amount, model.Reason, adminUsername); if (errorCode != ServerErrorCode.Success) return Results.BadRequest(ApiResponseFactory.error(errorCode)); if (accountEntity == null) return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity)); return Results.Ok(accountEntity.ToDTO()); } /// /// 메타버스 계정 정보 얻기 /// /// /// email을 입력하면 email로 accountId를 검색, email을 입력 안 하면 accountId 입력 값을 사용 /// [HttpGet] [Route("metaverse-account")] [Produces("application/json")] public async Task getMetaverseAccount([FromQuery] string? email, [FromQuery] ulong? accountId) { if (email != null) { accountId = await _authSql.getAccountIdFromMysql(email); if (accountId == 0) { return Results.Ok(new UGQMetaverseAccount { }); } } string loginAccountId = accountId.ToString() ?? ""; (Result result, UserByAccountIdResult? gameAccount) = await EntitySearchHelper.findUserByAccountId(_dynamoDbClient, loginAccountId, ""); if (result.isFail() && result.ErrorCode != ServerErrorCode.DynamoDbQueryNoMatchAttribute) return Results.BadRequest(ApiResponseFactory.error(result.ErrorCode)); if (gameAccount == null) { return Results.Ok(new UGQMetaverseAccount { LoginAccountId = loginAccountId, }); } else { return Results.Ok(new UGQMetaverseAccount { LoginAccountId = loginAccountId, UserGuid = gameAccount.UserGuid, Nickname = gameAccount.Nickname, }); } } }