초기커밋
This commit is contained in:
314
UGQApiServer/Controllers/AccountController.cs
Normal file
314
UGQApiServer/Controllers/AccountController.cs
Normal file
@@ -0,0 +1,314 @@
|
||||
using System.Text;
|
||||
using Asp.Versioning;
|
||||
using JwtRoleAuthentication.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ServerCommon;
|
||||
using ServerCommon.UGQ.Models;
|
||||
using StackExchange.Redis;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
using UGQApiServer.Auth;
|
||||
using UGQApiServer.Models;
|
||||
using UGQApiServer.Settings;
|
||||
using UGQDataAccess.Service;
|
||||
using UGQDatabase.Models;
|
||||
using ServerCore; using ServerBase;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using UGQDataAccess.Logs;
|
||||
using ServerCommon.BusinessLogDomain;
|
||||
|
||||
|
||||
namespace UGQApiServer.Controllers
|
||||
{
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{version:apiVersion}/[controller]")]
|
||||
[ApiController]
|
||||
public class AccountController : ControllerBase
|
||||
{
|
||||
AccountService _accountService;
|
||||
TokenService _tokenService;
|
||||
WebPortalTokenAuth _webPortalTokenAuth;
|
||||
DynamoDbClient _dynamoDbClient;
|
||||
IConnectionMultiplexer _redis;
|
||||
JWTSettings _jwtSettings;
|
||||
|
||||
public AccountController(
|
||||
AccountService accountService,
|
||||
TokenService tokenService,
|
||||
WebPortalTokenAuth webPortalTokenAuth,
|
||||
DynamoDbClient dynamoDbClient,
|
||||
IConnectionMultiplexer redis,
|
||||
IOptions<JWTSettings> settings)
|
||||
{
|
||||
_accountService = accountService;
|
||||
_tokenService = tokenService;
|
||||
_webPortalTokenAuth = webPortalTokenAuth;
|
||||
_dynamoDbClient = dynamoDbClient;
|
||||
_redis = redis;
|
||||
_jwtSettings = settings.Value;
|
||||
}
|
||||
|
||||
string? userGuidFromToken()
|
||||
{
|
||||
return User.Claims.FirstOrDefault(c => c.Type == "Id")?.Value;
|
||||
}
|
||||
|
||||
// #if DEBUG
|
||||
/// <summary>
|
||||
/// 게임DB와 연동된 버전 개발용 로그인
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 계정이 없으면 POST /api/v1/Admin/add-gamedb-account 로 테스트 계정을 만드십시오.
|
||||
/// </remarks>
|
||||
[HttpPost]
|
||||
[Route("dev-login-v2")]
|
||||
[Produces("application/json")]
|
||||
[DevelopmentOnly]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(LoginResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> loginDev([FromBody] DevLoginRequest request)
|
||||
{
|
||||
(Result result, UserByAccountIdResult? gameAccount) =
|
||||
await EntitySearchHelper.findUserByAccountId(_dynamoDbClient, request.LoginAccountId, "");
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqRequireAccount));
|
||||
|
||||
if (gameAccount == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
||||
|
||||
AccountEntity accountEntity = await _accountService.getOrInsertAccount(
|
||||
gameAccount.UserGuid, gameAccount.Nickname, request.LoginAccountId);
|
||||
|
||||
var accessToken = _tokenService.CreateToken(gameAccount.UserGuid, gameAccount.Nickname);
|
||||
var refreshToken = _tokenService.GenerateRefreshToken();
|
||||
DateTime refreshTokenExpryTime = DateTime.Now.AddDays(7);
|
||||
|
||||
if (await _accountService.saveRefreshToken(accountEntity.UserGuid, refreshToken, refreshTokenExpryTime) == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
{
|
||||
var expiry = TimeSpan.FromMinutes(_jwtSettings.TokenValidityInMinutes);
|
||||
await _redis.GetDatabase().StringSetAsync($"ugq_login:{accountEntity.UserGuid}", "online", expiry);
|
||||
}
|
||||
|
||||
List<ILogInvoker> business_logs = [
|
||||
new UgqApiLoginBusinessLog(UgqApiLoginType.DevelopmentApi),
|
||||
];
|
||||
var log_action = new LogActionEx(LogActionType.UgqApiLogin);
|
||||
UgqApiBusinessLogger.collectLogs(log_action, accountEntity, business_logs);
|
||||
|
||||
return Results.Ok(new LoginResponse
|
||||
{
|
||||
UserGuid = accountEntity.UserGuid,
|
||||
Nickname = accountEntity.Nickname,
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken,
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
|
||||
/// <summary>
|
||||
/// 로그인
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("login")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(LoginResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> login([FromBody] LoginRequest request)
|
||||
{
|
||||
WebPortalToken? webPortalToken = null;
|
||||
|
||||
{
|
||||
Result result2 = AuthHelper.verifySsoAccountAuthJWT(request.WebPortalToken,
|
||||
_webPortalTokenAuth.WebPortalTokenSecret,
|
||||
out var account_id, out var account_type, out var access_token);
|
||||
if (result2.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidWebPortalToken));
|
||||
|
||||
webPortalToken = new WebPortalToken
|
||||
{
|
||||
AccountId = account_id,
|
||||
AccountType = account_type,
|
||||
AccessToken = access_token,
|
||||
};
|
||||
|
||||
Log.getLogger().info($"webPortalToken. AccountId: {webPortalToken.AccountId}, AccountType: {webPortalToken.AccountType}");
|
||||
}
|
||||
|
||||
if (webPortalToken == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidWebPortalToken));
|
||||
|
||||
var errorCode = await _webPortalTokenAuth.mysqlAuth(webPortalToken);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidWebPortalToken));
|
||||
|
||||
(Result result, UserByAccountIdResult? gameAccount) =
|
||||
await EntitySearchHelper.findUserByAccountId(_dynamoDbClient, webPortalToken.AccountId, "");
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(result.ErrorCode));
|
||||
|
||||
if (gameAccount == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
||||
|
||||
AccountEntity accountEntity = await _accountService.getOrInsertAccount(
|
||||
gameAccount.UserGuid, gameAccount.Nickname, webPortalToken.AccountId);
|
||||
|
||||
var accessToken = _tokenService.CreateToken(gameAccount.UserGuid, gameAccount.Nickname);
|
||||
var refreshToken = _tokenService.GenerateRefreshToken();
|
||||
DateTime refreshTokenExpryTime = DateTime.Now.AddDays(7);
|
||||
|
||||
if (await _accountService.saveRefreshToken(accountEntity.UserGuid, refreshToken, refreshTokenExpryTime) == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
{
|
||||
var expiry = TimeSpan.FromMinutes(_jwtSettings.TokenValidityInMinutes);
|
||||
await _redis.GetDatabase().StringSetAsync($"ugq_login:{accountEntity.UserGuid}", "online", expiry);
|
||||
}
|
||||
|
||||
List<ILogInvoker> business_logs = [
|
||||
new UgqApiLoginBusinessLog(UgqApiLoginType.NormalApi),
|
||||
];
|
||||
var log_action = new LogActionEx(LogActionType.UgqApiLogin);
|
||||
UgqApiBusinessLogger.collectLogs(log_action, accountEntity, business_logs);
|
||||
|
||||
return Results.Ok(new LoginResponse
|
||||
{
|
||||
UserGuid = accountEntity.UserGuid,
|
||||
Nickname = accountEntity.Nickname,
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// img 로그인
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("login-igm")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(IgmLoginResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> login_igm([FromBody] LoginRequest request)
|
||||
{
|
||||
WebPortalToken? webPortalToken = null;
|
||||
|
||||
{
|
||||
Result result2 = AuthHelper.verifySsoAccountAuthJWT(request.WebPortalToken,
|
||||
_webPortalTokenAuth.WebPortalTokenSecret,
|
||||
out var account_id, out var account_type, out var access_token);
|
||||
if (result2.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidWebPortalToken));
|
||||
|
||||
webPortalToken = new WebPortalToken
|
||||
{
|
||||
AccountId = account_id,
|
||||
AccountType = account_type,
|
||||
AccessToken = access_token,
|
||||
};
|
||||
|
||||
Log.getLogger().info($"webPortalToken. igm - AccountId: {webPortalToken.AccountId}, AccountType: {webPortalToken.AccountType}");
|
||||
}
|
||||
|
||||
if (webPortalToken == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidWebPortalToken));
|
||||
|
||||
var errorCode = await _webPortalTokenAuth.mysqlAuth_igm(webPortalToken);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidWebPortalToken));
|
||||
|
||||
(Result result, UserByAccountIdResult? gameAccount) =
|
||||
await EntitySearchHelper.findUserByAccountId(_dynamoDbClient, webPortalToken.AccountId, "");
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(result.ErrorCode));
|
||||
|
||||
if (gameAccount == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
||||
|
||||
var accessToken = _tokenService.CreateToken(gameAccount.UserGuid, gameAccount.Nickname);
|
||||
|
||||
List<ILogInvoker> business_logs = [
|
||||
new UgqApiLoginBusinessLog(UgqApiLoginType.NormalApi),
|
||||
];
|
||||
var log_action = new LogActionEx(LogActionType.igmApiLogin);
|
||||
UgqApiBusinessLogger.collectLogs(log_action, gameAccount.UserGuid, gameAccount.Nickname, business_logs);
|
||||
|
||||
return Results.Ok(new IgmLoginResponse
|
||||
{
|
||||
UserGuid = gameAccount.UserGuid,
|
||||
Nickname = gameAccount.Nickname,
|
||||
AccessToken = accessToken,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 토큰 리프레쉬
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("refresh")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(RefreshTokenResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> Refresh([FromBody] RefreshTokenRequest request)
|
||||
{
|
||||
var principal = _tokenService.GetPrincipalFromExpiredToken(request.AccessToken);
|
||||
|
||||
var userGuid = principal.Claims.FirstOrDefault(c => c.Type == "Id")?.Value;
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
var accountEntity = await _accountService.getAccount(userGuid);
|
||||
if (accountEntity is null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
if (accountEntity.RefreshToken != request.RefreshToken || accountEntity.RefreshTokenExpiryTime <= DateTime.Now)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidToken));
|
||||
|
||||
var accessToken = _tokenService.CreateToken(userGuid, accountEntity.Nickname);
|
||||
var refreshToken = _tokenService.GenerateRefreshToken();
|
||||
|
||||
if (await _accountService.saveRefreshToken(accountEntity.UserGuid, refreshToken, null) == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
{
|
||||
var expiry = TimeSpan.FromMinutes(_jwtSettings.TokenValidityInMinutes);
|
||||
await _redis.GetDatabase().StringSetAsync($"ugq_login:{accountEntity.UserGuid}", "online", expiry);
|
||||
}
|
||||
|
||||
return Results.Ok(new RefreshTokenResponse
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken,
|
||||
});
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[Route("logout")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK)]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> Logout()
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
var accountEntity = await _accountService.getAccount(userGuid);
|
||||
if (accountEntity is null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
if (await _accountService.deleteRefreshToken(userGuid) == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
List<ILogInvoker> business_logs = [
|
||||
new UgqApiLogoutBusinessLog(),
|
||||
];
|
||||
var log_action = new LogActionEx(LogActionType.UgqApiLogout);
|
||||
UgqApiBusinessLogger.collectLogs(log_action, accountEntity, business_logs);
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
335
UGQApiServer/Controllers/Admin/AdminAccountController.cs
Normal file
335
UGQApiServer/Controllers/Admin/AdminAccountController.cs
Normal file
@@ -0,0 +1,335 @@
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 계정 리스트 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("all-accounts")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> 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()
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 계정 정보 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("account")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> 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,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 계정의 UGQ 슬롯 수 변경
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("modify-account-slot")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> 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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 계정의 UGQ 등급 변경 예약
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("reserve-account-grade")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> 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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 계정의 UGQ 등급 변경 예약 수정
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("modify-reserve-account-grade")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> 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());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 계정의 UGQ 등급 변경 예약 취소
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("delete-reserve-account-grade")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> deleteReserveAccountGrade([FromQuery] string reserveId)
|
||||
{
|
||||
var errorCode = await _accountService.deleteReserveAccountGrade(reserveId);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 계정의 UGQ 등급 변경 예약 조회
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("all-reserve-account-grade")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> 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()
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 계정의 UGQ 등급 변경 예약 조회
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("get-reserve-account-grade")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> 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()
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 계정의 Creator Point 변경
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("modify-creator-point")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> 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());
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 메타버스 계정 정보 얻기
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// email을 입력하면 email로 accountId를 검색, email을 입력 안 하면 accountId 입력 값을 사용
|
||||
/// </remarks>
|
||||
[HttpGet]
|
||||
[Route("metaverse-account")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> 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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
200
UGQApiServer/Controllers/Admin/AdminController.cs
Normal file
200
UGQApiServer/Controllers/Admin/AdminController.cs
Normal file
@@ -0,0 +1,200 @@
|
||||
using System.Globalization;
|
||||
using System.Security.Claims;
|
||||
using Amazon.Auth.AccessControlPolicy;
|
||||
using Asp.Versioning;
|
||||
using JwtRoleAuthentication.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ServerCommon;
|
||||
using ServerCommon.BusinessLogDomain;
|
||||
using ServerCommon.UGQ.Models;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
using UGQApiServer.Controllers.Common;
|
||||
using UGQApiServer.Converter;
|
||||
using UGQApiServer.Models;
|
||||
using UGQDataAccess.Repository.Models;
|
||||
using UGQDataAccess.Service;
|
||||
using UGQDatabase.Models;
|
||||
using UGQDataAccess.Logs;
|
||||
using ServerBase;
|
||||
|
||||
|
||||
namespace UGQApiServer.Controllers.Admin;
|
||||
|
||||
|
||||
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[ApiExplorerSettings(GroupName = "admin")]
|
||||
[Route("api/v{version:apiVersion}/[controller]")]
|
||||
[UGQAdminApi]
|
||||
public class AdminController : ControllerBase
|
||||
{
|
||||
AdminService _adminService;
|
||||
TokenService _tokenService;
|
||||
|
||||
public AdminController(AdminService adminService, TokenService tokenService)
|
||||
{
|
||||
_adminService = adminService;
|
||||
_tokenService = tokenService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 어드민 계정 추가
|
||||
/// </summary>
|
||||
// [Authorize(Roles = "Admin")]
|
||||
[HttpPost]
|
||||
[Route("signup")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(AdminSignupResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> signup([FromBody] AdminSignupRequest request)
|
||||
{
|
||||
var entity = await _adminService.signup(request.Username, request.Password, UGQAccountRole.Service);
|
||||
if (entity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
return Results.Ok(new AdminSignupResponse
|
||||
{
|
||||
Username = entity.Username,
|
||||
Role = entity.Role,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 어드민 로그인
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("login")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(AdminLoginResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> login([FromBody] AdminLoginRequest request)
|
||||
{
|
||||
var entity = await _adminService.login(request.Username, request.Password);
|
||||
if(entity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
var accessToken = _tokenService.CreateAdminToken(entity.Username, entity.Role);
|
||||
var refreshToken = _tokenService.GenerateRefreshToken();
|
||||
DateTime refreshTokenExpryTime = DateTime.Now.AddDays(7);
|
||||
|
||||
if (await _adminService.saveRefreshToken(entity.Id, refreshToken, refreshTokenExpryTime) == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
List<ILogInvoker> business_logs = [
|
||||
new UgqApiAdminLoginBusinessLog(entity.Username),
|
||||
];
|
||||
var log_action = new LogActionEx(LogActionType.UgqApiAdminLogin);
|
||||
UgqApiBusinessLogger.collectLogs(log_action, entity, business_logs);
|
||||
|
||||
return Results.Ok(new AdminLoginResponse
|
||||
{
|
||||
Username = entity.Username,
|
||||
Role = entity.Role,
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 어드민 패스워드 변경
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[Route("change-password")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(AdminChangePaswordResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> changePassowrd([FromBody] AdminChangePaswordRequest request)
|
||||
{
|
||||
var username = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
|
||||
if (username == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
var entity = await _adminService.changePassword(username, request.NewPassword);
|
||||
if (entity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
return Results.Ok(new AdminChangePaswordResponse
|
||||
{
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 어드민 패스워드 변경
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("update-password")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(AdminUpdatePaswordResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> updatePassowrd([FromBody] AdminUpdatePaswordRequest request)
|
||||
{
|
||||
var entity = await _adminService.changePassword(request.Username, request.NewPassword);
|
||||
if (entity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
return Results.Ok(new AdminUpdatePaswordResponse
|
||||
{
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 토큰 리프레쉬
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("refresh")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(RefreshTokenResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> Refresh([FromBody] RefreshTokenRequest request)
|
||||
{
|
||||
var principal = _tokenService.GetPrincipalFromExpiredToken(request.AccessToken);
|
||||
|
||||
var username = principal.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
|
||||
if (username == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
var entity = await _adminService.get(username);
|
||||
if (entity is null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
if (entity.RefreshToken != request.RefreshToken || entity.RefreshTokenExpiryTime <= DateTime.Now)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidToken));
|
||||
|
||||
var accessToken = _tokenService.CreateAdminToken(entity.Username, entity.Role);
|
||||
var refreshToken = _tokenService.GenerateRefreshToken();
|
||||
|
||||
if (await _adminService.saveRefreshToken(entity.Id, refreshToken, null) == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
return Results.Ok(new RefreshTokenResponse
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[Route("logout")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK)]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> Logout()
|
||||
{
|
||||
var username = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
|
||||
if (username == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
if (await _adminService.deleteRefreshToken(username) == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
164
UGQApiServer/Controllers/Admin/AdminMetaDataController.cs
Normal file
164
UGQApiServer/Controllers/Admin/AdminMetaDataController.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System.Globalization;
|
||||
using Asp.Versioning;
|
||||
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 UGQDataAccess.Repository.Models;
|
||||
using UGQDataAccess.Service;
|
||||
using UGQApiServer.UGQData;
|
||||
using ServerBase;
|
||||
|
||||
namespace UGQApiServer.Controllers.Admin;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[ApiExplorerSettings(GroupName = "admin")]
|
||||
[Route("api/v{version:apiVersion}/Admin/MetaData")]
|
||||
[ControllerName("MetaData")]
|
||||
[UGQAdminApi]
|
||||
[Authorize(Roles = "Admin,Service")]
|
||||
public class AdminMetaDataController : ControllerBase
|
||||
{
|
||||
AccountService _accountService;
|
||||
QuestEditorService _questEditorService;
|
||||
|
||||
MetaDataApi _metaDataApi;
|
||||
QuestEditorApi _questEditorApi;
|
||||
|
||||
DynamoDbClient _dynamoDbClient;
|
||||
|
||||
public AdminMetaDataController(
|
||||
AccountService accountService,
|
||||
QuestEditorService questEditorService, MetaDataApi metaDataApi, QuestEditorApi questEditorApi, DynamoDbClient dynamoDbClient)
|
||||
{
|
||||
_accountService = accountService;
|
||||
_questEditorService = questEditorService;
|
||||
_metaDataApi = metaDataApi;
|
||||
_questEditorApi = questEditorApi;
|
||||
_dynamoDbClient = dynamoDbClient;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// 메타데이터 어드민
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// <summary>
|
||||
/// 사용가능한 시작 npc 가져오기
|
||||
/// </summary>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// <param name="pageNumber">페이지</param>
|
||||
/// <param name="pageSize">pageSize (최대 100)</param>
|
||||
[HttpGet]
|
||||
[Route("assignable-npcs")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQNpcMetaDataList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getAssignableNpcs([FromQuery] string userGuid, [FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
return await _metaDataApi.getAssignableNpcs(userGuid, pageNumber, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사용가능한 task action 가져오기
|
||||
/// </summary>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// <param name="pageNumber">페이지</param>
|
||||
/// <param name="pageSize">pageSize (최대 100)</param>
|
||||
[HttpGet]
|
||||
[Route("available-task-actions")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(TaskActionList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getAvailableTaskActions([FromQuery] string userGuid, [FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
return await _metaDataApi.getAvailableTaskActions(pageNumber, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 액션의 입력가능한 값 리스트 얻어오기
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Task Action이 의상장착인 경우에 사용
|
||||
/// </remarks>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// <param name="taskActionId">TaskAction의 아이디</param>
|
||||
/// <param name="pageNumber">페이지</param>
|
||||
/// <param name="pageSize">pageSize (최대 100)</param>
|
||||
[HttpGet]
|
||||
[Route("task-action-values")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(TaskActionValueList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getActionValues([FromQuery] string userGuid, [FromQuery] int taskActionId, [FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
return await _metaDataApi.getActionValues(userGuid, taskActionId, pageNumber, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사용가능한 dialog 유형 가져오기
|
||||
/// </summary>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// <param name="pageNumber">페이지</param>
|
||||
/// <param name="pageSize">pageSize (최대 100)</param>
|
||||
[HttpGet]
|
||||
[Route("dialog-types")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(DialogTypeList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getDialogTypes([FromQuery] string userGuid, [FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
return await _metaDataApi.getDialogTypes(pageNumber, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// dialog 유형에 따라 사용가능한 dialog 액션 가져오기
|
||||
/// </summary>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// <param name="dialogType">dialog 유형</param>
|
||||
/// <param name="pageNumber">페이지</param>
|
||||
/// <param name="pageSize">pageSize (최대 100)</param>
|
||||
[HttpGet]
|
||||
[Route("dialog-actions")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(DialogActionList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getDialogActions([FromQuery] string userGuid, [FromQuery] int dialogType, [FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
return await _metaDataApi.getDialogActions(dialogType, pageNumber, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// dialog 액션에 따라 입력 가능한 값 가져오기
|
||||
/// </summary>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// <param name="dialogType">다이얼로그 타입</param>
|
||||
/// <param name="dialogActionId">조건 종류</param>
|
||||
/// <param name="pageNumber">페이지</param>
|
||||
/// <param name="pageSize">pageSize (최대 100)</param>
|
||||
[HttpGet]
|
||||
[Route("dialog-action-values")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(DialogActionValueList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getDialogConditionValues([FromQuery] string userGuid, [FromQuery] int dialogType, [FromQuery] int dialogActionId, [FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
return await _metaDataApi.getDialogConditionValues(dialogType, dialogActionId, pageNumber, pageSize);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("preset-images")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(DialogActionValueList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getPresetImages([FromQuery] string userGuid, [FromQuery] PresetKind kind, [FromQuery] PresetCategory category)
|
||||
{
|
||||
return await _metaDataApi.getPresetImages(kind, category);
|
||||
}
|
||||
|
||||
}
|
||||
341
UGQApiServer/Controllers/Admin/AdminQuestEditorController.cs
Normal file
341
UGQApiServer/Controllers/Admin/AdminQuestEditorController.cs
Normal file
@@ -0,0 +1,341 @@
|
||||
using System.Globalization;
|
||||
using System.Security.Claims;
|
||||
using Asp.Versioning;
|
||||
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 UGQDataAccess.Repository.Models;
|
||||
using UGQDataAccess.Service;
|
||||
using UGQDatabase.Models;
|
||||
using ServerBase;
|
||||
|
||||
|
||||
namespace UGQApiServer.Controllers.Admin;
|
||||
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[ApiExplorerSettings(GroupName = "admin")]
|
||||
[Route("api/v{version:apiVersion}/Admin/QuestEditor")]
|
||||
[ControllerName("QuestEditor")]
|
||||
[UGQAdminApi]
|
||||
[Authorize(Roles = "Admin,Service")]
|
||||
public class AdminQuestEditorController : ControllerBase
|
||||
{
|
||||
const int MAX_PAGE_SIZE = 100;
|
||||
|
||||
AccountService _accountService;
|
||||
QuestEditorService _questEditorService;
|
||||
|
||||
MetaDataApi _metaDataApi;
|
||||
QuestEditorApi _questEditorApi;
|
||||
|
||||
DynamoDbClient _dynamoDbClient;
|
||||
|
||||
public AdminQuestEditorController(
|
||||
AccountService accountService,
|
||||
QuestEditorService questEditorService, MetaDataApi metaDataApi, QuestEditorApi questEditorApi, DynamoDbClient dynamoDbClient)
|
||||
{
|
||||
_accountService = accountService;
|
||||
_questEditorService = questEditorService;
|
||||
_metaDataApi = metaDataApi;
|
||||
_questEditorApi = questEditorApi;
|
||||
_dynamoDbClient = dynamoDbClient;
|
||||
}
|
||||
|
||||
string? adminUsernameFromToken()
|
||||
{
|
||||
return User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 생성
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 빈 슬롯에 퀘스트 추가하는 경우에 호출
|
||||
/// </remarks>
|
||||
[HttpPost]
|
||||
[Route("quest")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> addQuest([FromQuery] string userGuid, [FromBody] UGQSaveQuestModel saveQuestModel)
|
||||
{
|
||||
string? adminUsername = adminUsernameFromToken();
|
||||
if(adminUsername == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.addQuest(userGuid, saveQuestModel, adminUsername);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 내용 가져오기
|
||||
/// </summary>
|
||||
/// <param name="questContentId">UGQSummary의 QuestContentId 값으로 api 호출</param>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
[HttpGet]
|
||||
[Route("quest/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getQuest(string questContentId, [FromQuery] string userGuid)
|
||||
{
|
||||
return await _questEditorApi.getQuest(userGuid, questContentId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 편집 가능한 경우 퀘스트 내용 가져오기
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// GET api와는 다르게 편집 불가능한 state인 경우에는 실패 리턴
|
||||
/// </remarks>
|
||||
/// <param name="questContentId">UGQSummary의 QuestContentId 값으로 api 호출</param>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
[HttpPost]
|
||||
[Route("quest/{questContentId}/edit")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> editQuest(string questContentId, [FromQuery] string userGuid)
|
||||
{
|
||||
return await _questEditorApi.editQuest(userGuid, questContentId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 내용 업데이트
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Task의 DialogId는 업데이트하지 않음.
|
||||
/// 편집 불가능 state인 경우 에러 리턴
|
||||
/// </remarks>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// <param name="saveQuestModel">웹에서 입력한 값</param>
|
||||
[HttpPut]
|
||||
[Route("quest/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> saveQuest(string questContentId, [FromQuery] string userGuid, [FromBody] UGQSaveQuestModel saveQuestModel)
|
||||
{
|
||||
return await _questEditorApi.saveQuest(userGuid, questContentId, saveQuestModel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 삭제
|
||||
/// </summary>
|
||||
/// <param name="questContentId">GET또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
[HttpDelete]
|
||||
[Route("quest/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK)]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> deleteQuest(string questContentId, [FromQuery] string userGuid)
|
||||
{
|
||||
return await _questEditorApi.deleteQuest(userGuid, questContentId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 이미지 업로드
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 세부사항은 정리 예정
|
||||
/// </remarks>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// <param name="titleImageId">기본 이미지 인덱스</param>
|
||||
/// <param name="bannerImageId">기본 이미지 인덱스</param>
|
||||
/// <param name="title">업로드 파일</param>
|
||||
/// <param name="banner">업로드 파일</param>
|
||||
[HttpPost]
|
||||
[Route("quest-image/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> uploadQuestImage(string questContentId, [FromQuery] string userGuid, [FromQuery] int? titleImageId, [FromQuery] int? bannerImageId, IFormFile? title, IFormFile? banner)
|
||||
{
|
||||
return await _questEditorApi.uploadQuestImage(userGuid, questContentId, titleImageId ?? 0, bannerImageId ?? 0, title, banner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 타이틀 이미지 업로드
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 세부사항은 정리 예정
|
||||
/// </remarks>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="titleImageId">기본 이미지 인덱스</param>
|
||||
/// <param name="title">업로드 파일</param>
|
||||
[HttpPost]
|
||||
[Route("quest-title-image/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> uploadQuestTitleImage(string questContentId, [FromQuery] string userGuid, [FromQuery] int? titleImageId, IFormFile? title)
|
||||
{
|
||||
return await _questEditorApi.uploadQuestImage(userGuid, questContentId, titleImageId ?? 0, 0, title, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 배너 이미지 업로드
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 세부사항은 정리 예정
|
||||
/// </remarks>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="bannerImageId">기본 이미지 인덱스</param>
|
||||
/// <param name="banner">업로드 파일</param>
|
||||
[HttpPost]
|
||||
[Route("quest-banner-image/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> uploadQuestBannerImage(string questContentId, [FromQuery] string userGuid, [FromQuery] int? bannerImageId, IFormFile? banner)
|
||||
{
|
||||
return await _questEditorApi.uploadQuestImage(userGuid, questContentId, 0, bannerImageId ?? 0, null, banner);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 다이얼로그 가져오기
|
||||
/// </summary>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="dialogId">UGQContent.Tasks에 있는 DialogId 사용</param>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
[HttpGet]
|
||||
[Route("quest-dialog/{questContentId}/{dialogId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQDialog))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getQuestDialog(string questContentId, string dialogId, [FromQuery] string userGuid)
|
||||
{
|
||||
return await _questEditorApi.getQuestDialog(userGuid, questContentId, dialogId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 다이얼로그 새로 생성
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Dialog 를 추가하고, UGQContent.Tasks의 DialogId를 저장
|
||||
/// </remarks>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// <param name="taskIndex">몇번째 Task에 추가되는 다이얼로그인지. 0부터 시작</param>
|
||||
/// <param name="questDialog">questDialog</param>
|
||||
[HttpPost]
|
||||
[Route("quest-dialog/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQAddQuestDialogResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> addQuestDialog(string questContentId, [FromQuery] string userGuid, [FromQuery] int taskIndex, [FromBody] UGQSaveDialogModel questDialog)
|
||||
{
|
||||
return await _questEditorApi.addQuestDialog(userGuid, questContentId, taskIndex, questDialog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 다이얼로그 업데이트
|
||||
/// </summary>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="dialogId">UGQContent.Tasks에 있는 DialogId 사용</param>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// /// <param name="questDialog">questDialog</param>
|
||||
[HttpPut]
|
||||
[Route("quest-dialog/{questContentId}/{dialogId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQDialog))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> saveQuestDialog(string questContentId, string dialogId, [FromQuery] string userGuid, [FromBody] UGQSaveDialogModel questDialog)
|
||||
{
|
||||
return await _questEditorApi.saveQuestDialog(userGuid, questContentId, dialogId, questDialog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 state 변경
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Test 로 변경 실패 시 ValidationErrorResponse로 응답
|
||||
/// </remarks>
|
||||
/// <param name="questContentId">GET또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="userGuid">유저 Guid</param>
|
||||
/// <param name="state">UGQState</param>
|
||||
[HttpPatch]
|
||||
[Route("quest-state/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ValidationErrorResponse))]
|
||||
public async Task<IResult> changeQuestState(string questContentId, [FromQuery] string userGuid, [FromQuery] QuestContentState state)
|
||||
{
|
||||
string? adminUsername = adminUsernameFromToken();
|
||||
if (adminUsername == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.changeQuestState(userGuid, questContentId, state, adminUsername);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("creator-point-history")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQCreatorPointHistoryResult))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getCreatorPointHistory([FromQuery] string userGuid, [FromQuery] int pageNumber, [FromQuery] int pageSize, [FromQuery] CreatorPointHistoryKind kind, DateTimeOffset startDate, DateTimeOffset endDate)
|
||||
{
|
||||
return await _questEditorApi.getCreatorPointHistory(userGuid, pageNumber, pageSize, kind, startDate, endDate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 모든 퀘스트 보기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("all-quests")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> getAllQuests([FromQuery] QuestContentState state, [FromQuery] int pageNumber, [FromQuery] int pageSize, [FromQuery] string? searchText)
|
||||
{
|
||||
pageSize = Math.Clamp(pageSize, 1, MAX_PAGE_SIZE);
|
||||
|
||||
var result = await _questEditorService.getAllQuests(pageNumber, pageSize, state, searchText);
|
||||
|
||||
long allCount = await _questEditorService.getAllCount();
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(new UQGAllQuestResponse
|
||||
{
|
||||
TotalCount = allCount,
|
||||
PageNumber = result.PageNumber,
|
||||
PageSize = result.PageSize,
|
||||
TotalPages = result.TotalPages,
|
||||
Summaries = result.Items.Select(x => x.ToDTO(langEnum)).ToList()
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 계정의 모든 퀘스트 보기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("account-quest")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(List<UGQSummary>))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getQuests([FromQuery] int pageNumber, [FromQuery] int pageSize, [FromQuery] string userGuid, [FromQuery] QuestContentState state, [FromQuery] string? searchText)
|
||||
{
|
||||
pageSize = Math.Clamp(pageSize, 1, MAX_PAGE_SIZE);
|
||||
|
||||
List<QuestContentEntity> all = await _questEditorService.getAll(userGuid);
|
||||
|
||||
var result = await _questEditorService.getSummaries(pageNumber, pageSize, userGuid, state, searchText);
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(new UGQSummaryResponse
|
||||
{
|
||||
TotalCount = all.Count,
|
||||
PageNumber = result.PageNumber,
|
||||
PageSize = result.PageSize,
|
||||
TotalPages = result.TotalPages,
|
||||
Summaries = result.Items.Select(x => x.ToDTO(langEnum)).ToList(),
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
320
UGQApiServer/Controllers/Common/MetaDataApi.cs
Normal file
320
UGQApiServer/Controllers/Common/MetaDataApi.cs
Normal file
@@ -0,0 +1,320 @@
|
||||
using System.Globalization;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using MetaAssets;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
using UGQApiServer.Converter;
|
||||
using UGQApiServer.Models;
|
||||
using UGQApiServer.UGQData;
|
||||
using UGQDataAccess;
|
||||
|
||||
|
||||
|
||||
namespace UGQApiServer.Controllers.Common;
|
||||
|
||||
public class MetaDataApi
|
||||
{
|
||||
UGQMetaData _ugqMetaData;
|
||||
DynamoDbClient _dynamoDbClient;
|
||||
|
||||
public MetaDataApi(UGQMetaData ugqMetaData, DynamoDbClient dynamoDbClient)
|
||||
{
|
||||
_ugqMetaData = ugqMetaData;
|
||||
_dynamoDbClient = dynamoDbClient;
|
||||
}
|
||||
|
||||
public async Task<IResult> getAssignableNpcs(string userGuid, int pageNumber, int pageSize)
|
||||
{
|
||||
if (pageNumber < 1)
|
||||
pageNumber = 1;
|
||||
|
||||
pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
|
||||
(Result result, List<UgcNpcAttrib>? ugcNpcs) =
|
||||
await EntitySearchHelper.findUgcNpcAttribAllByUserGuid(_dynamoDbClient, userGuid, "");
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(result.ErrorCode));
|
||||
|
||||
var UserNpcPos = MetaHelper.GameConfigMeta.UGQNormalBeaconQuestMarkerPos;
|
||||
|
||||
List<UGQNpcMetaData> value = new List<UGQNpcMetaData>();
|
||||
if (ugcNpcs != null)
|
||||
{
|
||||
value.AddRange(ugcNpcs.Select(x => new UGQNpcMetaData
|
||||
{
|
||||
UgcNpcGuid = x.UgcNpcMetaGuid,
|
||||
NpcName = x.Nickname,
|
||||
NpcTitle = x.Title,
|
||||
NpcGender = convertGenderTypeFromItem((int)x.BodyItemMetaId),
|
||||
Location = new Locaion() { x = (int)UserNpcPos.X, y = (int)UserNpcPos.Y },
|
||||
}));
|
||||
}
|
||||
|
||||
value.AddRange(_ugqMetaData.getAssignableNpcs().Select(x => new UGQNpcMetaData
|
||||
{
|
||||
NpcId = x.npc_id,
|
||||
NpcName = x.name,
|
||||
NpcTitle = x.NpcTitle,
|
||||
NpcGender = x.Gender,
|
||||
Location = new Locaion() { x = x.UGQmap_x, y = x.UGQmap_y },
|
||||
}));
|
||||
|
||||
var paging = value.Skip((pageNumber - 1) * pageSize).Take(pageSize + 1).ToList();
|
||||
|
||||
bool hasNextPage = false;
|
||||
if (paging.Count > pageSize)
|
||||
hasNextPage = true;
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(new UGQNpcMetaDataList
|
||||
{
|
||||
PageNumber = pageNumber,
|
||||
PageSize = pageSize,
|
||||
HasNextPage = hasNextPage,
|
||||
Npcs = paging.Take(pageSize).Select(x => x.ToNpcLocale(langEnum)).ToList(),
|
||||
});
|
||||
}
|
||||
|
||||
private EGenderType convertGenderTypeFromItem(int itemId)
|
||||
{
|
||||
MetaData.Instance._ItemTable.TryGetValue(itemId, out var item);
|
||||
if(item == null)
|
||||
return EGenderType.ALL;
|
||||
|
||||
return item.Gender;
|
||||
}
|
||||
|
||||
public async Task<IResult> getAvailableTaskActions(int pageNumber, int pageSize)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
|
||||
var paging = _ugqMetaData.TackActions.Skip((pageNumber - 1) * pageSize).Take(pageSize + 1).ToList();
|
||||
|
||||
bool hasNextPage = false;
|
||||
if (paging.Count > pageSize)
|
||||
hasNextPage = true;
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(new TaskActionList
|
||||
{
|
||||
PageNumber = pageNumber,
|
||||
PageSize = pageSize,
|
||||
HasNextPage = hasNextPage,
|
||||
TaskActions = paging.Take(pageSize).Select(x => x.Value.ToTaskAction(langEnum)).ToList(),
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IResult> getActionValues(string userGuid, int taskActionId, int pageNumber, int pageSize)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
if (pageNumber < 1)
|
||||
pageNumber = 1;
|
||||
|
||||
pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
|
||||
List<UGQActionValue> taskActions = [];
|
||||
{
|
||||
_ugqMetaData.TackActions.TryGetValue(taskActionId, out var value);
|
||||
if (value == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidTaskAction));
|
||||
|
||||
if (value.Disabled == true)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqTaskActionDisabled));
|
||||
|
||||
taskActions = value.TaskActions;
|
||||
}
|
||||
|
||||
// taskActionId<49><64> talk<6C><6B> <20><><EFBFBD><EFBFBD> dynamodb<64><62><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> npc <20><><EFBFBD><EFBFBD><EFBFBD>;<EFBFBD> <20>Ѵ<EFBFBD>
|
||||
if (taskActionId == UGQMetaData.TALK_ACTION_ID)
|
||||
{
|
||||
(Result result, List<UgcNpcAttrib>? ugcNpcs) =
|
||||
await EntitySearchHelper.findUgcNpcAttribAllByUserGuid(_dynamoDbClient, userGuid, "");
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(result.ErrorCode));
|
||||
|
||||
if (ugcNpcs != null)
|
||||
{
|
||||
List<UGQActionValue> npcValues = new List<UGQActionValue>();
|
||||
npcValues.AddRange(ugcNpcs.Select(x => new UGQActionValue
|
||||
{
|
||||
UgcValueGuid = x.UgcNpcMetaGuid,
|
||||
ValueName = x.Nickname,
|
||||
}));
|
||||
|
||||
npcValues.AddRange(taskActions);
|
||||
taskActions = npcValues;
|
||||
}
|
||||
}
|
||||
|
||||
var paging = taskActions.Skip((pageNumber - 1) * pageSize).Take(pageSize + 1).ToList();
|
||||
|
||||
bool hasNextPage = false;
|
||||
if (paging.Count > pageSize)
|
||||
hasNextPage = true;
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(new TaskActionValueList
|
||||
{
|
||||
PageNumber = pageNumber,
|
||||
PageSize = pageSize,
|
||||
HasNextPage = hasNextPage,
|
||||
Values = paging.Take(pageSize).Select(x => x.ToTaskActionValue(langEnum)).ToList(),
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IResult> getDialogTypes(int pageNumber, int pageSize)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
|
||||
var paging = _ugqMetaData.DialogActions.Skip((pageNumber - 1) * pageSize).Take(pageSize + 1).ToList();
|
||||
|
||||
bool hasNextPage = false;
|
||||
if (paging.Count > pageSize)
|
||||
hasNextPage = true;
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
|
||||
return Results.Ok(new DialogTypeList
|
||||
{
|
||||
PageNumber = pageNumber,
|
||||
PageSize = pageSize,
|
||||
HasNextPage = hasNextPage,
|
||||
DialogTypes = _ugqMetaData.DialogActions.Select(x => x.Value.ToDialogType(langEnum)).ToList(),
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IResult> getDialogActions(int dialogType, int pageNumber, int pageSize)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
if (pageNumber < 1)
|
||||
pageNumber = 1;
|
||||
|
||||
pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
|
||||
_ugqMetaData.DialogActions.TryGetValue(dialogType, out var value);
|
||||
if (value == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidDialogType));
|
||||
|
||||
var paging = value.DialogConditions.Skip((pageNumber - 1) * pageSize).Take(pageSize + 1).ToList();
|
||||
|
||||
bool hasNextPage = false;
|
||||
if (paging.Count > pageSize)
|
||||
hasNextPage = true;
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(new DialogActionList
|
||||
{
|
||||
PageNumber = pageNumber,
|
||||
PageSize = pageSize,
|
||||
HasNextPage = hasNextPage,
|
||||
DialogActions = paging.Take(pageSize).Select(x => x.ToDialogAction(langEnum)).ToList(),
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IResult> getDialogConditionValues(int dialogType, int dialogActionId, int pageNumber, int pageSize)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
if (pageNumber < 1)
|
||||
pageNumber = 1;
|
||||
|
||||
pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
|
||||
_ugqMetaData.DialogActions.TryGetValue(dialogType, out var value);
|
||||
if (value == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidDialogType));
|
||||
|
||||
var dialogAction = value.DialogConditions.Find(x => x.ConditionId == dialogActionId);
|
||||
if (dialogAction == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqInvalidDialogCondition));
|
||||
|
||||
var paging = dialogAction.DialogConditionValues.Skip((pageNumber - 1) * pageSize).Take(pageSize + 1).ToList();
|
||||
|
||||
bool hasNextPage = false;
|
||||
if (paging.Count > pageSize)
|
||||
hasNextPage = true;
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(new DialogActionValueList
|
||||
{
|
||||
PageNumber = pageNumber,
|
||||
PageSize = pageSize,
|
||||
HasNextPage = hasNextPage,
|
||||
Values = paging.Take(pageSize).Select(x => x.ToDialogActionValue(langEnum)).ToList(),
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IResult> getNpcActionValues(int pageNumber, int pageSize, EGenderType gender)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
if (pageNumber < 1)
|
||||
pageNumber = 1;
|
||||
|
||||
pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
|
||||
var npcActionMetaDatas = MetaData.Instance.UGQBeaconActionDataListbyId;
|
||||
if(gender != EGenderType.ALL)
|
||||
{
|
||||
npcActionMetaDatas = npcActionMetaDatas.Where(x => x.Value.Gender == gender || x.Value.Gender == EGenderType.ALL).ToDictionary(x => x.Key, x => x.Value);
|
||||
}
|
||||
var paging = npcActionMetaDatas.Skip((pageNumber - 1) * pageSize).Take(pageSize + 1).ToList();
|
||||
|
||||
bool hasNextPage = false;
|
||||
if (paging.Count > pageSize)
|
||||
hasNextPage = true;
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(new NpcActionValueList
|
||||
{
|
||||
PageNumber = pageNumber,
|
||||
PageSize = pageSize,
|
||||
HasNextPage = hasNextPage,
|
||||
Values = paging.Take(pageSize).Select(x => x.Value.ToNpcActionValue(langEnum)).ToList(),
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IResult> getMapDataValues(int pageNumber, int pageSize)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
if (pageNumber < 1)
|
||||
pageNumber = 1;
|
||||
|
||||
pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
|
||||
var zoneMetaDatas = MetaData.Instance.ZoneMetaDataListbyId;
|
||||
var paging = zoneMetaDatas.Skip((pageNumber - 1) * pageSize).Take(pageSize + 1).ToList();
|
||||
|
||||
bool hasNextPage = false;
|
||||
if (paging.Count > pageSize)
|
||||
hasNextPage = true;
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(new MapDataValueList
|
||||
{
|
||||
PageNumber = pageNumber,
|
||||
PageSize = pageSize,
|
||||
HasNextPage = hasNextPage,
|
||||
Values = paging.Take(pageSize).Select(x => x.Value.ToMapDataValue(langEnum)).ToList(),
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IResult> getPresetImages(PresetKind kind, PresetCategory category)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
if (_ugqMetaData.PresetImages.TryGetValue((kind, category), out var presetImages) == false)
|
||||
return Results.BadRequest();
|
||||
|
||||
return Results.Ok(presetImages.Select(x => x.ToDTO()).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
475
UGQApiServer/Controllers/Common/QuestEditorApi.cs
Normal file
475
UGQApiServer/Controllers/Common/QuestEditorApi.cs
Normal file
@@ -0,0 +1,475 @@
|
||||
using System.Globalization;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
using Swashbuckle.AspNetCore.Filters;
|
||||
using UGQApiServer.Models;
|
||||
using UGQDatabase.Models;
|
||||
using UGQDataAccess.Service;
|
||||
using UGQApiServer.Converter;
|
||||
using UGQApiServer.Storage;
|
||||
using UGQDataAccess.Repository.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using UGQDataAccess;
|
||||
using UGQApiServer.Validation;
|
||||
using UGQApiServer.UGQData;
|
||||
using UGQApiServer.Auth;
|
||||
using JwtRoleAuthentication.Services;
|
||||
using System.Security.Claims;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ServerCommon;
|
||||
using ServerBase;
|
||||
using ServerCommon.UGQ;
|
||||
using ServerCommon.UGQ.Models;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace UGQApiServer.Controllers.Common;
|
||||
|
||||
public class QuestEditorApi
|
||||
{
|
||||
QuestEditorService _questEditorService;
|
||||
UGQMetaData _ugqMetaData;
|
||||
DynamoDbClient _dynamoDbClient;
|
||||
IStorageService _storageService;
|
||||
|
||||
public QuestEditorApi(QuestEditorService questEditorService,
|
||||
UGQMetaData ugqMetaData, DynamoDbClient dynamoDbClient,
|
||||
IStorageService storageService)
|
||||
{
|
||||
_questEditorService = questEditorService;
|
||||
_ugqMetaData = ugqMetaData;
|
||||
_dynamoDbClient = dynamoDbClient;
|
||||
_storageService = storageService;
|
||||
}
|
||||
|
||||
public async Task<IResult> addQuest(string userGuid, UGQSaveQuestModel saveQuestModel, string adminUsername = "")
|
||||
{
|
||||
if (string.IsNullOrEmpty(saveQuestModel.UgcBeaconGuid) == false)
|
||||
{
|
||||
(Result result, List<UgcNpcAttrib>? ugcNpcs) =
|
||||
await EntitySearchHelper.findUgcNpcAttribAllByUserGuid(_dynamoDbClient, userGuid, "");
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError));
|
||||
|
||||
if (ugcNpcs == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError));
|
||||
|
||||
UgcNpcAttrib? found = ugcNpcs.Where(x => x.UgcNpcMetaGuid == saveQuestModel.UgcBeaconGuid).FirstOrDefault();
|
||||
if (found == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNotOwnUgcNpc));
|
||||
|
||||
saveQuestModel.UgcBeaconNickname = found.Nickname;
|
||||
}
|
||||
|
||||
foreach (var task in saveQuestModel.Tasks)
|
||||
{
|
||||
if (string.IsNullOrEmpty(task.UgcActionValueGuid) == false)
|
||||
{
|
||||
(Result result, List<UgcNpcAttrib>? ugcNpcs) =
|
||||
await EntitySearchHelper.findUgcNpcAttribAllByUserGuid(_dynamoDbClient, userGuid, "");
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError));
|
||||
|
||||
if (ugcNpcs == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError));
|
||||
|
||||
UgcNpcAttrib? found = ugcNpcs.Where(x => x.UgcNpcMetaGuid == task.UgcActionValueGuid).FirstOrDefault();
|
||||
if (found == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNotOwnUgcNpc));
|
||||
|
||||
task.UgcActionValueName = found.Nickname;
|
||||
}
|
||||
}
|
||||
|
||||
(ServerErrorCode errorCode, var entity) = await _questEditorService.addQuest(userGuid, saveQuestModel, adminUsername);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (entity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(entity.ToDTO(langEnum));
|
||||
}
|
||||
|
||||
public async Task<IResult> getQuest(string userGuid, string questContentId)
|
||||
{
|
||||
(ServerErrorCode errorCode, var entity) = await _questEditorService.getQuest(userGuid, questContentId);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (entity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(entity.ToDTO(langEnum));
|
||||
}
|
||||
|
||||
public async Task<IResult> editQuest(string userGuid, string questContentId)
|
||||
{
|
||||
(ServerErrorCode errorCode, var entity) = await _questEditorService.editQuest(userGuid, questContentId);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (entity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(entity.ToDTO(langEnum));
|
||||
}
|
||||
|
||||
public async Task<IResult> saveQuest(string userGuid, string questContentId, UGQSaveQuestModel saveQuestModel)
|
||||
{
|
||||
if (string.IsNullOrEmpty(saveQuestModel.UgcBeaconGuid) == false)
|
||||
{
|
||||
(Result result, List<UgcNpcAttrib>? ugcNpcs) =
|
||||
await EntitySearchHelper.findUgcNpcAttribAllByUserGuid(_dynamoDbClient, userGuid, "");
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError));
|
||||
|
||||
if (ugcNpcs == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError));
|
||||
|
||||
UgcNpcAttrib? found = ugcNpcs.Where(x => x.UgcNpcMetaGuid == saveQuestModel.UgcBeaconGuid).FirstOrDefault();
|
||||
if (found == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNotOwnUgcNpc));
|
||||
|
||||
saveQuestModel.UgcBeaconNickname = found.Nickname;
|
||||
}
|
||||
|
||||
foreach (var task in saveQuestModel.Tasks)
|
||||
{
|
||||
if (string.IsNullOrEmpty(task.UgcActionValueGuid) == false)
|
||||
{
|
||||
(Result result, List<UgcNpcAttrib>? ugcNpcs) =
|
||||
await EntitySearchHelper.findUgcNpcAttribAllByUserGuid(_dynamoDbClient, userGuid, "");
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError));
|
||||
|
||||
if (ugcNpcs == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqGameDbaccessError));
|
||||
|
||||
UgcNpcAttrib? found = ugcNpcs.Where(x => x.UgcNpcMetaGuid == task.UgcActionValueGuid).FirstOrDefault();
|
||||
if (found == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNotOwnUgcNpc));
|
||||
|
||||
task.UgcActionValueName = found.Nickname;
|
||||
}
|
||||
}
|
||||
|
||||
(ServerErrorCode errorCode, var updated) = await _questEditorService.saveQuest(userGuid, questContentId, saveQuestModel);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (updated == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(updated.ToDTO(langEnum));
|
||||
}
|
||||
|
||||
public async Task<IResult> deleteQuest(string userGuid, string questContentId)
|
||||
{
|
||||
var errorCode = await _questEditorService.deleteQuest(userGuid, questContentId);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
public async Task<IResult> uploadQuestImage(string userGuid, string questContentId,
|
||||
int titleImageId, int bannerImageId,
|
||||
IFormFile? title, IFormFile? banner)
|
||||
{
|
||||
(ServerErrorCode errorCode, var entity) = await _questEditorService.editQuest(userGuid, questContentId);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (entity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
string titleFile = entity.TitleImagePath;
|
||||
string bannerFile = entity.BannerImagePath;
|
||||
|
||||
List<string> deleteFiles = new();
|
||||
|
||||
if (titleImageId != 0)
|
||||
{
|
||||
var data = _ugqMetaData.getPresetImage(titleImageId);
|
||||
if (data != null)
|
||||
titleFile = data.FileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (title != null)
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(title.FileName);
|
||||
|
||||
(errorCode, int uploadCounter) = await _questEditorService.incUploadCounter(userGuid, questContentId);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
string filename = $"{questContentId}_title_{uploadCounter}{fileInfo.Extension}";
|
||||
await _storageService.uploadFile(filename, title);
|
||||
|
||||
if (string.IsNullOrEmpty(entity.TitleImagePath) == false &&
|
||||
entity.TitleImagePath.StartsWith("preset/") == false)
|
||||
deleteFiles.Add(entity.TitleImagePath);
|
||||
|
||||
titleFile = filename;
|
||||
}
|
||||
}
|
||||
|
||||
if (bannerImageId != 0)
|
||||
{
|
||||
var data = _ugqMetaData.getPresetImage(bannerImageId);
|
||||
if (data != null)
|
||||
bannerFile = data.FileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (banner != null)
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(banner.FileName);
|
||||
|
||||
(errorCode, int uploadCounter) = await _questEditorService.incUploadCounter(userGuid, questContentId);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
string filename = $"{questContentId}_banner_{uploadCounter}{fileInfo.Extension}";
|
||||
await _storageService.uploadFile(filename, banner);
|
||||
|
||||
if (string.IsNullOrEmpty(entity.BannerImagePath) == false &&
|
||||
entity.BannerImagePath.StartsWith("preset/") == false)
|
||||
deleteFiles.Add(entity.BannerImagePath);
|
||||
bannerFile = filename;
|
||||
}
|
||||
}
|
||||
|
||||
(errorCode, var updated) = await _questEditorService.updateQuestImages(questContentId, titleFile, bannerFile);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (updated == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
foreach (var file in deleteFiles)
|
||||
await _storageService.deleteFile(file);
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(updated.ToDTO(langEnum));
|
||||
}
|
||||
|
||||
public async Task<IResult> getQuestDialog(string userGuid, string questContentId, string dialogId)
|
||||
{
|
||||
(ServerErrorCode errorCode, var entity) = await _questEditorService.getQuestDialog(userGuid, questContentId, dialogId);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (entity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(entity.ToDTO(langEnum));
|
||||
}
|
||||
|
||||
public async Task<IResult> addQuestDialog(string userGuid, string questContentId, int taskIndex, UGQSaveDialogModel questDialog)
|
||||
{
|
||||
var sequences = questDialog.Sequences.Select(x => new DialogSequenceEntity
|
||||
{
|
||||
SequenceId = x.SequenceId,
|
||||
Actions = x.Actions.Select(x => new DialogSequenceActionEntity
|
||||
{
|
||||
Talker = x.Talker,
|
||||
Type = x.Type,
|
||||
Talk = new TextEntity
|
||||
{
|
||||
Kr = x.Talk.Kr,
|
||||
En = x.Talk.En,
|
||||
Jp = x.Talk.Jp,
|
||||
},
|
||||
NpcAction = x.NpcAction,
|
||||
Condition = x.Condition,
|
||||
ConditionValue = x.ConditionValue,
|
||||
NextSequence = x.NextSequence,
|
||||
}).ToList(),
|
||||
}).ToList();
|
||||
|
||||
(ServerErrorCode errorCode, var updated, var inserted) = await _questEditorService.addQuestDialog(userGuid, questContentId, taskIndex, sequences);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (updated == null || inserted == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(new UGQAddQuestDialogResponse
|
||||
{
|
||||
QuestContent = updated.ToDTO(langEnum),
|
||||
QuestDialog = inserted.ToDTO(langEnum),
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IResult> saveQuestDialog(string userGuid, string questContentId, string dialogId, [FromBody] UGQSaveDialogModel questDialog)
|
||||
{
|
||||
var sequences = questDialog.Sequences.Select(x => new DialogSequenceEntity
|
||||
{
|
||||
SequenceId = x.SequenceId,
|
||||
Actions = x.Actions.Select(x => new DialogSequenceActionEntity
|
||||
{
|
||||
Talker = x.Talker,
|
||||
Type = x.Type,
|
||||
Talk = new TextEntity
|
||||
{
|
||||
Kr = x.Talk.Kr,
|
||||
En = x.Talk.En,
|
||||
Jp = x.Talk.Jp,
|
||||
},
|
||||
NpcAction = x.NpcAction,
|
||||
Condition = x.Condition,
|
||||
ConditionValue = x.ConditionValue,
|
||||
NextSequence = x.NextSequence,
|
||||
}).ToList(),
|
||||
}).ToList();
|
||||
|
||||
(ServerErrorCode errorCode, var updated) = await _questEditorService.saveQuestDialog(userGuid, questContentId, dialogId, sequences);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (updated == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(updated.ToDTO(langEnum));
|
||||
}
|
||||
|
||||
List<QuestContentState> getAvailablesState(QuestContentState state)
|
||||
{
|
||||
List<QuestContentState> availables = [];
|
||||
switch (state)
|
||||
{
|
||||
case QuestContentState.Editable:
|
||||
availables = [
|
||||
QuestContentState.Test,
|
||||
QuestContentState.Standby,
|
||||
QuestContentState.Shutdown
|
||||
];
|
||||
break;
|
||||
case QuestContentState.Uncomplate:
|
||||
case QuestContentState.Test:
|
||||
availables = [
|
||||
QuestContentState.Editable
|
||||
];
|
||||
break;
|
||||
case QuestContentState.Standby:
|
||||
availables = [
|
||||
QuestContentState.Live
|
||||
];
|
||||
break;
|
||||
case QuestContentState.Live:
|
||||
availables = [
|
||||
QuestContentState.Standby
|
||||
];
|
||||
break;
|
||||
case QuestContentState.Shutdown:
|
||||
availables = [];
|
||||
break;
|
||||
}
|
||||
|
||||
return availables;
|
||||
}
|
||||
|
||||
public async Task<IResult> changeQuestState(string userGuid, string questContentId, QuestContentState state, string adminUsername = "")
|
||||
{
|
||||
(ServerErrorCode errorCode, var questContentEntity) = await _questEditorService.getQuest(userGuid, questContentId);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (questContentEntity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
// admin<69><6E> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>Ѿ<EFBFBD><D1BE><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ϵ<EFBFBD><CFB5><EFBFBD> ó<><C3B3> - Uncomplate <20><><EFBFBD>¶<EFBFBD><C2B6><EFBFBD> <20><><EFBFBD><EFBFBD> <20>Ұ<EFBFBD><D2B0><EFBFBD>
|
||||
List<QuestContentState> before = [];
|
||||
if (string.IsNullOrEmpty(adminUsername) == false)
|
||||
{
|
||||
before = [
|
||||
QuestContentState.Editable,
|
||||
QuestContentState.Test,
|
||||
QuestContentState.Standby,
|
||||
QuestContentState.Live,
|
||||
QuestContentState.Shutdown,
|
||||
];
|
||||
}
|
||||
else
|
||||
{
|
||||
before = getAvailablesState(state);
|
||||
}
|
||||
|
||||
if (before.Any(x => x == questContentEntity.State) == false)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqStateChangeError));
|
||||
|
||||
QuestContentEntity? updated = null;
|
||||
var questDialogIds = questContentEntity.Tasks
|
||||
.Where(x => string.IsNullOrEmpty(x.DialogId) == false)
|
||||
.Select(x => x.DialogId!)
|
||||
.ToList();
|
||||
var questDialogs = await _questEditorService.getQuestDialogs(questDialogIds);
|
||||
if (state == QuestContentState.Test)
|
||||
{
|
||||
UGQValidator validator = new UGQValidator(_ugqMetaData);
|
||||
var errors = validator.Validate(questContentEntity, questDialogs);
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
return Results.BadRequest(
|
||||
ApiResponseFactory.validationError(ServerErrorCode.UgqValidationError,
|
||||
errors.Select(x => x.ToString()).ToList()));
|
||||
}
|
||||
}
|
||||
|
||||
(errorCode, updated) = await _questEditorService.changeQuestStateForEditor(userGuid,
|
||||
questContentEntity, questDialogs, before, state, adminUsername);
|
||||
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (updated == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(updated.ToDTO(langEnum));
|
||||
}
|
||||
|
||||
public async Task<IResult> getCreatorPointHistory(string userGuid, int pageNumber, int pageSize, CreatorPointHistoryKind kind, DateTimeOffset startDate, DateTimeOffset endDate)
|
||||
{
|
||||
pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
|
||||
var result = await _questEditorService.getCreatorPointHistories(
|
||||
userGuid, pageNumber, pageSize, kind, startDate, endDate);
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(result.ToDTO(langEnum));
|
||||
}
|
||||
|
||||
public async Task<IResult> getQuestProfitStats(string userGuid, int pageNumber, int pageSize)
|
||||
{
|
||||
pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
|
||||
var result = await _questEditorService.getQuestProfitStats(userGuid, pageNumber, pageSize);
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(new UGQQuestProfitStatsResult
|
||||
{
|
||||
PageNumber = result.PageNumber,
|
||||
PageSize = result.PageSize,
|
||||
TotalPages = result.TotalPages,
|
||||
Items = result.Items.Select(x => x.ToDTO(langEnum)).ToList()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
598
UGQApiServer/Controllers/DevelopmentController.cs
Normal file
598
UGQApiServer/Controllers/DevelopmentController.cs
Normal file
@@ -0,0 +1,598 @@
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using ServerCommon;
|
||||
using UGQApiServer.Models;
|
||||
using UGQDataAccess.Repository;
|
||||
using UGQDataAccess.Service;
|
||||
using UGQDatabase.Models;
|
||||
using MySqlConnector;
|
||||
using ServerCore; using ServerBase;
|
||||
using UGQApiServer.UGQData;
|
||||
using Amazon.DynamoDBv2;
|
||||
using Amazon.DynamoDBv2.DataModel;
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
using MongoDB.Bson;
|
||||
using System.Text.Json.Nodes;
|
||||
using StackExchange.Redis;
|
||||
using UGQApiServer.Validation;
|
||||
using ServerCommon.BusinessLogDomain;
|
||||
using UGQApiServer.Auth;
|
||||
|
||||
namespace UGQApiServer.Controllers
|
||||
{
|
||||
|
||||
#if DEBUG
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[ApiExplorerSettings(GroupName = "development")]
|
||||
[Route("api/v{version:apiVersion}/[controller]")]
|
||||
[DevelopmentOnly]
|
||||
public class DevelopmentController : ControllerBase
|
||||
{
|
||||
DynamoDbClient _dynamoDbClient;
|
||||
AccountService _accountService;
|
||||
QuestEditorService _questEditorService;
|
||||
InGameService _inGameService;
|
||||
readonly string _ssoAccountDb;
|
||||
UGQBannerImageList _ugqbannerImageList;
|
||||
IConnectionMultiplexer _redis;
|
||||
UGQMetaData _ugqMetaData;
|
||||
AuthSql _authSql;
|
||||
|
||||
public DevelopmentController(DynamoDbClient dynamoDbClient,
|
||||
AccountService accountService,
|
||||
QuestEditorService questEditorService,
|
||||
InGameService inGameService,
|
||||
IConfiguration configuration,
|
||||
UGQBannerImageList ugqbannerImageList,
|
||||
IConnectionMultiplexer redis,
|
||||
UGQMetaData ugqMetaData,
|
||||
AuthSql authSql)
|
||||
{
|
||||
_dynamoDbClient = dynamoDbClient;
|
||||
_accountService = accountService;
|
||||
_questEditorService = questEditorService;
|
||||
_inGameService = inGameService;
|
||||
_ssoAccountDb = configuration["SSOAccount:SsoAccountDb"] ?? "";
|
||||
_ugqbannerImageList = ugqbannerImageList;
|
||||
_redis = redis;
|
||||
_ugqMetaData = ugqMetaData;
|
||||
_authSql = authSql;
|
||||
}
|
||||
|
||||
string? userGuidFromToken()
|
||||
{
|
||||
return User.Claims.FirstOrDefault(c => c.Type == "Id")?.Value;
|
||||
}
|
||||
async Task<(ServerErrorCode, AccountBaseAttrib?)> insertAccount(string loginAccountId)
|
||||
{
|
||||
var account_doc = new AccountBaseDoc(loginAccountId);
|
||||
|
||||
string newUserGuid = System.Guid.NewGuid().ToString("N");
|
||||
|
||||
var account_base_attrib = account_doc.getAttrib<AccountBaseAttrib>();
|
||||
NullReferenceCheckHelper.throwIfNull(account_base_attrib, () => $"account_base_attrib is null !!!");
|
||||
|
||||
account_base_attrib.AccountId = loginAccountId;
|
||||
account_base_attrib.UserGuid = newUserGuid;
|
||||
account_base_attrib.Password = "1234";
|
||||
account_base_attrib.LanguageType = LanguageType.Ko;
|
||||
account_base_attrib.AuthAdminLevelType = AuthAdminLevelType.None;
|
||||
account_base_attrib.AccountCreationType = AccountCreationType.None;
|
||||
account_base_attrib.AccountCreationMetaId = 0;
|
||||
account_base_attrib.LoginDateTime = DateTime.MinValue;
|
||||
account_base_attrib.LogoutDateTime = DateTime.MinValue;
|
||||
account_base_attrib.CreatedDateTime = DateTimeHelper.Current;
|
||||
|
||||
var result = await account_doc.newDoc4Query();
|
||||
if(result.isFail())
|
||||
{
|
||||
return (result.getErrorCode(), null);
|
||||
}
|
||||
|
||||
(result, var account_document) = await account_doc.onCopyToDocument();
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result.getErrorCode(), null);
|
||||
}
|
||||
|
||||
var table = _dynamoDbClient.getTableByDoc<AccountBaseDoc>();
|
||||
(result, _) = await table.simpleUpsertDocument(account_document);
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result.getErrorCode(), null);
|
||||
}
|
||||
|
||||
return (ServerErrorCode.Success, account_base_attrib);
|
||||
}
|
||||
|
||||
async Task<(ServerErrorCode, NicknameAttrib?)> insertNickname(string userGuid, string nickname)
|
||||
{
|
||||
var nickname_doc = new NicknameDoc(OwnerEntityType.User, userGuid, string.Empty);
|
||||
|
||||
var nickname_doc_doc_attrib = nickname_doc.getAttrib<NicknameAttrib>();
|
||||
NullReferenceCheckHelper.throwIfNull(nickname_doc_doc_attrib, () => $"nickname_doc_doc_attrib is null !!!");
|
||||
|
||||
nickname_doc_doc_attrib.Nickname = nickname;
|
||||
|
||||
var result = await nickname_doc.newDoc4Query();
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result.getErrorCode(), null);
|
||||
}
|
||||
|
||||
(result, var document) = await nickname_doc.onCopyToDocument();
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result.getErrorCode(), null);
|
||||
}
|
||||
|
||||
var table = _dynamoDbClient.getTableByDoc<NicknameDoc>();
|
||||
(result, _) = await table.simpleUpsertDocument(document);
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result.getErrorCode(), null);
|
||||
}
|
||||
|
||||
return (ServerErrorCode.Success, nickname_doc_doc_attrib);
|
||||
}
|
||||
|
||||
async Task<(ServerErrorCode, MoneyAttrib?)> insertMoney(string userGuid)
|
||||
{
|
||||
var money_doc = new MoneyDoc(OwnerEntityType.User, userGuid);
|
||||
|
||||
var money_doc_doc_attrib = money_doc.getAttrib<MoneyAttrib>();
|
||||
NullReferenceCheckHelper.throwIfNull(money_doc_doc_attrib, () => $"money_doc_doc_attrib is null !!!");
|
||||
|
||||
money_doc_doc_attrib.Gold = 0;
|
||||
money_doc_doc_attrib.Sapphire = 0;
|
||||
money_doc_doc_attrib.Calium = 0;
|
||||
money_doc_doc_attrib.Ruby = 0;
|
||||
|
||||
var result = await money_doc.newDoc4Query();
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result.getErrorCode(), null);
|
||||
}
|
||||
|
||||
(result, var money_document) = await money_doc.onCopyToDocument();
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result.getErrorCode(), null);
|
||||
}
|
||||
|
||||
var table = _dynamoDbClient.getTableByDoc<MoneyDoc>();
|
||||
(result, _) = await table.simpleUpsertDocument(money_document);
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result.getErrorCode(), null);
|
||||
}
|
||||
|
||||
return (ServerErrorCode.Success, money_doc_doc_attrib);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// email로 계정 정보 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("get-gamedb-account-by-email")]
|
||||
public async Task<IResult> getGameDBAccountByEmail([FromQuery] string email)
|
||||
{
|
||||
ulong accountId = await _authSql.getAccountIdFromMysql(email);
|
||||
if (accountId == 0)
|
||||
{
|
||||
return Results.Ok(new GetGameDBAccountByEmail
|
||||
{
|
||||
Message = $"{email}이 인증 DB에 없음"
|
||||
});
|
||||
}
|
||||
|
||||
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 GetGameDBAccountByEmail
|
||||
{
|
||||
Message = $"{email}이 게임 DB에 없음",
|
||||
LoginAccountId = loginAccountId,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return Results.Ok(new GetGameDBAccountByEmail
|
||||
{
|
||||
Message = $"성공",
|
||||
|
||||
LoginAccountId = loginAccountId,
|
||||
UserGuid = gameAccount.UserGuid,
|
||||
Nickname = gameAccount.Nickname,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// email로 DynamoDB에 AccountBaseDoc, NicknameDoc을 생성
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 주의! - 게임 클라이언트를 사용할 수 없는 경우에만 api로 계정을 만드십시오!
|
||||
/// UGQ api를 사용하기 위한 최소한의 세팅으로 계정을 만드는 거라
|
||||
/// 이 api로 만든 계정으로 게임 접속 시에는 정상적으로 작동하지 않습니다.
|
||||
/// </remarks>
|
||||
[HttpPost]
|
||||
[Route("add-gamedb-account-by-email")]
|
||||
public async Task<IResult> addGameDBAccountByEmail(AddGameDBAccountByEmailRequest request)
|
||||
{
|
||||
ulong accountId = await _authSql.getAccountIdFromMysql(request.Email);
|
||||
if (accountId == 0)
|
||||
return Results.BadRequest($"{request.Email} not found");
|
||||
|
||||
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 AddGameDBAccountResponse
|
||||
{
|
||||
LoginAccountId = loginAccountId,
|
||||
UserGuid = gameAccount.UserGuid,
|
||||
Nickname = gameAccount.Nickname,
|
||||
});
|
||||
}
|
||||
|
||||
(var errrorCode, AccountBaseAttrib? account_doc_doc_attrib) = await insertAccount(loginAccountId);
|
||||
if (account_doc_doc_attrib == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errrorCode));
|
||||
|
||||
(errrorCode, NicknameAttrib? nickname_doc_doc_attrib) = await insertNickname(account_doc_doc_attrib.UserGuid, request.Nickname);
|
||||
if (nickname_doc_doc_attrib == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errrorCode));
|
||||
|
||||
return Results.Ok(new AddGameDBAccountResponse
|
||||
{
|
||||
LoginAccountId = account_doc_doc_attrib.AccountId,
|
||||
UserGuid = account_doc_doc_attrib.UserGuid,
|
||||
Nickname = nickname_doc_doc_attrib.Nickname,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// loginAccountId로 DynamoDB에 AccountBaseDoc, NicknameDoc을 생성
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 주의! - 게임 클라이언트를 사용할 수 없는 경우에만 api로 계정을 만드십시오!
|
||||
/// UGQ api를 사용하기 위한 최소한의 세팅으로 계정을 만드는 거라
|
||||
/// 이 api로 만든 계정으로 게임 접속 시에는 정상적으로 작동하지 않습니다.
|
||||
/// </remarks>
|
||||
[HttpPost]
|
||||
[Route("add-gamedb-account")]
|
||||
public async Task<IResult> addGameDBAccount(AddGameDBAccountRequest request)
|
||||
{
|
||||
(Result result, UserByAccountIdResult? gameAccount) =
|
||||
await EntitySearchHelper.findUserByAccountId(_dynamoDbClient, request.LoginAccountId, "");
|
||||
if (result.isFail() && result.ErrorCode != ServerErrorCode.DynamoDbQueryNoMatchAttribute)
|
||||
return Results.BadRequest(ApiResponseFactory.error(result.ErrorCode));
|
||||
|
||||
if (gameAccount != null)
|
||||
{
|
||||
return Results.Ok(new AddGameDBAccountResponse
|
||||
{
|
||||
LoginAccountId = request.LoginAccountId,
|
||||
UserGuid = gameAccount.UserGuid,
|
||||
Nickname = gameAccount.Nickname,
|
||||
});
|
||||
}
|
||||
|
||||
(var errrorCode, AccountBaseAttrib? account_doc_doc_attrib) = await insertAccount(request.LoginAccountId);
|
||||
if (account_doc_doc_attrib == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errrorCode));
|
||||
|
||||
(errrorCode, NicknameAttrib? nickname_doc_doc_attrib) = await insertNickname(account_doc_doc_attrib.UserGuid, request.Nickname);
|
||||
if (nickname_doc_doc_attrib == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errrorCode));
|
||||
|
||||
return Results.Ok(new AddGameDBAccountResponse
|
||||
{
|
||||
LoginAccountId = account_doc_doc_attrib.AccountId,
|
||||
UserGuid = account_doc_doc_attrib.UserGuid,
|
||||
Nickname = nickname_doc_doc_attrib.Nickname,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DynamoDB에 UgcNpcDoc을 생성
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 주의! - 게임 클라이언트를 사용할 수 없는 경우에만 api로 UgcNpc를 만드십시오!
|
||||
/// UGQ api를 사용하기 위한 최소한의 세팅으로 UgcNpc를 만드는 거라
|
||||
/// 이 api로 만든 UgcNpc는 게임 접속 시에는 정상적으로 작동하지 않을 수 잇습니다.
|
||||
/// </remarks>
|
||||
[HttpPost]
|
||||
[Route("add-fake-ugc-npc")]
|
||||
public async Task<IResult> addFakeUgcNpc(AddFakeUgcNpcReqeust request)
|
||||
{
|
||||
string newUgcNpcGUid = System.Guid.NewGuid().ToString("N");
|
||||
|
||||
var ugc_npc_doc = new UgcNpcDoc(OwnerEntityType.User, request.UserGuid, newUgcNpcGUid);
|
||||
|
||||
var ugc_npc_attrib = ugc_npc_doc.getAttrib<UgcNpcAttrib>();
|
||||
if (ugc_npc_attrib == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
||||
|
||||
ugc_npc_attrib.Nickname = request.UgcNpcNickname;
|
||||
ugc_npc_attrib.Title = "empty";
|
||||
ugc_npc_attrib.Greeting = "empty";
|
||||
ugc_npc_attrib.Introduction = "empty";
|
||||
ugc_npc_attrib.Description = "empty";
|
||||
ugc_npc_attrib.WorldScenario = "empty";
|
||||
ugc_npc_attrib.DefaultSocialActionMetaId = 0;
|
||||
ugc_npc_attrib.HabitSocialActionMetaIds = [];
|
||||
ugc_npc_attrib.DialogueSocialActionMetaIds = [];
|
||||
ugc_npc_attrib.BodyItemMetaId = 0;
|
||||
ugc_npc_attrib.HashTagMetaIds = [];
|
||||
ugc_npc_attrib.IsRegisteredAiChatServer = false;
|
||||
|
||||
var result = await ugc_npc_doc.newDoc4Query();
|
||||
if (result.isFail())
|
||||
{
|
||||
return Results.BadRequest(ApiResponseFactory.error(result.getErrorCode()));
|
||||
}
|
||||
|
||||
(result, var ugq_npc_document) = await ugc_npc_doc.onCopyToDocument();
|
||||
if (result.isFail())
|
||||
{
|
||||
return Results.BadRequest(ApiResponseFactory.error(result.getErrorCode()));
|
||||
}
|
||||
|
||||
var table = _dynamoDbClient.getTableByDoc<UgcNpcDoc>();
|
||||
(result, _) = await table.simpleUpsertDocument(ugq_npc_document);
|
||||
if (result.isFail())
|
||||
{
|
||||
return Results.BadRequest(ApiResponseFactory.error(result.getErrorCode()));
|
||||
}
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creator Point를 증가
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("add-creator-point")]
|
||||
public async Task<IResult> addCreatorPoint(AddCreatorPointReqeust request)
|
||||
{
|
||||
var errorCode = await _accountService.addCreatorPoint(request.UserGuid, request.QuestId, request.Revision, request.Amount, UgqCreatorPointReason.Development);
|
||||
if(errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(errorCode);
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creator Point를 감소
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("settle-up-creator-point")]
|
||||
public async Task<IResult> settleUpCreatorPoint(SettleUpCreatorPointReqeust request)
|
||||
{
|
||||
var errorCode = await _accountService.decCreatorPoint(request.UserGuid, request.Amount, UgqCreatorPointReason.Development);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(errorCode);
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// GameQuestData를 새로 쓰기
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("ugq-game-quest-data-fake-update")]
|
||||
public async Task<IResult> gameQuestDataFakeUpdate(GameQuestDataFakeUpdateReqeust request)
|
||||
{
|
||||
await _questEditorService.gameQuestDataFakeUpdate(request.UserGuid, request.QuestContentId);
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// s3에 올라간 preset 파일 리스트 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("get-s3-preset")]
|
||||
public async Task<IResult> getS3Preset()
|
||||
{
|
||||
return Results.Ok(await _ugqbannerImageList.getFiles());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DynamoDB sacn을 사용해서 userGuid로 AccountBaseDoc을 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("find-account-id")]
|
||||
public async Task<IResult> findAccountId([FromQuery] string userGuid)
|
||||
{
|
||||
var client = _dynamoDbClient.getDbClient();
|
||||
if (client == null)
|
||||
return Results.BadRequest();
|
||||
|
||||
JsonNode? found_account = null;
|
||||
Dictionary<string, AttributeValue>? lastEvaluatedKey = null;
|
||||
do
|
||||
{
|
||||
var request = new ScanRequest()
|
||||
{
|
||||
TableName = _dynamoDbClient.getTableFullName(ServerBase.DynamoDbDefine.TableNames.Main),
|
||||
FilterExpression = "DocType = :v_docType",
|
||||
ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
|
||||
{
|
||||
{ ":v_docType", new AttributeValue("AccountBaseDoc") }
|
||||
},
|
||||
Limit = 100,
|
||||
ExclusiveStartKey = lastEvaluatedKey,
|
||||
};
|
||||
|
||||
var response = await client.ScanAsync(request);
|
||||
lastEvaluatedKey = response.LastEvaluatedKey;
|
||||
|
||||
foreach (var item in response.Items)
|
||||
{
|
||||
if (item.TryGetValue("AccountBaseAttrib", out var attr) == false)
|
||||
continue;
|
||||
|
||||
JsonNode node = JsonNode.Parse(attr.S)!;
|
||||
|
||||
string doc_user_guid = node["user_guid"]!.GetValue<string>();
|
||||
if(doc_user_guid == userGuid)
|
||||
{
|
||||
found_account = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_account != null)
|
||||
break;
|
||||
|
||||
} while (lastEvaluatedKey.Count != 0);
|
||||
|
||||
return Results.Ok(new
|
||||
{
|
||||
UserGuid = userGuid,
|
||||
AccountBaseAttrib = found_account,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 레디스에 저장된 ugq 로그인 정보를 모두 삭제
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("clear-all-login")]
|
||||
public async Task<IResult> clearAllLogin()
|
||||
{
|
||||
var server = _redis.GetServer(_redis.GetEndPoints().First());
|
||||
var enumerable = server.KeysAsync(pattern: "ugq_login:*");
|
||||
var enumerator = enumerable.GetAsyncEnumerator();
|
||||
var dataList = new List<RedisKey>();
|
||||
|
||||
while (await enumerator.MoveNextAsync())
|
||||
{
|
||||
dataList.Add(enumerator.Current);
|
||||
}
|
||||
|
||||
Log.getLogger().info($"delete keys. [{string.Join(",", dataList.Select(x => x.ToString()))}]");
|
||||
await _redis.GetDatabase().KeyDeleteAsync(dataList.ToArray());
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
async Task<(Result, MoneyDoc?)> findMondyDoc(DynamoDbClient dynamo_db_client, string userGuid)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
(result, var make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<MoneyDoc>(userGuid);
|
||||
if (result.isFail())
|
||||
{
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
var query_config = dynamo_db_client.makeQueryConfigForReadByPKOnly(make_primary_key!.PK);
|
||||
(result, var found_money_doc) = await dynamo_db_client.simpleQueryDocTypeWithQueryOperationConfig<MoneyDoc>(query_config);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to simpleQueryDocTypeWithQueryOperationConfig() !!! : {result.toBasicString()}, {make_primary_key.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
|
||||
return (result, null);
|
||||
}
|
||||
|
||||
return (result, found_money_doc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DynamoDB에 저장된 재화를 변경
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("change-currency-amount")]
|
||||
public async Task<IResult> changeCurrencyAmount(ChangeCurrencyAmountRequest request)
|
||||
{
|
||||
var result = new Result();
|
||||
|
||||
var db_connector = CurrencyControlHelper.getDbConnector();
|
||||
(result, var is_login) = await ServerCommon.EntityHelper.isUserLoggedIn(db_connector, request.UserGuid);
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
||||
if(true == is_login)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
||||
|
||||
(result, var moneyDoc) = await findMondyDoc(_dynamoDbClient, request.UserGuid);
|
||||
if (moneyDoc == null)
|
||||
await insertMoney(request.UserGuid);
|
||||
|
||||
if (Enum.TryParse(MetaHelper.GameConfigMeta.UGQAddSlotType, true, out CurrencyType currencyType) == false)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
||||
|
||||
(result, double updatedAmount) = await CurrencyControlHelper.changeMoneyByUserGuid(request.UserGuid, currencyType, request.Amount);
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqCurrencyError));
|
||||
|
||||
return Results.Ok(new ChangeCurrencyAmountResponse
|
||||
{
|
||||
UserGuid = request.UserGuid,
|
||||
CurrencyType = currencyType,
|
||||
CurrentAmount = updatedAmount,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 계정이 가지고 있는 모든 UGQ 데이터를 검증
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("validate-quest")]
|
||||
public async Task<IResult> validateQuest(ValidateQuestRequest request)
|
||||
{
|
||||
List<ValidateQuestItem> items = new();
|
||||
|
||||
var quests = await _questEditorService.getAll(request.UserGuid);
|
||||
foreach (var quest in quests)
|
||||
{
|
||||
var questDialogIds = quest.Tasks
|
||||
.Where(x => string.IsNullOrEmpty(x.DialogId) == false)
|
||||
.Select(x => x.DialogId!)
|
||||
.ToList();
|
||||
var questDialogs = await _questEditorService.getQuestDialogs(questDialogIds);
|
||||
|
||||
UGQValidator validator = new UGQValidator(_ugqMetaData);
|
||||
var errors = validator.Validate(quest, questDialogs);
|
||||
|
||||
items.Add(new ValidateQuestItem
|
||||
{
|
||||
QuestContent = quest,
|
||||
QuestDialogs = questDialogs,
|
||||
Errors = errors.Select(x => x.ToString()).ToList()
|
||||
});
|
||||
}
|
||||
|
||||
return Results.Ok(items);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("set-author")]
|
||||
public async Task<IResult> setAuthor()
|
||||
{
|
||||
await _inGameService.setAuthor();
|
||||
return Results.Ok();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
371
UGQApiServer/Controllers/InGameController.cs
Normal file
371
UGQApiServer/Controllers/InGameController.cs
Normal file
@@ -0,0 +1,371 @@
|
||||
using Asp.Versioning;
|
||||
using MetaAssets;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StackExchange.Redis;
|
||||
using UGQDataAccess.Repository;
|
||||
using UGQDataAccess.Service;
|
||||
using ServerCommon.UGQ.Models;
|
||||
using System.Globalization;
|
||||
using MongoDB.Driver.Linq;
|
||||
using UGQApiServer.Converter;
|
||||
using UGQDataAccess;
|
||||
using ServerCommon.UGQ;
|
||||
using UGQDatabase.Models;
|
||||
using Amazon.Runtime.Internal;
|
||||
using System.Security.Principal;
|
||||
using Amazon.S3.Model;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using ServerCommon.BusinessLogDomain;
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
namespace UGQApiServer.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[ApiExplorerSettings(GroupName = "ingame")]
|
||||
[Route("api/v{version:apiVersion}/[controller]")]
|
||||
[UGQInGameApi]
|
||||
public class InGameController : ControllerBase
|
||||
{
|
||||
InGameService _inGameService;
|
||||
IConnectionMultiplexer _redis;
|
||||
|
||||
public InGameController(InGameService inGameService, IConnectionMultiplexer redis)
|
||||
{
|
||||
_inGameService = inGameService;
|
||||
_redis = redis;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// userGuid의 UGQ 계정 정보 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("account/{userGuid}")]
|
||||
public async Task<IResult> getAccount(string userGuid)
|
||||
{
|
||||
(ServerErrorCode errorCode, var accountEntity) = await _inGameService.getAccount(userGuid);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (accountEntity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
return Results.Ok(new UgqAccount
|
||||
{
|
||||
GradeType = accountEntity.GradeType,
|
||||
CreatorPoint = accountEntity.CreatorPoint,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 직접 작성한 퀘스트를 수락해서 완료까지 진행해보기 위해서 Test 상태인 퀘스트 리스트
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("user-quests-in-test-state/{userGuid}")]
|
||||
public async Task<IResult> getUserQuestsInTestState(string userGuid)
|
||||
{
|
||||
var result = await _inGameService.getUserQuests(userGuid, [QuestContentState.Test]);
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(result.Select(x => x.ToProtoUgqQuestInTestState(langEnum, ImageUrlType.BannerImg)).ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// userGuid가 작성해서 Live 상태로 보낸 퀘스트 리스트
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("quest-board/{userGuid}")]
|
||||
public async Task<IResult> getUserQuestBoard(string userGuid, [FromQuery] UgqQuestBoardRequest request)
|
||||
{
|
||||
int pageSize = Math.Clamp(request.PageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
var result = await _inGameService.getQuestBoard(userGuid, request, pageSize);
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(result.ToProto(langEnum, ImageUrlType.BannerImg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 전체 Live 상태인 퀘스트 리스트
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("quest-board")]
|
||||
public async Task<IResult> getQuestBoard([FromQuery] UgqQuestBoardRequest request)
|
||||
{
|
||||
int pageSize = Math.Clamp(request.PageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
var result = await _inGameService.getQuestBoard(null, request, pageSize);
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(result.ToProto(langEnum, ImageUrlType.BannerImg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// userGuid가 북마크한 Live 상태 퀘스트 리스트
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("bookmark-quest-board/{userGuid}")]
|
||||
public async Task<IResult> getBookmarkQuestBoard(string userGuid, [FromQuery] UgqQuestBoardRequest request)
|
||||
{
|
||||
int pageSize = Math.Clamp(request.PageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
var result = await _inGameService.getBookmarkQuests(userGuid, request, pageSize);
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(result.ToProto(langEnum, ImageUrlType.BannerImg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 스포트라이트
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("quest-board-spotlight")]
|
||||
public async Task<IResult> getQuestBoardSpotlight()
|
||||
{
|
||||
var result = await _inGameService.getQuestBoardSpotlight();
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(result.ToProto(langEnum, ImageUrlType.TitleImg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 자세한 정보
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("quest-board-detail/{userGuid}")]
|
||||
public async Task<IResult> getQuestBoardDetail(string userGuid, [FromQuery] UgqQuestIdRevisionRequest request)
|
||||
{
|
||||
var result = await _inGameService.getQuestBoardDetail(userGuid, request.QuestId, request.Revision);
|
||||
if (result == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(result.ToProto(langEnum, ImageUrlType.BannerImg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test 상태인 퀘스트를 정상적으로 완료시켰을 때 호출해서 Standby 상태로 변경
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("set-test-completed/{userGuid}")]
|
||||
public async Task<IResult> setTestCompleted(string userGuid, [FromQuery] long questId, [FromQuery] long revision)
|
||||
{
|
||||
// 여기에서만 Test -> Standby 가능
|
||||
(var errorCode, var updated) = await _inGameService.changeQuestStateForInGame(userGuid,
|
||||
questId, revision, [QuestContentState.Test], QuestContentState.Standby);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test 상태인 퀘스트를 포기했을 때 호출해서 Editable 상태로 변경
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("set-test-aborted/{userGuid}")]
|
||||
public async Task<IResult> setTestAborted(string userGuid, [FromQuery] long questId, [FromQuery] long revision)
|
||||
{
|
||||
// 여기에서만 Test -> Editable 가능
|
||||
(var errorCode, var updated) = await _inGameService.changeQuestStateForInGame(userGuid,
|
||||
questId, revision, [QuestContentState.Test], QuestContentState.Editable);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트를 수락했을 때 호출
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("set-quest-accepted/{userGuid}")]
|
||||
public async Task<IResult> setQuestAccepted(string userGuid, UgqQuestAcceptedRequest request)
|
||||
{
|
||||
var errorCode = await _inGameService.setQuestAccepted(userGuid, request);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트를 완료했을 때 호출
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("set-quest-completed/{userGuid}")]
|
||||
public async Task<IResult> setQuestCompleted(string userGuid, UgqQuestCompletedRequest request)
|
||||
{
|
||||
var errorCode = await _inGameService.setQuestCompleted(userGuid, request);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트를 포기했을 때 호출
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("set-quest-aborted/{userGuid}")]
|
||||
public async Task<IResult> setQuestAbort(string userGuid, UgqQuestAbortedRequest request)
|
||||
{
|
||||
var errorCode = await _inGameService.setQuestAborted(userGuid, request);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트를 북마크
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("bookmark")]
|
||||
public async Task<IResult> bookmark(UgqBookmarkRequest request)
|
||||
{
|
||||
var errorCode = await _inGameService.bookmark(request.QuestId, request.UserGuid);
|
||||
if(errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트를 북마크 취소
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("unbookmark")]
|
||||
public async Task<IResult> unbookmark(UgqUnbookmarkRequest request)
|
||||
{
|
||||
var errorCode = await _inGameService.unbookmark(request.QuestId, request.UserGuid);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트를 좋아요
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("like")]
|
||||
public async Task<IResult> like(UgqLikeRequest request)
|
||||
{
|
||||
var errorCode = await _inGameService.like(request.QuestId, request.Revision, request.UserGuid);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트를 좋아요 취소
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("unlike")]
|
||||
public async Task<IResult> unlike(UgqUnlikeRequest request)
|
||||
{
|
||||
var errorCode = await _inGameService.unlike(request.QuestId, request.Revision, request.UserGuid);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트를 신고
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("report")]
|
||||
public async Task<IResult> report(UgqReportRequest request)
|
||||
{
|
||||
var errorCode = await _inGameService.report(request.UserGuid, request.QuestId, request.Revision, request.Contents);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
return Results.Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test 상태인 UGQ QuestScript 정보 얻기 (직접 작성한 퀘스트만 획득 가능)
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("test-game-quest-data/{userGuid}")]
|
||||
public async Task<IResult> getTestGameQuestData(string userGuid, [FromQuery] long questId, [FromQuery] long revision)
|
||||
{
|
||||
(ServerErrorCode errorCode, var gameQuestDataEntity) = await _inGameService.getTestGameQuestData(userGuid, questId, revision);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (gameQuestDataEntity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
return Results.Ok(gameQuestDataEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Live 상태인 UGQ QuestScript 정보 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("game-quest-data")]
|
||||
public async Task<IResult> getGameQuestData([FromQuery] long questId, [FromQuery] long revision)
|
||||
{
|
||||
(ServerErrorCode errorCode, var gameQuestDataEntity) = await _inGameService.getGameQuestData(questId, revision, QuestContentState.Live);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (gameQuestDataEntity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
return Results.Ok(gameQuestDataEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// QuestScript 데이터 중 일부만 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("game-quest-data-simple")]
|
||||
public async Task<IResult> getGameQuestDataSimple([FromQuery] long questId, [FromQuery] long revision, [FromQuery] QuestContentState state)
|
||||
{
|
||||
(ServerErrorCode errorCode, var gameQuestDataEntity) = await _inGameService.getGameQuestData(questId, revision, state);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (gameQuestDataEntity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
return Results.Ok(gameQuestDataEntity.ToSimpleDTO());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 최신 리비전의 QuestScript 정보 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("latest-revision-game-quest-data")]
|
||||
public async Task<IResult> getLatestRevisionGameQuestData([FromQuery] long questId)
|
||||
{
|
||||
(ServerErrorCode errorCode, var gameQuestDataEntity) = await _inGameService.getLatestRevisionGameQuestData(questId);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (gameQuestDataEntity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
return Results.Ok(gameQuestDataEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UGQ 로그인 상태를 레디스에서 삭제
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("clear-login/{userGuid}")]
|
||||
public async Task<IResult> clearLogin(string userGuid)
|
||||
{
|
||||
// ugq 로그인 상태를 삭제 시킨다.
|
||||
await _redis.GetDatabase().KeyDeleteAsync($"ugq_login:{userGuid}");
|
||||
return Results.Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
198
UGQApiServer/Controllers/MetaDataController.cs
Normal file
198
UGQApiServer/Controllers/MetaDataController.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using System.Globalization;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
using UGQApiServer.Converter;
|
||||
using UGQApiServer.Models;
|
||||
using UGQApiServer.UGQData;
|
||||
using UGQDataAccess;
|
||||
using ServerCommon.UGQ;
|
||||
using ServerCommon.UGQ.Models;
|
||||
using ServerCommon;
|
||||
using UGQApiServer.Auth;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using UGQApiServer.Controllers.Common;
|
||||
using MetaAssets;
|
||||
using ServerBase;
|
||||
|
||||
|
||||
namespace UGQApiServer.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{version:apiVersion}/[controller]")]
|
||||
[UGQWebApi]
|
||||
[AllowWhenSingleLogin]
|
||||
public class MetaDataController : ControllerBase
|
||||
{
|
||||
const int PAGE_SIZE = 30;
|
||||
UGQMetaData _ugqMetaData;
|
||||
DynamoDbClient _dynamoDbClient;
|
||||
|
||||
MetaDataApi _metaDataApi;
|
||||
|
||||
public MetaDataController(UGQMetaData ugqMetaData,
|
||||
DynamoDbClient dynamoDbClient,
|
||||
MetaDataApi metaDataApi)
|
||||
{
|
||||
_ugqMetaData = ugqMetaData;
|
||||
_dynamoDbClient = dynamoDbClient;
|
||||
_metaDataApi = metaDataApi;
|
||||
}
|
||||
|
||||
string? userGuidFromToken()
|
||||
{
|
||||
return User.Claims.FirstOrDefault(c => c.Type == "Id")?.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD>밡<EFBFBD><EBB0A1><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> npc <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <param name="pageNumber"><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
|
||||
/// <param name="pageSize">pageSize (<28>ִ<EFBFBD> 100)</param>
|
||||
[HttpGet]
|
||||
[Route("assignable-npcs")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQNpcMetaDataList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getAssignableNpcs([FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _metaDataApi.getAssignableNpcs(userGuid, pageNumber, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD>밡<EFBFBD><EBB0A1><EFBFBD><EFBFBD> task action <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <param name="pageNumber"><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
|
||||
/// <param name="pageSize">pageSize (<28>ִ<EFBFBD> 100)</param>
|
||||
[HttpGet]
|
||||
[Route("available-task-actions")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(TaskActionList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getAvailableTaskActions([FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
return await _metaDataApi.getAvailableTaskActions(pageNumber, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><EFBFBD><D7BC><EFBFBD> <20>Է°<D4B7><C2B0><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD>Ʈ <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Task Action<6F><6E> <20>ǻ<EFBFBD><C7BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>쿡 <20><><EFBFBD><EFBFBD>
|
||||
/// </remarks>
|
||||
/// <param name="taskActionId">TaskAction<6F><6E> <20><><EFBFBD>̵<EFBFBD></param>
|
||||
/// <param name="pageNumber"><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
|
||||
/// <param name="pageSize">pageSize (<28>ִ<EFBFBD> 100)</param>
|
||||
[HttpGet]
|
||||
[Route("task-action-values")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(TaskActionValueList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getActionValues([FromQuery] int taskActionId, [FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _metaDataApi.getActionValues(userGuid, taskActionId, pageNumber, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD>밡<EFBFBD><EBB0A1><EFBFBD><EFBFBD> dialog <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <param name="pageNumber"><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
|
||||
/// <param name="pageSize">pageSize (<28>ִ<EFBFBD> 100)</param>
|
||||
[HttpGet]
|
||||
[Route("dialog-types")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(DialogTypeList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getDialogTypes([FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
return await _metaDataApi.getDialogTypes(pageNumber, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// dialog <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>밡<EFBFBD><EBB0A1><EFBFBD><EFBFBD> dialog <20><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <param name="dialogType">dialog <20><><EFBFBD><EFBFBD></param>
|
||||
/// <param name="pageNumber"><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
|
||||
/// <param name="pageSize">pageSize (<28>ִ<EFBFBD> 100)</param>
|
||||
[HttpGet]
|
||||
[Route("dialog-actions")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(DialogActionList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getDialogActions([FromQuery] int dialogType, [FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
return await _metaDataApi.getDialogActions(dialogType, pageNumber, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// dialog <20>ǿ<D7BC> <20><><EFBFBD><EFBFBD> <20>Է<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <param name="dialogType"><3E><><EFBFBD>̾<EFBFBD><CCBE>α<EFBFBD> Ÿ<><C5B8></param>
|
||||
/// <param name="dialogActionId"><3E><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD></param>
|
||||
/// <param name="pageNumber"><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
|
||||
/// <param name="pageSize">pageSize (<28>ִ<EFBFBD> 100)</param>
|
||||
[HttpGet]
|
||||
[Route("dialog-action-values")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(DialogActionValueList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getDialogConditionValues([FromQuery] int dialogType, [FromQuery] int dialogActionId, [FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
return await _metaDataApi.getDialogConditionValues(dialogType, dialogActionId, pageNumber, pageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NPC <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <param name="gender">npc gender</param>
|
||||
/// <param name="pageNumber"><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
|
||||
/// <param name="pageSize">pageSize (<28>ִ<EFBFBD> 100)</param>
|
||||
[HttpGet]
|
||||
[Route("npc-action-values")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(NpcActionValueList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getNpcActionValues([FromQuery] EGenderType gender, [FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
return await _metaDataApi.getNpcActionValues(pageNumber, pageSize, gender);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
/// </summary>
|
||||
/// <param name="pageNumber"><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
|
||||
/// <param name="pageSize">pageSize (<28>ִ<EFBFBD> 100)</param>
|
||||
[HttpGet]
|
||||
[Route("map-data-values")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(MapDataValueList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getNpcActionValues([FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
return await _metaDataApi.getMapDataValues(pageNumber, pageSize);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("preset-images")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(DialogActionValueList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getPresetImages([FromQuery] PresetKind kind, [FromQuery] PresetCategory category)
|
||||
{
|
||||
return await _metaDataApi.getPresetImages(kind, category);
|
||||
}
|
||||
}
|
||||
}
|
||||
624
UGQApiServer/Controllers/QuestEditorController.cs
Normal file
624
UGQApiServer/Controllers/QuestEditorController.cs
Normal file
@@ -0,0 +1,624 @@
|
||||
using System.Globalization;
|
||||
using Asp.Versioning;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using Swashbuckle.AspNetCore.Annotations;
|
||||
using Swashbuckle.AspNetCore.Filters;
|
||||
using UGQApiServer.Models;
|
||||
using UGQDatabase.Models;
|
||||
using UGQDataAccess.Service;
|
||||
using UGQApiServer.Converter;
|
||||
using UGQApiServer.Storage;
|
||||
using UGQDataAccess.Repository.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using UGQDataAccess;
|
||||
using UGQApiServer.Validation;
|
||||
using UGQApiServer.UGQData;
|
||||
using UGQApiServer.Auth;
|
||||
using JwtRoleAuthentication.Services;
|
||||
using System.Security.Claims;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using ServerCommon;
|
||||
using ServerCommon.UGQ;
|
||||
using ServerCommon.UGQ.Models;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Collections.Generic;
|
||||
using UGQApiServer.Controllers.Common;
|
||||
using UGQDataAccess.Repository;
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
using StackExchange.Redis;
|
||||
using UGQDataAccess.Logs;
|
||||
|
||||
|
||||
|
||||
namespace UGQApiServer.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{version:apiVersion}/[controller]")]
|
||||
[UGQWebApi]
|
||||
[AllowWhenSingleLogin]
|
||||
public class QuestEditorController : ControllerBase
|
||||
{
|
||||
AccountService _accountService;
|
||||
QuestEditorService _questEditorService;
|
||||
IStorageService _storageService;
|
||||
UGQMetaData _ugqMetaData;
|
||||
TokenService _tokenService;
|
||||
WebPortalTokenAuth _webPortalTokenAuth;
|
||||
DynamoDbClient _dynamoDbClient;
|
||||
readonly IConnectionMultiplexer _redis;
|
||||
|
||||
QuestEditorApi _questEditorApi;
|
||||
|
||||
public QuestEditorController(
|
||||
AccountService accountService,
|
||||
QuestEditorService questEditorService, IStorageService storageService,
|
||||
UGQMetaData uqgMetadata, TokenService tokenService,
|
||||
WebPortalTokenAuth webPortalTokenAuth,
|
||||
DynamoDbClient dynamoDbClient,
|
||||
QuestEditorApi questEditorApi,
|
||||
IConnectionMultiplexer redis)
|
||||
{
|
||||
_accountService = accountService;
|
||||
_questEditorService = questEditorService;
|
||||
_storageService = storageService;
|
||||
_ugqMetaData = uqgMetadata;
|
||||
_tokenService = tokenService;
|
||||
_webPortalTokenAuth = webPortalTokenAuth;
|
||||
_dynamoDbClient = dynamoDbClient;
|
||||
_questEditorApi = questEditorApi;
|
||||
_redis = redis;
|
||||
}
|
||||
|
||||
string? userGuidFromToken()
|
||||
{
|
||||
return User.Claims.FirstOrDefault(c => c.Type == "Id")?.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 랩 시작 페이지
|
||||
/// </summary>
|
||||
/// <param name="pageNumber">pageNumber</param>
|
||||
/// <param name="pageSize">pageSize (최대 100)</param>
|
||||
/// <param name="state">검색할 상태</param>
|
||||
/// <param name="searchText">검색할 텍스트</param>
|
||||
[HttpGet]
|
||||
[Route("summaries")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQSummaryList))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getSummaries([FromQuery] int pageNumber, [FromQuery] int pageSize, [FromQuery] QuestContentState state, [FromQuery] string? searchText)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
pageSize = Math.Clamp(pageSize, 1, UGQConstants.MAX_INPUT_PAGE_SIZE);
|
||||
|
||||
// TODO: state만 얻어오게 수정
|
||||
List<QuestContentEntity> all = await _questEditorService.getAll(userGuid);
|
||||
|
||||
var result = await _questEditorService.getSummaries(pageNumber, pageSize, userGuid, state, searchText);
|
||||
|
||||
var query = all.GroupBy(
|
||||
p => p.State,
|
||||
p => p.State,
|
||||
(state, states) => new
|
||||
{
|
||||
Key = state,
|
||||
Count = states.Count(),
|
||||
});
|
||||
|
||||
var stateGroups = query.Select(x => new UGQStateGroup
|
||||
{
|
||||
State = x.Key,
|
||||
Count = x.Count,
|
||||
}).ToList();
|
||||
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
return Results.Ok(new UGQSummaryResponse
|
||||
{
|
||||
TotalCount = all.Count,
|
||||
StateGroups = stateGroups,
|
||||
PageNumber = result.PageNumber,
|
||||
PageSize = result.PageSize,
|
||||
TotalPages = result.TotalPages,
|
||||
Summaries = result.Items.Select(x => x.ToDTO(langEnum)).ToList(),
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 유저 정보 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("account")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQAccountDetail))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getAccount()
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
var accountEntity = await _accountService.getAccount(userGuid);
|
||||
if(accountEntity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
// TODO: count만 얻어오게 수정
|
||||
List<QuestContentEntity> all = await _questEditorService.getAll(userGuid);
|
||||
|
||||
return Results.Ok(new UGQAccountDetail
|
||||
{
|
||||
UserGuid = accountEntity.UserGuid,
|
||||
Nickname = accountEntity.Nickname,
|
||||
AccountId = accountEntity.AccountId ?? "",
|
||||
SlotCount = (MetaHelper.GameConfigMeta.UGQDefaultSlot + accountEntity.AdditionalSlotCount),
|
||||
GradeType = accountEntity.GradeType,
|
||||
CreatorPoint = accountEntity.CreatorPoint,
|
||||
TotalQuests = all.Count,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 유저 재화 정보 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("account-currency")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQAccount))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getAccountCurrency()
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
if (Enum.TryParse(MetaHelper.GameConfigMeta.UGQAddSlotType, true, out CurrencyType currencyType) == false)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
||||
|
||||
double slotPrice = MetaHelper.GameConfigMeta.UGQAddSlotAmount;
|
||||
|
||||
(var result, double currentAmount) = await CurrencyControlHelper.getMoneyByUserGuid(userGuid, currencyType);
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqCurrencyError));
|
||||
|
||||
return Results.Ok(new UGQAccountCurrency
|
||||
{
|
||||
CurrencyType = currencyType,
|
||||
Amount = currentAmount,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 유저의 메타버스 로그인 상태 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("metaverse-online-status")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQAccount))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getMetaverseOnlineStatus()
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
bool online = false;
|
||||
|
||||
var game_login_cache = await _redis.GetDatabase().StringGetAsync($"login:{userGuid}");
|
||||
if (game_login_cache.HasValue == true)
|
||||
{
|
||||
online = true;
|
||||
}
|
||||
|
||||
return Results.Ok(new {
|
||||
Online = online
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 슬롯을 추가하는데 필요한 재화와 소모량 얻기
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
[Route("slot-price")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQSlotPrice))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getSlotPrice()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
// MetaHelper.GameConfigMeta.UGQDefaultSlot
|
||||
if (Enum.TryParse(MetaHelper.GameConfigMeta.UGQAddSlotType, true, out CurrencyType currencyType) == false)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
||||
|
||||
double slotPrice = MetaHelper.GameConfigMeta.UGQAddSlotAmount;
|
||||
|
||||
return Results.Ok(new UGQSlotPrice
|
||||
{
|
||||
CurrencyType = currencyType,
|
||||
Price = slotPrice
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 슬롯 추가
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[Route("slot")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQAccount))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> addSlot()
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
if (Enum.TryParse(MetaHelper.GameConfigMeta.UGQAddSlotType, true, out CurrencyType currencyType) == false)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
||||
|
||||
double slotPrice = MetaHelper.GameConfigMeta.UGQAddSlotAmount;
|
||||
|
||||
var accountEntity = await _accountService.getAccount(userGuid);
|
||||
if (accountEntity == null)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
|
||||
int finalCount = MetaHelper.GameConfigMeta.UGQDefaultSlot + accountEntity.AdditionalSlotCount + 1;
|
||||
if (finalCount > MetaHelper.GameConfigMeta.UGQMaximumSlot)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqSlotLimit));
|
||||
|
||||
var result = new Result();
|
||||
|
||||
var db_connector = CurrencyControlHelper.getDbConnector();
|
||||
(result, var is_login) = await ServerCommon.EntityHelper.isUserLoggedIn(db_connector, userGuid);
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
||||
if (true == is_login)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqServerException));
|
||||
|
||||
// 일단 슬롯 추가할 수 있음
|
||||
|
||||
// 게임 DB 재화 체크, 소모
|
||||
(result, double currentAmount) = await CurrencyControlHelper.getMoneyByUserGuid(userGuid, currencyType);
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqCurrencyError));
|
||||
|
||||
if (currentAmount < slotPrice)
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNotEnoughCurrency));
|
||||
|
||||
(result, double updatedAmount) = await CurrencyControlHelper.changeMoneyByUserGuid(userGuid, currencyType, slotPrice);
|
||||
if (result.isFail())
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqCurrencyError));
|
||||
|
||||
(ServerErrorCode errorCode, var updatedAccountEntity) = await _accountService.addSlotCount(userGuid, 1, accountEntity.SlotCountVersion);
|
||||
if (errorCode != ServerErrorCode.Success)
|
||||
return Results.BadRequest(ApiResponseFactory.error(errorCode));
|
||||
|
||||
if (updatedAccountEntity == null)
|
||||
{
|
||||
// 슬롯 추가 실패
|
||||
// 슬롯수 획인, 재화 소모 처리후 SlotCountVersion이 변경되었으면 슬롯 수 변경이 저장되지 않고 실패함.
|
||||
// 재화는 돌려준다.
|
||||
(result, double rollbackAmount) = await CurrencyControlHelper.earnMoneyByUserGuid(userGuid, currencyType, slotPrice);
|
||||
if (result.isFail())
|
||||
{
|
||||
Log.getLogger().error($"currency rollback error. userGuid: {userGuid}, currencyType: {currencyType}, slotPrice: {slotPrice}");
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqCurrencyError));
|
||||
}
|
||||
|
||||
return Results.BadRequest(ApiResponseFactory.error(ServerErrorCode.UgqNullEntity));
|
||||
}
|
||||
|
||||
List<ILogInvoker> business_logs = [
|
||||
new UgqApiAddSlotBusinessLog(updatedAccountEntity.AdditionalSlotCount, ""),
|
||||
];
|
||||
var log_action = new LogActionEx(LogActionType.UgqApiAddSlot);
|
||||
UgqApiBusinessLogger.collectLogs(log_action, userGuid, business_logs);
|
||||
|
||||
return Results.Ok(updatedAccountEntity.ToDTO());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 생성
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 빈 슬롯에 퀘스트 추가하는 경우에 호출
|
||||
/// </remarks>
|
||||
[HttpPost]
|
||||
[Route("quest")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> addQuest([FromBody] UGQSaveQuestModel saveQuestModel)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.addQuest(userGuid, saveQuestModel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 내용 가져오기
|
||||
/// </summary>
|
||||
/// <param name="questContentId">UGQSummary의 QuestContentId 값으로 api 호출</param>
|
||||
[HttpGet]
|
||||
[Route("quest/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getQuest(string questContentId)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.getQuest(userGuid, questContentId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 편집 가능한 경우 퀘스트 내용 가져오기
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// GET api와는 다르게 편집 불가능한 state인 경우에는 실패 리턴
|
||||
/// </remarks>
|
||||
/// <param name="questContentId">UGQSummary의 QuestContentId 값으로 api 호출</param>
|
||||
[HttpPost]
|
||||
[Route("quest/{questContentId}/edit")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> editQuest(string questContentId)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.editQuest(userGuid, questContentId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 내용 업데이트
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Task의 DialogId는 업데이트하지 않음.
|
||||
/// 편집 불가능 state인 경우 에러 리턴
|
||||
/// </remarks>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="saveQuestModel">웹에서 입력한 값</param>
|
||||
[HttpPut]
|
||||
[Route("quest/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> saveQuest(string questContentId, [FromBody] UGQSaveQuestModel saveQuestModel)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.saveQuest(userGuid, questContentId, saveQuestModel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 삭제
|
||||
/// </summary>
|
||||
/// <param name="questContentId">GET또는 edit 에서 얻은 questContentId 사용</param>
|
||||
[HttpDelete]
|
||||
[Route("quest/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK)]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> deleteQuest(string questContentId)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.deleteQuest(userGuid, questContentId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 이미지 업로드
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 세부사항은 정리 예정
|
||||
/// </remarks>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="titleImageId">기본 이미지 인덱스</param>
|
||||
/// <param name="bannerImageId">기본 이미지 인덱스</param>
|
||||
/// <param name="title">업로드 파일</param>
|
||||
/// <param name="banner">업로드 파일</param>
|
||||
[HttpPost]
|
||||
[Route("quest-image/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> uploadQuestImage(string questContentId, [FromQuery] int? titleImageId, [FromQuery] int? bannerImageId, IFormFile? title, IFormFile? banner)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.uploadQuestImage(userGuid, questContentId, titleImageId ?? 0, bannerImageId ?? 0, title, banner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 타이틀 이미지 업로드
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 세부사항은 정리 예정
|
||||
/// </remarks>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="titleImageId">기본 이미지 인덱스</param>
|
||||
/// <param name="title">업로드 파일</param>
|
||||
[HttpPost]
|
||||
[Route("quest-title-image/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> uploadQuestTitleImage(string questContentId, [FromQuery] int? titleImageId, IFormFile? title)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.uploadQuestImage(userGuid, questContentId, titleImageId ?? 0, 0, title, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 배너 이미지 업로드
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 세부사항은 정리 예정
|
||||
/// </remarks>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="bannerImageId">기본 이미지 인덱스</param>
|
||||
/// <param name="banner">업로드 파일</param>
|
||||
[HttpPost]
|
||||
[Route("quest-banner-image/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
public async Task<IResult> uploadQuestBannerImage(string questContentId, [FromQuery] int? bannerImageId, IFormFile? banner)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.uploadQuestImage(userGuid, questContentId, 0, bannerImageId ?? 0, null, banner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 다이얼로그 가져오기
|
||||
/// </summary>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="dialogId">UGQContent.Tasks에 있는 DialogId 사용</param>
|
||||
[HttpGet]
|
||||
[Route("quest-dialog/{questContentId}/{dialogId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQDialog))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getQuestDialog(string questContentId, string dialogId)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.getQuestDialog(userGuid, questContentId, dialogId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 다이얼로그 새로 생성
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Dialog 를 추가하고, UGQContent.Tasks의 DialogId를 저장
|
||||
/// </remarks>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="taskIndex">몇번째 Task에 추가되는 다이얼로그인지. 0부터 시작</param>
|
||||
/// <param name="questDialog">questDialog</param>
|
||||
[HttpPost]
|
||||
[Route("quest-dialog/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQAddQuestDialogResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> addQuestDialog(string questContentId, [FromQuery] int taskIndex, [FromBody] UGQSaveDialogModel questDialog)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.addQuestDialog(userGuid, questContentId, taskIndex, questDialog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 다이얼로그 업데이트
|
||||
/// </summary>
|
||||
/// <param name="questContentId">GET 또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="dialogId">UGQContent.Tasks에 있는 DialogId 사용</param>
|
||||
/// /// <param name="questDialog">questDialog</param>
|
||||
[HttpPut]
|
||||
[Route("quest-dialog/{questContentId}/{dialogId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQDialog))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> saveQuestDialog(string questContentId, string dialogId, [FromBody] UGQSaveDialogModel questDialog)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.saveQuestDialog(userGuid, questContentId, dialogId, questDialog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트 state 변경
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Test 로 변경 실패 시 ValidationErrorResponse로 응답
|
||||
/// </remarks>
|
||||
/// <param name="questContentId">GET또는 edit 에서 얻은 questContentId 사용</param>
|
||||
/// <param name="state">QuestContentState</param>
|
||||
[HttpPatch]
|
||||
[Route("quest-state/{questContentId}")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQContent))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ValidationErrorResponse))]
|
||||
public async Task<IResult> changeQuestState(string questContentId, [FromQuery] QuestContentState state)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.changeQuestState(userGuid, questContentId, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CreatorPoint 내역 보기
|
||||
/// </summary>
|
||||
/// <param name="pageNumber">페이지</param>
|
||||
/// <param name="pageSize">페이지 사이즈 (최대 100)</param>
|
||||
/// <param name="kind">kind</param>
|
||||
/// <param name="startDate">검색할 시작 시간</param>
|
||||
/// <param name="endDate">검색할 종료 시간</param>
|
||||
[HttpGet]
|
||||
[Route("creator-point-history")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQCreatorPointHistoryResult))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getCreatorPointHistory([FromQuery] int pageNumber, [FromQuery] int pageSize, CreatorPointHistoryKind kind, DateTimeOffset startDate, DateTimeOffset endDate)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.getCreatorPointHistory(userGuid, pageNumber, pageSize, kind, startDate, endDate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 퀘스트별 수익 보기
|
||||
/// </summary>
|
||||
/// <param name="pageNumber">페이지</param>
|
||||
/// <param name="pageSize">페이지 사이즈 (최대 100)</param>
|
||||
[HttpGet]
|
||||
[Route("quest-profit-stats")]
|
||||
[Produces("application/json")]
|
||||
[SwaggerResponse(StatusCodes.Status200OK, Type = typeof(UGQQuestProfitStatsResult))]
|
||||
[SwaggerResponse(StatusCodes.Status400BadRequest, Type = typeof(ApiErrorResponse))]
|
||||
public async Task<IResult> getQuestProfitStats([FromQuery] int pageNumber, [FromQuery] int pageSize)
|
||||
{
|
||||
var userGuid = userGuidFromToken();
|
||||
if (userGuid == null)
|
||||
return Results.Unauthorized();
|
||||
|
||||
return await _questEditorApi.getQuestProfitStats(userGuid, pageNumber, pageSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user