초기커밋
This commit is contained in:
66
UGQApiServer/AllowWhenSingleLoginFilter.cs
Normal file
66
UGQApiServer/AllowWhenSingleLoginFilter.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Globalization;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace UGQApiServer;
|
||||
|
||||
public class AllowWhenSingleLoginAttribute : TypeFilterAttribute
|
||||
{
|
||||
public AllowWhenSingleLoginAttribute() : base(typeof(AllowWhenSingleLoginFilter))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class AllowWhenSingleLoginFilter : IAsyncAuthorizationFilter
|
||||
{
|
||||
readonly IConfiguration _configuration;
|
||||
readonly IHttpContextAccessor _httpContextAccessor;
|
||||
readonly IConnectionMultiplexer _redis;
|
||||
|
||||
bool EnableAllowWhenSingleLogin = true;
|
||||
|
||||
|
||||
public AllowWhenSingleLoginFilter(IConfiguration configuration,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IConnectionMultiplexer redis)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_redis = redis;
|
||||
|
||||
|
||||
EnableAllowWhenSingleLogin = _configuration.GetValue<bool>("EnableAllowWhenSingleLogin", true);
|
||||
}
|
||||
|
||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||
{
|
||||
if (EnableAllowWhenSingleLogin == false)
|
||||
return;
|
||||
|
||||
var user_guid = context.HttpContext.User.Claims.FirstOrDefault(c => c.Type == "Id")?.Value;
|
||||
if (user_guid == null)
|
||||
{
|
||||
context.Result = new UnauthorizedObjectResult(string.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
var game_login_cache = await _redis.GetDatabase().StringGetAsync($"login:{user_guid}");
|
||||
if (game_login_cache.HasValue == true)
|
||||
{
|
||||
context.Result = new BadRequestObjectResult(ApiResponseFactory.error(ServerErrorCode.UgqMetaverseOnline));
|
||||
return;
|
||||
}
|
||||
|
||||
var ugq_login_cache = await _redis.GetDatabase().StringGetAsync($"ugq_login:{user_guid}");
|
||||
if(ugq_login_cache.HasValue == false)
|
||||
{
|
||||
context.Result = new BadRequestObjectResult(ApiResponseFactory.error(ServerErrorCode.UgqAuthRemoved));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
142
UGQApiServer/ApiFilter.cs
Normal file
142
UGQApiServer/ApiFilter.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace UGQApiServer;
|
||||
|
||||
public enum UgqApiType
|
||||
{
|
||||
None,
|
||||
UgqAdmin,
|
||||
UgqIngame,
|
||||
UgqApi,
|
||||
UgqAllInOne
|
||||
}
|
||||
|
||||
public class ApiAllowSettings
|
||||
{
|
||||
public bool AllowInGameApi { get; init; } = false;
|
||||
public bool AllowWebApi { get; init; } = false;
|
||||
public bool AllowAdminApi { get; init; } = false;
|
||||
public ServerType ServerType { get; init; } = ServerType.None;
|
||||
|
||||
public ApiAllowSettings(UgqApiType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case UgqApiType.UgqAdmin:
|
||||
AllowAdminApi = true;
|
||||
ServerType = ServerType.UgqAdmin;
|
||||
break;
|
||||
case UgqApiType.UgqApi:
|
||||
AllowWebApi = true;
|
||||
ServerType = ServerType.UgqApi;
|
||||
break;
|
||||
case UgqApiType.UgqIngame:
|
||||
AllowInGameApi = true;
|
||||
ServerType = ServerType.UgqIngame;
|
||||
break;
|
||||
case UgqApiType.UgqAllInOne:
|
||||
AllowAdminApi = true;
|
||||
AllowWebApi = true;
|
||||
AllowInGameApi = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class UGQInGameApiAttribute : Attribute, IResourceFilter
|
||||
{
|
||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
var settings = context.HttpContext.RequestServices.GetService<ApiAllowSettings>();
|
||||
if (settings == null)
|
||||
{
|
||||
context.Result = new NotFoundResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(settings.AllowInGameApi == false)
|
||||
context.Result = new NotFoundResult();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class UGQWebApiAttribute : Attribute, IResourceFilter
|
||||
{
|
||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
var settings = context.HttpContext.RequestServices.GetService<ApiAllowSettings>();
|
||||
if (settings == null)
|
||||
{
|
||||
context.Result = new NotFoundResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (settings.AllowWebApi == false)
|
||||
context.Result = new NotFoundResult();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class UGQAdminApiAttribute : Attribute, IResourceFilter
|
||||
{
|
||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
var settings = context.HttpContext.RequestServices.GetService<ApiAllowSettings>();
|
||||
if (settings == null)
|
||||
{
|
||||
context.Result = new NotFoundResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (settings.AllowAdminApi == false)
|
||||
context.Result = new NotFoundResult();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class DevelopmentOnlyAttribute : Attribute, IResourceFilter
|
||||
{
|
||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
bool cmd_develop = false;
|
||||
|
||||
var cmdOptions = context.HttpContext.RequestServices.GetService<CommandLineOptions>();
|
||||
if (cmdOptions != null)
|
||||
{
|
||||
cmd_develop = cmdOptions.m_develop;
|
||||
}
|
||||
|
||||
bool isDevelopment = false;
|
||||
var env = context.HttpContext.RequestServices.GetService<IWebHostEnvironment>();
|
||||
if (env != null)
|
||||
{
|
||||
isDevelopment = env.IsDevelopment() || (env.EnvironmentName == "AWSDev") || cmd_develop;
|
||||
}
|
||||
|
||||
if (isDevelopment == false)
|
||||
context.Result = new NotFoundResult();
|
||||
}
|
||||
}
|
||||
|
||||
45
UGQApiServer/ApiResponseFactory.cs
Normal file
45
UGQApiServer/ApiResponseFactory.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Globalization;
|
||||
using UGQApiServer.Converter;
|
||||
using UGQApiServer.Models;
|
||||
using UGQDataAccess;
|
||||
using ServerCommon;
|
||||
using ServerCommon.UGQ.Models;
|
||||
using UGQApiServer.UGQData;
|
||||
|
||||
namespace UGQApiServer;
|
||||
|
||||
public static class ApiResponseFactory
|
||||
{
|
||||
public static ApiErrorResponse error(ServerErrorCode errorCode)
|
||||
{
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
|
||||
var textId = errorCode.ToString().Replace("Ugq", "UGQ_Error_");
|
||||
var message = UGQDataHelper.getText(langEnum, textId);
|
||||
|
||||
return new ApiErrorResponse
|
||||
{
|
||||
ErrorCode = (int)errorCode,
|
||||
ErrorMessage = message ?? errorCode.ToString(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static ValidationErrorResponse validationError(ServerErrorCode errorCode, List<string> errors)
|
||||
{
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
|
||||
var textId = errorCode.ToString().Replace("Ugq", "UGQ_Error_");
|
||||
var message = UGQDataHelper.getText(langEnum, textId);
|
||||
|
||||
return new ValidationErrorResponse
|
||||
{
|
||||
ErrorCode = (int)errorCode,
|
||||
ErrorMessage = message ?? errorCode.ToString(),
|
||||
ValidationErrorMessages = errors
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
13
UGQApiServer/Appsettings.cs
Normal file
13
UGQApiServer/Appsettings.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace UGQApiServer;
|
||||
|
||||
public class Default
|
||||
{
|
||||
public const int DefaultPort = 11000;
|
||||
}
|
||||
|
||||
public class NamedPipeConf
|
||||
{
|
||||
public static string m_name { get; set; } = "NamedPipe";
|
||||
|
||||
[ConfigurationKeyName("enable")] public bool m_enable { get; set; } = false;
|
||||
}
|
||||
76
UGQApiServer/Auth/AuthSql.cs
Normal file
76
UGQApiServer/Auth/AuthSql.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
using MySqlConnector;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
using ServerCommon.BusinessLogDomain;
|
||||
using MetaAssets;
|
||||
|
||||
|
||||
|
||||
namespace UGQApiServer.Auth
|
||||
{
|
||||
public class AuthSql
|
||||
{
|
||||
readonly string _ssoAccountDb;
|
||||
|
||||
public AuthSql(IConfiguration configuration)
|
||||
{
|
||||
_ssoAccountDb = configuration["SSOAccount:SsoAccountDb"] ?? "";
|
||||
}
|
||||
|
||||
public async Task<ulong> getAccountIdFromMysql(string email)
|
||||
{
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
bool is_found_account_id = false;
|
||||
ulong account_id = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var read_func = delegate (MySqlDataReader dataReader)
|
||||
{
|
||||
is_found_account_id = true;
|
||||
account_id = dataReader.GetUInt64("id");
|
||||
return ServerErrorCode.Success;
|
||||
};
|
||||
|
||||
var query = $"SELECT * FROM wallet_user WHERE email = '{email}'";
|
||||
Console.WriteLine(query);
|
||||
|
||||
// 1. 계정 존재 여부와 AccessToken의 동일 여부를 통합인증DB의 정보 참조하여 체크 한다.
|
||||
result = await MySqlConnectorHelper.simpleQueryExecuteForReaderAsync(
|
||||
$"SELECT * FROM wallet_user WHERE email = '{email}'", read_func, _ssoAccountDb);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to simpleQueryExecuteForReaderAsync() for SsoAccountDb !!! : email:{email}, {result.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return 0;
|
||||
}
|
||||
if (false == is_found_account_id)
|
||||
{
|
||||
err_msg = $"Not found Account ID in SsoAccountDb !!! : email:{email}";
|
||||
result.setFail(ServerErrorCode.AccountIdNotFoundInSsoAccountDb, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
err_msg = $"Failed to query SsoAccountDb !!! : Exception:{e} - AccountId:{account_id}";
|
||||
result.setFail(ServerErrorCode.MySqlDbQueryException, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return account_id;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
160
UGQApiServer/Auth/WebPortalTokenAuth.cs
Normal file
160
UGQApiServer/Auth/WebPortalTokenAuth.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System.Text;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
|
||||
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using MySqlConnector;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
namespace UGQApiServer.Auth;
|
||||
|
||||
public class WebPortalToken
|
||||
{
|
||||
public string AccountId { get; set; } = "";
|
||||
public AccountType AccountType { get; set; }
|
||||
public ulong AccessToken { get; set; }
|
||||
}
|
||||
|
||||
public class WebPortalTokenAuth
|
||||
{
|
||||
readonly string _ssoAccountDb;
|
||||
readonly string _webPortalTokenSecret;
|
||||
|
||||
public string WebPortalTokenSecret => _webPortalTokenSecret;
|
||||
|
||||
public WebPortalTokenAuth(IConfiguration configuration)
|
||||
{
|
||||
_ssoAccountDb = configuration["SSOAccount:SsoAccountDb"] ?? "";
|
||||
_webPortalTokenSecret = configuration["SSOAccount:WebPortalTokenSecret"] ?? "";
|
||||
}
|
||||
|
||||
public async Task<ServerErrorCode> mysqlAuth(WebPortalToken token)
|
||||
{
|
||||
// db <20>ּ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ó<><C3B3>
|
||||
if (string.IsNullOrEmpty(_ssoAccountDb) == true)
|
||||
return ServerErrorCode.Success;
|
||||
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var account_id = token.AccountId;
|
||||
var access_tocken = token.AccessToken;
|
||||
|
||||
try
|
||||
{
|
||||
var is_found_account_id = false;
|
||||
string email = string.Empty;
|
||||
UInt64 read_access_token = 0;
|
||||
var read_func = delegate (MySqlDataReader dataReader)
|
||||
{
|
||||
is_found_account_id = true;
|
||||
|
||||
email = dataReader.GetString("email");
|
||||
read_access_token = dataReader.GetUInt64("access_token");
|
||||
|
||||
return ServerErrorCode.Success;
|
||||
};
|
||||
|
||||
// 1. <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>ο<EFBFBD> AccessToken<65><6E> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>θ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>DB<44><42> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>Ͽ<EFBFBD> üũ <20>Ѵ<EFBFBD>.
|
||||
result = await MySqlConnectorHelper.simpleQueryExecuteForReaderAsync(
|
||||
$"SELECT * FROM wallet_user WHERE id = {account_id}", read_func, _ssoAccountDb);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to simpleQueryExecuteForReaderAsync() for SsoAccountDb !!! : AccountId:{account_id}, {result.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return result.ErrorCode;
|
||||
}
|
||||
if (false == is_found_account_id)
|
||||
{
|
||||
err_msg = $"Not found Account ID in SsoAccountDb !!! : AccountId:{account_id}";
|
||||
result.setFail(ServerErrorCode.AccountIdNotFoundInSsoAccountDb, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return result.ErrorCode;
|
||||
}
|
||||
if (read_access_token != access_tocken)
|
||||
{
|
||||
err_msg = $"Not match AccessToken in SsoAccountDb !!! : JWT:{access_tocken} == SsoAccountDb:{read_access_token} - AccountId:{account_id}, email:{email}";
|
||||
result.setFail(ServerErrorCode.AccessTokenNotMatchInSsoAccountDb, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return result.ErrorCode;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
err_msg = $"Failed to query SsoAccountDb !!! : Exception:{e} - AccountId:{account_id}";
|
||||
result.setFail(ServerErrorCode.MySqlDbQueryException, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return result.ErrorCode;
|
||||
}
|
||||
|
||||
return ServerErrorCode.Success;
|
||||
}
|
||||
|
||||
public async Task<ServerErrorCode> mysqlAuth_igm(WebPortalToken token)
|
||||
{
|
||||
// db <20>ּ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ó<><C3B3>
|
||||
if (string.IsNullOrEmpty(_ssoAccountDb) == true)
|
||||
return ServerErrorCode.Success;
|
||||
|
||||
var result = new Result();
|
||||
var err_msg = string.Empty;
|
||||
|
||||
var account_id = token.AccountId;
|
||||
var access_tocken = token.AccessToken;
|
||||
|
||||
try
|
||||
{
|
||||
var is_found_account_id = false;
|
||||
string email = string.Empty;
|
||||
UInt64 read_access_token = 0;
|
||||
var read_func = delegate (MySqlDataReader dataReader)
|
||||
{
|
||||
is_found_account_id = true;
|
||||
|
||||
email = dataReader.GetString("email");
|
||||
read_access_token = dataReader.GetUInt64("access_igm_token");
|
||||
|
||||
return ServerErrorCode.Success;
|
||||
};
|
||||
|
||||
// 1. <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>ο<EFBFBD> AccessToken<65><6E> <20><><EFBFBD><EFBFBD> <20><><EFBFBD>θ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>DB<44><42> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>Ͽ<EFBFBD> üũ <20>Ѵ<EFBFBD>.
|
||||
result = await MySqlConnectorHelper.simpleQueryExecuteForReaderAsync(
|
||||
$"SELECT * FROM wallet_user WHERE id = {account_id}", read_func, _ssoAccountDb);
|
||||
if (result.isFail())
|
||||
{
|
||||
err_msg = $"Failed to simpleQueryExecuteForReaderAsync() for SsoAccountDb !!! : AccountId:{account_id}, {result.toBasicString()}";
|
||||
Log.getLogger().error(err_msg);
|
||||
return result.ErrorCode;
|
||||
}
|
||||
if (false == is_found_account_id)
|
||||
{
|
||||
err_msg = $"Not found Account ID in SsoAccountDb !!! : AccountId:{account_id}";
|
||||
result.setFail(ServerErrorCode.AccountIdNotFoundInSsoAccountDb, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return result.ErrorCode;
|
||||
}
|
||||
if (read_access_token != access_tocken)
|
||||
{
|
||||
err_msg = $"Not match AccessToken in SsoAccountDb !!! : JWT:{access_tocken} == SsoAccountDb:{read_access_token} - AccountId:{account_id}, email:{email}";
|
||||
result.setFail(ServerErrorCode.AccessTokenNotMatchInSsoAccountDb, err_msg);
|
||||
Log.getLogger().error(err_msg);
|
||||
return result.ErrorCode;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
err_msg = $"Failed to query SsoAccountDb !!! : Exception:{e} - AccountId:{account_id}";
|
||||
result.setFail(ServerErrorCode.MySqlDbQueryException, err_msg);
|
||||
Log.getLogger().error(result.toBasicString());
|
||||
|
||||
return result.ErrorCode;
|
||||
}
|
||||
|
||||
return ServerErrorCode.Success;
|
||||
}
|
||||
}
|
||||
97
UGQApiServer/BackGroundService/OnTimeBackGroundService.cs
Normal file
97
UGQApiServer/BackGroundService/OnTimeBackGroundService.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
namespace UGQApiServer.BackGroundService;
|
||||
|
||||
public static class BackgroundServiceExtensions
|
||||
{
|
||||
public static IServiceCollection AddOnTimeBackgroundService(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<OnTimeBackGroundService>();
|
||||
services.AddHostedService(provider => provider.GetRequiredService<OnTimeBackGroundService>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddOnTimeTask(this IServiceCollection services, string taskName, DateTime onTime, Task task)
|
||||
{
|
||||
var provider = services.BuildServiceProvider();
|
||||
var service = provider.GetRequiredService<OnTimeBackGroundService>();
|
||||
|
||||
service.addOnTimeTask(taskName, onTime, task);
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
public class OnTimeBackGroundService : BackgroundService
|
||||
{
|
||||
private const int BaseDelayMs = 1_000;
|
||||
private PeriodicTimer? m_timer { get; set; }
|
||||
|
||||
private Dictionary<string, (Task task, DateTime time)> m_onTime_tasks { get; set; } = new();
|
||||
|
||||
public OnTimeBackGroundService() {}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
m_timer?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public bool addOnTimeTask(string taskName, DateTime onTime, Task action)
|
||||
{
|
||||
return m_onTime_tasks.TryAdd(taskName, (action, onTime));
|
||||
}
|
||||
|
||||
public bool deleteOnTimeTask(string taskName)
|
||||
{
|
||||
return m_onTime_tasks.Remove(taskName);
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
CreateTimer();
|
||||
|
||||
try
|
||||
{
|
||||
if (!await m_timer!.WaitForNextTickAsync(stoppingToken).ConfigureAwait(false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
await RunAsync().ConfigureAwait(false);
|
||||
|
||||
if (!await m_timer!.WaitForNextTickAsync(stoppingToken).ConfigureAwait(false))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.getLogger().error($"Background Exception : {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunAsync()
|
||||
{
|
||||
var current = DateTime.UtcNow;
|
||||
|
||||
foreach (var task in m_onTime_tasks)
|
||||
{
|
||||
var on_time = task.Value.time;
|
||||
if (current.Hour != on_time.Hour || current.Minute != on_time.Minute || current.Second != on_time.Second) continue;
|
||||
|
||||
// Fire and Forget
|
||||
_ = Task.Run(() => task.Value.task);
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void CreateTimer() => m_timer = new PeriodicTimer(TimeSpan.FromMilliseconds(BaseDelayMs));
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using UGQDataAccess.Service;
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
public class ReserveAccountGradeBackGroundService : BackgroundService
|
||||
{
|
||||
private const int BaseDelayMs = 1 * 60 * 1_000;
|
||||
private PeriodicTimer? m_timer;
|
||||
private AccountService _accountService;
|
||||
|
||||
public ReserveAccountGradeBackGroundService(AccountService accountService)
|
||||
{
|
||||
_accountService = accountService;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
m_timer?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
CreateTimer();
|
||||
|
||||
try
|
||||
{
|
||||
if (!await m_timer!.WaitForNextTickAsync(stoppingToken).ConfigureAwait(false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
await RunAsync().ConfigureAwait(false);
|
||||
|
||||
if (!await m_timer!.WaitForNextTickAsync(stoppingToken).ConfigureAwait(false))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.getLogger().error($"Background Exception : {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTimer() => m_timer = new PeriodicTimer(TimeSpan.FromMilliseconds(BaseDelayMs));
|
||||
|
||||
protected async Task RunAsync()
|
||||
{
|
||||
var list = await _accountService.completeReserveAccountGrade();
|
||||
|
||||
foreach(var reserveAccount in list)
|
||||
{
|
||||
await _accountService.modifyAccountGrade(reserveAccount.UserGuid, reserveAccount.ReserveGradeType);
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26
UGQApiServer/Converter/ConverterCommon.cs
Normal file
26
UGQApiServer/Converter/ConverterCommon.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using UGQDatabase.Models;
|
||||
|
||||
namespace UGQApiServer.Converter;
|
||||
|
||||
public static class ConverterCommon
|
||||
{
|
||||
public static List<string> buildLanguages(TextEntity titleText)
|
||||
{
|
||||
List<string> languages = new List<string>();
|
||||
|
||||
if (string.IsNullOrEmpty(titleText.En) == false)
|
||||
languages.Add("en");
|
||||
|
||||
if (string.IsNullOrEmpty(titleText.Kr) == false)
|
||||
languages.Add("kr");
|
||||
|
||||
if (string.IsNullOrEmpty(titleText.Jp) == false)
|
||||
languages.Add("jp");
|
||||
|
||||
if (languages.Count == 0)
|
||||
languages.Add("en");
|
||||
|
||||
return languages;
|
||||
}
|
||||
|
||||
}
|
||||
179
UGQApiServer/Converter/InGameConverter.cs
Normal file
179
UGQApiServer/Converter/InGameConverter.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using ServerCommon;
|
||||
using ServerCommon.UGQ.Models;
|
||||
using UGQApiServer.Storage;
|
||||
using UGQApiServer.UGQData;
|
||||
using UGQDataAccess.Repository.Models;
|
||||
using UGQDatabase.Models;
|
||||
|
||||
namespace UGQApiServer.Converter;
|
||||
|
||||
|
||||
public enum ImageUrlType
|
||||
{
|
||||
TitleImg,
|
||||
BannerImg,
|
||||
}
|
||||
|
||||
public static class InGameConverter
|
||||
{
|
||||
static IStorageService _storageService = null!;
|
||||
static UGQMetaData _ugqMetadata = null!;
|
||||
|
||||
public static void init(IStorageService storageService, UGQMetaData ugqMetadata)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_ugqMetadata = ugqMetadata;
|
||||
}
|
||||
|
||||
|
||||
static BoolType ToProto(this bool value)
|
||||
{
|
||||
return (value == true) ? BoolType.True : BoolType.False;
|
||||
}
|
||||
|
||||
public static string GetLangText(this TextEntity entity, LangEnum langEnum)
|
||||
{
|
||||
string text = string.Empty;
|
||||
switch(langEnum)
|
||||
{
|
||||
case LangEnum.Ko:
|
||||
text = entity.Kr;
|
||||
break;
|
||||
case LangEnum.Jp:
|
||||
text = entity.Jp;
|
||||
break;
|
||||
case LangEnum.En:
|
||||
text = entity.En;
|
||||
break;
|
||||
}
|
||||
|
||||
if (text == string.Empty)
|
||||
text = entity.En;
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
public static UgqQuestInTestState ToProtoUgqQuestInTestState(this QuestContentEntity result, LangEnum langEnum, ImageUrlType ImgUrlType)
|
||||
{
|
||||
string ImagePath = "";
|
||||
if (ImgUrlType == ImageUrlType.TitleImg)
|
||||
ImagePath = _storageService.fileUrl(result.TitleImagePath);
|
||||
else if (ImgUrlType == ImageUrlType.BannerImg)
|
||||
ImagePath = _storageService.fileUrl(result.BannerImagePath);
|
||||
|
||||
return new UgqQuestInTestState
|
||||
{
|
||||
//ComposedQuestId = result.QuestId << 32 | result.Revision,
|
||||
ComposedQuestId = QuestHelper.convertQuestIdAndRevisionToUgqQuestId((UInt32)result.QuestId, (UInt32)result.Revision),
|
||||
Title = result.Title.GetLangText(langEnum),
|
||||
TitleImagePath = ImagePath,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static UgqBoardSearchResult ToProto(this QuestBoardQueryResult result, LangEnum langEnum, ImageUrlType ImgUrlType)
|
||||
{
|
||||
return new UgqBoardSearchResult
|
||||
{
|
||||
PageNumber = result.PageNumber,
|
||||
PageSize = result.PageSize,
|
||||
TotalPages = result.TotalPages,
|
||||
Items = { result.Items.Select(x => x.ToProto(langEnum, ImgUrlType)).ToList() }
|
||||
};
|
||||
}
|
||||
|
||||
public static UgqBoardSportlightResult ToProto(this QuestBoardSportlightQueryResult result, LangEnum langEnum, ImageUrlType ImgUrlType)
|
||||
{
|
||||
return new UgqBoardSportlightResult
|
||||
{
|
||||
MostLiked = new DateRangeUgqBoardItem
|
||||
{
|
||||
Today = result.MostLiked.Today?.ToProto(langEnum, ImgUrlType),
|
||||
ThisWeek = result.MostLiked.ThisWeek?.ToProto(langEnum, ImgUrlType),
|
||||
ThisMonth = result.MostLiked.ThisMonth?.ToProto(langEnum, ImgUrlType),
|
||||
},
|
||||
MostBookmarked = new DateRangeUgqBoardItem
|
||||
{
|
||||
Today = result.MostBookmarked.Today?.ToProto(langEnum, ImgUrlType),
|
||||
ThisWeek = result.MostBookmarked.ThisWeek?.ToProto(langEnum, ImgUrlType),
|
||||
ThisMonth = result.MostBookmarked.ThisMonth?.ToProto(langEnum, ImgUrlType),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static UgqBoardItem ToProto(this QuestBoardItemResult result, LangEnum langEnum, ImageUrlType ImgUrlType)
|
||||
{
|
||||
var compoesed_quest_id = QuestHelper.convertQuestIdAndRevisionToUgqQuestId((UInt32)result.QuestId, (UInt32)result.Revision);
|
||||
string ImagePath = "";
|
||||
if (ImgUrlType == ImageUrlType.TitleImg)
|
||||
ImagePath = _storageService.fileUrl(result.TitleImagePath);
|
||||
else if (ImgUrlType == ImageUrlType.BannerImg)
|
||||
ImagePath = _storageService.fileUrl(result.BannerImagePath);
|
||||
|
||||
return new UgqBoardItem
|
||||
{
|
||||
//ComposedQuestId = result.QuestId << 32 | result.Revision,
|
||||
ComposedQuestId = compoesed_quest_id,
|
||||
Title = result.Title.GetLangText(langEnum),
|
||||
TitleImagePath = ImagePath,
|
||||
LikeCount = result.LikeCount,
|
||||
BookmarkCount = result.BookmarkCount,
|
||||
Author = result.Author ?? "",
|
||||
GradeType = result.GradeType,
|
||||
Cost = result.Cost,
|
||||
};
|
||||
}
|
||||
|
||||
public static UgqBoardItemDetail ToProto(this QuestBoardDetailItemResult result, LangEnum langEnum, ImageUrlType ImgUrlType)
|
||||
{
|
||||
string beaconName = "";
|
||||
|
||||
if (result.BeaconId != 0)
|
||||
beaconName = _ugqMetadata.getBeaconName((int)result.BeaconId, langEnum);
|
||||
else if (result.UgcBeaconNickname != null)
|
||||
beaconName = result.UgcBeaconNickname;
|
||||
|
||||
string ImagePath = "";
|
||||
if (ImgUrlType == ImageUrlType.TitleImg)
|
||||
ImagePath = _storageService.fileUrl(result.TitleImagePath);
|
||||
else if (ImgUrlType == ImageUrlType.BannerImg)
|
||||
ImagePath = _storageService.fileUrl(result.BannerImagePath);
|
||||
|
||||
var compoesed_quest_id = QuestHelper.convertQuestIdAndRevisionToUgqQuestId((UInt32)result.QuestId, (UInt32)result.Revision);
|
||||
return new UgqBoardItemDetail
|
||||
{
|
||||
ComposedQuestId = compoesed_quest_id,
|
||||
Title = result.Title.GetLangText(langEnum),
|
||||
TitleImagePath = ImagePath,
|
||||
LikeCount = result.LikeCount,
|
||||
BookmarkCount = result.BookmarkCount,
|
||||
AcceptCount = result.QuestAcceptedCount,
|
||||
CompleteCount = result.QuestCompletedCount,
|
||||
Author = result.Author ?? "",
|
||||
Description = result.Description.GetLangText(langEnum),
|
||||
Langs = { ConverterCommon.buildLanguages(result.Title) },
|
||||
Beacon = beaconName,
|
||||
Liked = result.Liked.ToProto(),
|
||||
Bookmarked = result.Bookmarked.ToProto(),
|
||||
GradeType = result.GradeType,
|
||||
Cost = result.Cost,
|
||||
};
|
||||
}
|
||||
|
||||
public static GameQuestDataSimple ToSimpleDTO(this GameQuestDataEntity entity)
|
||||
{
|
||||
return new GameQuestDataSimple
|
||||
{
|
||||
QuestId = entity.QuestId,
|
||||
Revision = entity.Revision,
|
||||
UserGuid = entity.UserGuid,
|
||||
Author = entity.Author ?? "",
|
||||
Title = entity.Title,
|
||||
GradeType = entity.GradeType,
|
||||
State = entity.State,
|
||||
Cost = entity.Cost,
|
||||
Shutdown = entity.Shutdown,
|
||||
};
|
||||
}
|
||||
}
|
||||
282
UGQApiServer/Converter/UGQConveter.cs
Normal file
282
UGQApiServer/Converter/UGQConveter.cs
Normal file
@@ -0,0 +1,282 @@
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Amazon.S3.Model;
|
||||
|
||||
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
using MetaAssets;
|
||||
|
||||
|
||||
using UGQDataAccess.Repository.Models;
|
||||
using UGQApiServer.Models;
|
||||
using UGQDatabase.Models;
|
||||
using UGQApiServer.Storage;
|
||||
using UGQApiServer.UGQData;
|
||||
using UGQDataAccess.Service;
|
||||
|
||||
|
||||
namespace UGQApiServer.Converter;
|
||||
|
||||
|
||||
public static class UGQConverter
|
||||
{
|
||||
static IStorageService _storageService = null!;
|
||||
static UGQMetaData _ugqMetadata = null!;
|
||||
|
||||
public static void init(IStorageService storageService, UGQMetaData ugqMetadata)
|
||||
{
|
||||
_storageService = storageService;
|
||||
_ugqMetadata = ugqMetadata;
|
||||
}
|
||||
|
||||
public static UGQText ToDTO(this TextEntity entity)
|
||||
{
|
||||
return new UGQText
|
||||
{
|
||||
Kr = entity.Kr,
|
||||
En = entity.En,
|
||||
Jp = entity.Jp,
|
||||
};
|
||||
}
|
||||
|
||||
public static UGQTask ToDTO(this TaskEntity entity, LangEnum langEnum)
|
||||
{
|
||||
return new UGQTask
|
||||
{
|
||||
GoalText = entity.GoalText.ToDTO(),
|
||||
ActionId = entity.ActionId,
|
||||
ActionValue = entity.ActionValue,
|
||||
IsShowNpcLocation = entity.IsShowNpcLocation,
|
||||
UgcActionValueGuid = entity.UgcActionValueGuid,
|
||||
UgcActionValueName = entity.UgcActionValueName,
|
||||
DialogId = entity.DialogId,
|
||||
|
||||
ActionName = _ugqMetadata.getTaskActionName(entity.ActionId, langEnum),
|
||||
ActionValueName = _ugqMetadata.getTaskActionValueName(entity.ActionId, entity.ActionValue, langEnum),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static UGQAccount ToDTO(this AccountEntity entity)
|
||||
{
|
||||
return new UGQAccount
|
||||
{
|
||||
UserGuid = entity.UserGuid,
|
||||
Nickname = entity.Nickname,
|
||||
AccountId = entity.AccountId ?? "",
|
||||
SlotCount = (MetaHelper.GameConfigMeta.UGQDefaultSlot + entity.AdditionalSlotCount),
|
||||
GradeType = entity.GradeType,
|
||||
CreatorPoint = entity.CreatorPoint,
|
||||
};
|
||||
}
|
||||
|
||||
public static UGQAccountItem ToDTO(this AccountItemResult entity)
|
||||
{
|
||||
return new UGQAccountItem
|
||||
{
|
||||
UserGuid = entity.UserGuid,
|
||||
Nickname = entity.Nickname,
|
||||
AccountId = entity.AccountId ?? "",
|
||||
SlotCount = (MetaHelper.GameConfigMeta.UGQDefaultSlot + entity.AdditionalSlotCount),
|
||||
GradeType = entity.GradeType,
|
||||
CreatorPoint = entity.CreatorPoint,
|
||||
QuestCount = entity.QuestCount,
|
||||
CreatedAt = entity.CreatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
public static UGQQuestProfitStats ToDTO(this QuestProfitStatsItemResult result, LangEnum langEnum)
|
||||
{
|
||||
return new UGQQuestProfitStats
|
||||
{
|
||||
QuestContentId = result.QuestContentId,
|
||||
QuestId = result.QuestId,
|
||||
Revision = result.Revision,
|
||||
Title = result.Title.GetLangText(langEnum),
|
||||
TitleImagePath = _storageService.fileUrl(result.TitleImagePath),
|
||||
BannerImagePath = _storageService.fileUrl(result.BannerImagePath),
|
||||
Completed = result.CompletedCount,
|
||||
TotalProfit = result.TotalProfit,
|
||||
};
|
||||
}
|
||||
|
||||
public static UGQDialogSequenceAction ToDTO(this DialogSequenceActionEntity entity, LangEnum langEnum)
|
||||
{
|
||||
return new UGQDialogSequenceAction
|
||||
{
|
||||
Talker = entity.Talker,
|
||||
Type = entity.Type,
|
||||
Talk = entity.Talk.ToDTO(),
|
||||
Condition = entity.Condition,
|
||||
ConditionValue = entity.ConditionValue,
|
||||
NextSequence = entity.NextSequence,
|
||||
NpcAction = entity.NpcAction,
|
||||
|
||||
TypeName = _ugqMetadata.getDialogTypeName(entity.Type, langEnum),
|
||||
ConditioneName = _ugqMetadata.getDialogConditionName(entity.Type, entity.Condition, langEnum),
|
||||
ConditioneValueName = _ugqMetadata.getDialogConditionValueName(entity.Type, entity.Condition, entity.ConditionValue, langEnum),
|
||||
};
|
||||
}
|
||||
|
||||
public static UGQDialogSequence ToDTO(this DialogSequenceEntity entity, LangEnum langEnum)
|
||||
{
|
||||
return new UGQDialogSequence
|
||||
{
|
||||
SequenceId = entity.SequenceId,
|
||||
Actions = entity.Actions.Select(x => x.ToDTO(langEnum)).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
public static UGQPresetImage ToDTO(this UGQPresetImageMetaData data)
|
||||
{
|
||||
return new UGQPresetImage
|
||||
{
|
||||
Id = data.Id,
|
||||
ImagePath = _storageService.fileUrl(data.FileName),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static UGQSummary ToDTO(this SummaryItemResult result, LangEnum langEnum)
|
||||
{
|
||||
string beaconName = "";
|
||||
|
||||
if (result.BeaconId != 0)
|
||||
beaconName = _ugqMetadata.getBeaconName(result.BeaconId, langEnum);
|
||||
else if (result.UgcBeaconNickname != null)
|
||||
beaconName = result.UgcBeaconNickname;
|
||||
|
||||
return new UGQSummary
|
||||
{
|
||||
QuestContentId = result.QuestContentId,
|
||||
QuestId = result.QuestId,
|
||||
Revision = result.Revision,
|
||||
|
||||
UserGuid = result.UserGuid,
|
||||
Author = result.Author,
|
||||
|
||||
BeaconId = result.BeaconId,
|
||||
UgcBeaconGuid = result.UgcBeaconGuid,
|
||||
|
||||
GradeType = result.GradeType,
|
||||
|
||||
Title = result.Title.GetLangText(Culture.ToLangEnum(result.Savelanguage)),
|
||||
Languages = ConverterCommon.buildLanguages(result.Title),
|
||||
TitleImagePath = _storageService.fileUrl(result.TitleImagePath),
|
||||
BannerImagePath = _storageService.fileUrl(result.BannerImagePath),
|
||||
Description = result.Description.GetLangText(Culture.ToLangEnum(result.Savelanguage)),
|
||||
State = result.State,
|
||||
Cost = result.Cost,
|
||||
UpdatedAt = result.UpdatedAt,
|
||||
CreatedAt = result.CreatedAt,
|
||||
Savelanguage = result.Savelanguage,
|
||||
|
||||
Stats = new UGQStats
|
||||
{
|
||||
Accepted = result.AcceptedCount,
|
||||
Completed = result.CompletedCount,
|
||||
Like = result.LikeCount,
|
||||
Bookmark = result.BookmarkCount,
|
||||
Report = result.ReportCount,
|
||||
TotalProfit = result.TotalProfit,
|
||||
},
|
||||
|
||||
BeaconName = beaconName,
|
||||
};
|
||||
}
|
||||
|
||||
public static UGQContent ToDTO(this QuestContentEntity entity, LangEnum langEnum)
|
||||
{
|
||||
string beaconName = "";
|
||||
|
||||
if (entity.BeaconId != 0)
|
||||
beaconName = _ugqMetadata.getBeaconName(entity.BeaconId, langEnum);
|
||||
else if (entity.UgcBeaconNickname != null)
|
||||
beaconName = entity.UgcBeaconNickname;
|
||||
|
||||
return new UGQContent
|
||||
{
|
||||
QuestContentId = entity.Id,
|
||||
QuestId = entity.QuestId,
|
||||
Revision = entity.Revision,
|
||||
BeaconId = entity.BeaconId,
|
||||
UgcBeaconGuid = entity.UgcBeaconGuid,
|
||||
|
||||
GradeType = entity.GradeType,
|
||||
Title = entity.Title.ToDTO(),
|
||||
Languages = ConverterCommon.buildLanguages(entity.Title),
|
||||
TitleImagePath = _storageService.fileUrl(entity.TitleImagePath),
|
||||
BannerImagePath = _storageService.fileUrl(entity.BannerImagePath),
|
||||
Description = entity.Description.ToDTO(),
|
||||
State = entity.State,
|
||||
Cost = entity.Cost,
|
||||
Tasks = entity.Tasks.Select(x => x.ToDTO(langEnum)).ToList(),
|
||||
LastUpdated = entity.UpdatedAt,
|
||||
|
||||
BeaconName = beaconName,
|
||||
};
|
||||
}
|
||||
|
||||
public static UGQDialog ToDTO(this QuestDialogEntity entity, LangEnum langEnum)
|
||||
{
|
||||
return new UGQDialog
|
||||
{
|
||||
DialogId = entity.Id,
|
||||
Sequences = entity.Sequences.Select(x => x.ToDTO(langEnum)).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
public static UGQCreatorPointHistory ToDTO(this CreatorPointHistoryEntity entity, LangEnum langEnum)
|
||||
{
|
||||
return new UGQCreatorPointHistory
|
||||
{
|
||||
Kind = entity.Kind,
|
||||
Amount = entity.Amount,
|
||||
TotalAmount = entity.TotalAmount,
|
||||
CreatedAt = entity.CreatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
public static UGQCreatorPointHistoryResult ToDTO(this CreatorPointHistoryQueryResult result, LangEnum langEnum)
|
||||
{
|
||||
return new UGQCreatorPointHistoryResult
|
||||
{
|
||||
PageNumber = result.PageNumber,
|
||||
PageSize = result.PageSize,
|
||||
TotalPages = result.TotalPages,
|
||||
Items = result.Items.Select(x => x.ToDTO(langEnum)).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
public static UGQReserveAccountGradeResult ToDTO(this ReserveAccountGradeEntity entity)
|
||||
{
|
||||
return new UGQReserveAccountGradeResult
|
||||
{
|
||||
ReserveId = entity.Id.ToString(),
|
||||
UserGuid = entity.UserGuid,
|
||||
CurrentGradeType = entity.BeforeGradeType,
|
||||
ReserveGradeType = entity.ReserveGradeType,
|
||||
ReserveTime = entity.ReserveTime,
|
||||
UpdatedAt = entity.UpdatedAt,
|
||||
IsCompleted = entity.IsCompleted,
|
||||
};
|
||||
}
|
||||
|
||||
public static UGQReserveAccountGradeResult ToDTO(this ReserveAccountGradeItemResult entity)
|
||||
{
|
||||
return new UGQReserveAccountGradeResult
|
||||
{
|
||||
ReserveId = entity.ReserveId,
|
||||
UserGuid = entity.UserGuid,
|
||||
AccountId = entity.AccountId,
|
||||
CurrentGradeType = entity.CurrentGradeType,
|
||||
ReserveGradeType = entity.ReserveGradeType,
|
||||
ReserveTime = entity.ReserveTime,
|
||||
UpdatedAt = entity.UpdatedAt,
|
||||
IsCompleted = entity.IsCompleted,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
154
UGQApiServer/Converter/UGQMetaDataConverter.cs
Normal file
154
UGQApiServer/Converter/UGQMetaDataConverter.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
using MetaAssets;
|
||||
|
||||
|
||||
using UGQApiServer.Models;
|
||||
using UGQApiServer.UGQData;
|
||||
|
||||
|
||||
namespace UGQApiServer.Converter;
|
||||
|
||||
public static class UGQMetaDataConverter
|
||||
{
|
||||
public static UGQNpcMetaData ToNpcLocale(this UGQNpcMetaData npc, LangEnum langEnum)
|
||||
{
|
||||
if (npc.NpcId != null)
|
||||
{
|
||||
var text = UGQDataHelper.getText(langEnum, npc.NpcName);
|
||||
if (text != null)
|
||||
npc.NpcName = text;
|
||||
|
||||
var titleText = UGQDataHelper.getText(langEnum, npc.NpcTitle);
|
||||
if (titleText != null)
|
||||
npc.NpcTitle = titleText;
|
||||
}
|
||||
|
||||
return npc;
|
||||
}
|
||||
|
||||
public static TaskAction ToTaskAction(this UGQTaskAction action, LangEnum langEnum)
|
||||
{
|
||||
string actionName = action.ActionName;
|
||||
if (action.TextId != string.Empty)
|
||||
{
|
||||
var text = UGQDataHelper.getText(langEnum, action.TextId);
|
||||
if (text != null)
|
||||
actionName = text;
|
||||
}
|
||||
|
||||
return new TaskAction
|
||||
{
|
||||
ActionId = action.ActionId,
|
||||
ActionName = actionName,
|
||||
};
|
||||
}
|
||||
|
||||
public static TaskActionValue ToTaskActionValue(this UGQActionValue actionValue, LangEnum langEnum)
|
||||
{
|
||||
string valueName = actionValue.ValueName;
|
||||
if (actionValue.TextId != string.Empty)
|
||||
{
|
||||
var text = UGQDataHelper.getText(langEnum, actionValue.TextId);
|
||||
if (text != null)
|
||||
valueName = text;
|
||||
}
|
||||
|
||||
return new TaskActionValue
|
||||
{
|
||||
ValueId = actionValue.ValueId,
|
||||
UgcValueGuid = actionValue.UgcValueGuid,
|
||||
ValueName = valueName,
|
||||
};
|
||||
}
|
||||
|
||||
public static DialogType ToDialogType(this UGQDialogType dialogType, LangEnum langEnum)
|
||||
{
|
||||
string typeName = dialogType.TypeName;
|
||||
if (dialogType.TextId != string.Empty)
|
||||
{
|
||||
var text = UGQDataHelper.getText(langEnum, dialogType.TextId);
|
||||
if (text != null)
|
||||
typeName = text;
|
||||
}
|
||||
|
||||
return new DialogType
|
||||
{
|
||||
TypeId = dialogType.TypeId,
|
||||
TypeName = typeName,
|
||||
};
|
||||
}
|
||||
|
||||
public static DialogAction ToDialogAction(this UGQDialogCondition action, LangEnum langEnum)
|
||||
{
|
||||
string actionName = action.ConditionName;
|
||||
if (action.TextId != string.Empty)
|
||||
{
|
||||
var text = UGQDataHelper.getText(langEnum, action.TextId);
|
||||
if (text != null)
|
||||
actionName = text;
|
||||
}
|
||||
|
||||
return new DialogAction
|
||||
{
|
||||
ActionId = action.ConditionId,
|
||||
ActionName = actionName,
|
||||
};
|
||||
}
|
||||
|
||||
public static DialogActionValue ToDialogActionValue(this UGQActionValue actionValue, LangEnum langEnum)
|
||||
{
|
||||
string valueName = actionValue.ValueName;
|
||||
if (actionValue.TextId != string.Empty)
|
||||
{
|
||||
var text = UGQDataHelper.getText(langEnum, actionValue.TextId);
|
||||
if (text != null)
|
||||
valueName = text;
|
||||
}
|
||||
|
||||
return new DialogActionValue
|
||||
{
|
||||
ValueId = actionValue.ValueId,
|
||||
ValueName = valueName,
|
||||
};
|
||||
}
|
||||
public static NpcActionValue ToNpcActionValue(this UGQBeaconActionData npcAction, LangEnum langEnum)
|
||||
{
|
||||
string valueName = npcAction.Name;
|
||||
if (npcAction.Name != string.Empty)
|
||||
{
|
||||
var text = UGQDataHelper.getText(langEnum, npcAction.Name);
|
||||
if (text != null)
|
||||
valueName = text;
|
||||
}
|
||||
|
||||
return new NpcActionValue
|
||||
{
|
||||
Id = npcAction.Id,
|
||||
Name = valueName,
|
||||
FileName = npcAction.FileName,
|
||||
};
|
||||
}
|
||||
|
||||
public static MapDataValue ToMapDataValue(this ZoneMetaData zoneMetaData, LangEnum langEnum)
|
||||
{
|
||||
//string valueName = npcAction.zone_name;
|
||||
//if (npcAction.zone_name != string.Empty)
|
||||
//{
|
||||
// var text = UGQDataHelper.getText(langEnum, npcAction.zone_name);
|
||||
// if (text != null)
|
||||
// valueName = text;
|
||||
//}
|
||||
MetaData.Instance.UGQMapImageMetaDataListbyId.TryGetValue(zoneMetaData.Id, out var mapImageData);
|
||||
|
||||
return new MapDataValue
|
||||
{
|
||||
Id = zoneMetaData.Id,
|
||||
Name = zoneMetaData.zone_name,
|
||||
MapSize_X = zoneMetaData.mapDistance_x,
|
||||
MapSize_Y = zoneMetaData.mapDistance_y,
|
||||
ImageFileName = mapImageData == null ? string.Empty : mapImageData.FileName,
|
||||
};
|
||||
}
|
||||
}
|
||||
31
UGQApiServer/Culture.cs
Normal file
31
UGQApiServer/Culture.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
namespace UGQApiServer;
|
||||
|
||||
|
||||
|
||||
public enum LangEnum
|
||||
{
|
||||
En,
|
||||
Ko,
|
||||
Jp,
|
||||
}
|
||||
|
||||
public static class Culture
|
||||
{
|
||||
public static LangEnum ToLangEnum(string lang)
|
||||
{
|
||||
switch (lang)
|
||||
{
|
||||
case "ko-KR":
|
||||
case "ko":
|
||||
case "kr":
|
||||
return LangEnum.Ko;
|
||||
case "ja-JP":
|
||||
case "ja":
|
||||
case "jp":
|
||||
return LangEnum.Jp;
|
||||
default:
|
||||
return LangEnum.En;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
8
UGQApiServer/Directory.Build.props
Normal file
8
UGQApiServer/Directory.Build.props
Normal file
@@ -0,0 +1,8 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<BaseIntermediateOutputPath>..\..\obj\AnyCPU\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
|
||||
<BaseOutputPath>..\..\bin\</BaseOutputPath>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
281
UGQApiServer/Extensions/UGQApiExtentions.cs
Normal file
281
UGQApiServer/Extensions/UGQApiExtentions.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
using Asp.Versioning;
|
||||
using JwtRoleAuthentication.Services;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.Filters;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.AspNetCore.HttpLogging;
|
||||
using StackExchange.Redis;
|
||||
|
||||
|
||||
using ServerCore; using ServerBase;
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
using UGQApiServer.Auth;
|
||||
using UGQApiServer.Settings;
|
||||
using UGQApiServer.Storage;
|
||||
using UGQApiServer.UGQData;
|
||||
using UGQDataAccess.Settings;
|
||||
using UGQDataAccess.Extensions;
|
||||
using UGQApiServer.Controllers.Common;
|
||||
using UGQDataAccess.Service;
|
||||
|
||||
|
||||
namespace UGQApiServer.Extensions;
|
||||
|
||||
|
||||
public class AcceptLanguageHeaderFilter : IOperationFilter
|
||||
{
|
||||
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||
{
|
||||
if (operation == null)
|
||||
{
|
||||
throw new Exception("Invalid operation");
|
||||
}
|
||||
|
||||
operation.Parameters.Add(new OpenApiParameter
|
||||
{
|
||||
In = ParameterLocation.Header,
|
||||
Name = "accept-language",
|
||||
Description = "pass the locale here: examples like => en-US,en,ko-KR,ko,ja-JP,ja",
|
||||
Schema = new OpenApiSchema
|
||||
{
|
||||
Type = "String"
|
||||
},
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class UGQApiExtentions
|
||||
{
|
||||
public static async Task AddUGQApiExtention(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
// Add services to the container.
|
||||
services.AddControllers().AddJsonOptions(x =>
|
||||
{
|
||||
// serialize enums as strings in api responses (e.g. Role)
|
||||
x.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
|
||||
x.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
|
||||
});
|
||||
Log.getLogger().info("AddJsonOptions");
|
||||
|
||||
services.ConfigureHttpJsonOptions(options =>
|
||||
{
|
||||
// options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
|
||||
options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
|
||||
});
|
||||
Log.getLogger().info("ConfigureHttpJsonOptions");
|
||||
|
||||
services.Configure<RequestLocalizationOptions>(options =>
|
||||
{
|
||||
var supportedCultures = new[]
|
||||
{
|
||||
new CultureInfo("en-US"),
|
||||
new CultureInfo("en"),
|
||||
new CultureInfo("ko-KR"),
|
||||
new CultureInfo("ko"),
|
||||
new CultureInfo("ja-JP"),
|
||||
new CultureInfo("ja"),
|
||||
};
|
||||
options.SupportedCultures = supportedCultures;
|
||||
options.SupportedUICultures = supportedCultures;
|
||||
options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
|
||||
|
||||
options.RequestCultureProviders.Insert(0, new AcceptLanguageHeaderRequestCultureProvider());
|
||||
});
|
||||
|
||||
Log.getLogger().info("RequestLocalizationOptions");
|
||||
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
services.AddEndpointsApiExplorer();
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.OperationFilter<AcceptLanguageHeaderFilter>();
|
||||
|
||||
options.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
|
||||
{
|
||||
Name = "Authorization",
|
||||
Type = Microsoft.OpenApi.Models.SecuritySchemeType.Http,
|
||||
Scheme = "Bearer",
|
||||
BearerFormat = "JWT",
|
||||
In = Microsoft.OpenApi.Models.ParameterLocation.Header,
|
||||
Description = "JWT Authorization header using the Bearer scheme."
|
||||
});
|
||||
options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new Microsoft.OpenApi.Models.OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new Microsoft.OpenApi.Models.OpenApiReference
|
||||
{
|
||||
Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
},
|
||||
new string [] {}
|
||||
}
|
||||
});
|
||||
|
||||
// using System.Reflection;
|
||||
// var xmlFilename = $"{Assembly.GetEntryAssembly()?.GetName().Name ?? string.Empty}.xml";
|
||||
|
||||
// <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ʈ <20≯<EFBFBD><CCB8><EFBFBD><EFBFBD><EFBFBD> <20>ؾ<EFBFBD> Comment<6E><74> <20>ε<EFBFBD> <20>ȴ<EFBFBD>.
|
||||
var xmlFilename = "UGQApiServer.xml";
|
||||
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
|
||||
|
||||
options.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();
|
||||
|
||||
options.SwaggerDoc("v1", new OpenApiInfo { Title = "UGQApiServer", Version = "v1" });
|
||||
options.SwaggerDoc("ingame", new OpenApiInfo { Title = "InGame Api", Version = "ingame" });
|
||||
options.SwaggerDoc("admin", new OpenApiInfo { Title = "Admin Api", Version = "admin" });
|
||||
|
||||
options.SwaggerDoc("development", new OpenApiInfo { Title = "Development Api", Version = "development" });
|
||||
});
|
||||
|
||||
Log.getLogger().info("AddSwaggerGen");
|
||||
|
||||
services.AddApiVersioning(x =>
|
||||
{
|
||||
x.DefaultApiVersion = new ApiVersion(1, 0);
|
||||
x.AssumeDefaultVersionWhenUnspecified = true;
|
||||
x.ReportApiVersions = true;
|
||||
})
|
||||
.AddApiExplorer(x =>
|
||||
{
|
||||
x.GroupNameFormat = "'v'VVV";
|
||||
x.SubstituteApiVersionInUrl = true;
|
||||
});
|
||||
|
||||
Log.getLogger().info("AddApiVersioning");
|
||||
|
||||
services.Configure<JWTSettings>(configuration.GetSection("JWT"));
|
||||
|
||||
var jwtSettings = new JWTSettings();
|
||||
configuration.Bind("JWT", jwtSettings);
|
||||
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.IncludeErrorDetails = true;
|
||||
options.TokenValidationParameters = new TokenValidationParameters()
|
||||
{
|
||||
ClockSkew = TimeSpan.Zero,
|
||||
ValidateIssuer = false,
|
||||
ValidateAudience = false,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidIssuer = jwtSettings.ValidIssuer,
|
||||
ValidAudience = jwtSettings.ValidAudience,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Secret)
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
Log.getLogger().info("JWT");
|
||||
|
||||
MetaData.Instance.LoadTableAll();
|
||||
|
||||
Log.getLogger().info("LoadTableAll");
|
||||
|
||||
await services.AddUGQDataAccess(configuration);
|
||||
|
||||
Log.getLogger().info("AddUGQDataAccess");
|
||||
|
||||
services.AddSingleton<UGQMetaData>(provider =>
|
||||
{
|
||||
var inGameService = provider.GetRequiredService<InGameService>();
|
||||
var ugqMetaData = new UGQMetaData(inGameService);
|
||||
ugqMetaData.init();
|
||||
|
||||
return ugqMetaData;
|
||||
});
|
||||
|
||||
Log.getLogger().info("UGQMetaData");
|
||||
|
||||
services.Configure<S3Settings>(configuration.GetSection("S3"));
|
||||
services.AddSingleton<IStorageService, S3StorageService>();
|
||||
services.AddSingleton<UGQBannerImageList>();
|
||||
|
||||
Log.getLogger().info("S3StorageService");
|
||||
|
||||
services.AddSingleton<TokenService>();
|
||||
services.AddSingleton<WebPortalTokenAuth>();
|
||||
|
||||
services.AddSingleton<IConnectionMultiplexer>(ConnectionMultiplexer.Connect(configuration["Redis"]!));
|
||||
|
||||
Log.getLogger().info("Redis");
|
||||
|
||||
services.AddSingleton<DynamoDbClient>(_ =>
|
||||
{
|
||||
var dynamoDbClient = new DynamoDbClient();
|
||||
|
||||
var settings = configuration.GetSection("DynamoDb").Get<DynamoDbSettings>();
|
||||
|
||||
(var error_code, var table_names) = ServerConfigHelper.getDynamoDbTableNamesWithServiceType(settings?.TablePostfix ?? string.Empty);
|
||||
ConditionValidCheckHelper.throwIfFalseWithCondition(() => ServerErrorCode.Success == error_code, () => $"Failed to ServerConfigHelper.getDynamoDbTableNamesWithServiceType() !!! : errorCode:{error_code}");
|
||||
|
||||
if (settings != null)
|
||||
{
|
||||
var local = !string.IsNullOrEmpty(settings.Url);
|
||||
dynamoDbClient.connectToDb( table_names, local, settings.Url ?? string.Empty
|
||||
, settings.AccessKey, settings.SecretKey, settings.Region );
|
||||
}
|
||||
|
||||
dynamoDbClient.createDBIfNotExists(false).GetAwaiter().GetResult();
|
||||
|
||||
CurrencyControlHelper.setDbConnector(dynamoDbClient);
|
||||
|
||||
return dynamoDbClient;
|
||||
});
|
||||
|
||||
Log.getLogger().info("DynamoDbClient");
|
||||
|
||||
services.AddSingleton<MetaDataApi>();
|
||||
services.AddSingleton<QuestEditorApi>();
|
||||
|
||||
services.AddCors(options => options.AddPolicy("Everything", policy =>
|
||||
{
|
||||
policy
|
||||
.AllowAnyOrigin()
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod();
|
||||
}));
|
||||
|
||||
Log.getLogger().info("AddCors");
|
||||
|
||||
services.AddHealthChecks();
|
||||
Log.getLogger().info("AddHealthChecks");
|
||||
|
||||
/*
|
||||
services.AddHttpLogging(o => {
|
||||
o.LoggingFields = HttpLoggingFields.RequestBody | HttpLoggingFields.ResponseBody;
|
||||
o.RequestBodyLogLimit = 4096;
|
||||
o.ResponseBodyLogLimit = 4096;
|
||||
});
|
||||
Log.getLogger().info("AddHttpLogging");
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
150
UGQApiServer/JWT/TokenService.cs
Normal file
150
UGQApiServer/JWT/TokenService.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
|
||||
using UGQApiServer.Settings;
|
||||
using UGQDatabase.Models;
|
||||
|
||||
|
||||
namespace JwtRoleAuthentication.Services;
|
||||
|
||||
public class TokenService
|
||||
{
|
||||
readonly JWTSettings _jwtSettings;
|
||||
|
||||
public TokenService(IOptions<JWTSettings> settings)
|
||||
{
|
||||
_jwtSettings = settings.Value;
|
||||
}
|
||||
|
||||
public string CreateToken(string userGuid, string nickname)
|
||||
{
|
||||
var token = CreateJwtToken(CreateClaims(userGuid, nickname));
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
return tokenHandler.WriteToken(token);
|
||||
}
|
||||
|
||||
public string CreateAdminToken(string username, UGQAccountRole role)
|
||||
{
|
||||
var token = CreateJwtToken(CreateAdminClaims(username, role));
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
return tokenHandler.WriteToken(token);
|
||||
}
|
||||
|
||||
public string GenerateRefreshToken()
|
||||
{
|
||||
var randomNumber = new byte[32];
|
||||
using var rng = RandomNumberGenerator.Create();
|
||||
rng.GetBytes(randomNumber);
|
||||
return Convert.ToBase64String(randomNumber);
|
||||
}
|
||||
|
||||
public ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
|
||||
{
|
||||
var tokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateAudience = false, //you might want to validate the audience and issuer depending on your use case
|
||||
ValidateIssuer = false,
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Secret)),
|
||||
ValidateLifetime = false //here we are saying that we don't care about the token's expiration date
|
||||
};
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
SecurityToken securityToken;
|
||||
var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out securityToken);
|
||||
var jwtSecurityToken = securityToken as JwtSecurityToken;
|
||||
if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new SecurityTokenException("Invalid token");
|
||||
|
||||
return principal;
|
||||
}
|
||||
|
||||
public string GenerateAccessTokenFromRefreshToken(string refreshToken, string secret)
|
||||
{
|
||||
// Implement logic to generate a new access token from the refresh token
|
||||
// Verify the refresh token and extract necessary information (e.g., user ID)
|
||||
// Then generate a new access token
|
||||
|
||||
// For demonstration purposes, return a new token with an extended expiry
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var key = Encoding.ASCII.GetBytes(secret);
|
||||
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
Expires = DateTime.UtcNow.AddMinutes(15), // Extend expiration time
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
||||
};
|
||||
|
||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||
return tokenHandler.WriteToken(token);
|
||||
}
|
||||
|
||||
private JwtSecurityToken CreateJwtToken(List<Claim> claims)
|
||||
{
|
||||
var symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Secret));
|
||||
DateTime expiration = DateTime.UtcNow.AddMinutes(_jwtSettings.TokenValidityInMinutes);
|
||||
|
||||
return new(
|
||||
_jwtSettings.ValidIssuer,
|
||||
_jwtSettings.ValidAudience,
|
||||
claims,
|
||||
expires: expiration,
|
||||
signingCredentials: new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256)
|
||||
);
|
||||
}
|
||||
|
||||
private List<Claim> CreateAdminClaims(string username, UGQAccountRole role)
|
||||
{
|
||||
try
|
||||
{
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
// new Claim(JwtRegisteredClaimNames.Sub, jwtSub),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString()),
|
||||
new Claim(ClaimTypes.Name, username),
|
||||
new Claim(ClaimTypes.Role, role.ToString())
|
||||
};
|
||||
|
||||
return claims;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Claim> CreateClaims(string userGuid, string nickname)
|
||||
{
|
||||
try
|
||||
{
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
// new Claim(JwtRegisteredClaimNames.Sub, jwtSub),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString()),
|
||||
new Claim("Id", userGuid),
|
||||
new Claim(ClaimTypes.Name, nickname),
|
||||
// new Claim(ClaimTypes.Email, user.Email),
|
||||
// new Claim(ClaimTypes.Role, user.Role.ToString())
|
||||
};
|
||||
|
||||
return claims;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
144
UGQApiServer/Models/AdminApiModels.cs
Normal file
144
UGQApiServer/Models/AdminApiModels.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using Amazon.S3.Model;
|
||||
using UGQDatabase.Models;
|
||||
|
||||
namespace UGQApiServer.Models;
|
||||
|
||||
#pragma warning disable CS8618
|
||||
|
||||
public class AdminSignupRequest
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
public class AdminSignupResponse
|
||||
{
|
||||
public string Username { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public UGQAccountRole Role { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class AdminLoginRequest
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
|
||||
public class AdminLoginResponse
|
||||
{
|
||||
public string Username { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public UGQAccountRole Role { get; set; }
|
||||
public string AccessToken { get; set; }
|
||||
public string RefreshToken { get; set; }
|
||||
}
|
||||
|
||||
public class AdminChangePaswordRequest
|
||||
{
|
||||
public string NewPassword { get; set; }
|
||||
}
|
||||
public class AdminChangePaswordResponse
|
||||
{
|
||||
}
|
||||
|
||||
public class AdminUpdatePaswordRequest
|
||||
{
|
||||
public string Username { get; set; }
|
||||
public string NewPassword { get; set; }
|
||||
}
|
||||
public class AdminUpdatePaswordResponse
|
||||
{
|
||||
}
|
||||
|
||||
public class UQGAllQuestResponse
|
||||
{
|
||||
public long TotalCount { get; set; }
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public int TotalPages { get; set; }
|
||||
public List<UGQSummary> Summaries { get; set; }
|
||||
}
|
||||
|
||||
public class UGQAllAccountItemResponse
|
||||
{
|
||||
public long TotalCount { get; set; }
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public int TotalPages { get; set; }
|
||||
public List<UGQAccountItem> Accounts { get; set; }
|
||||
}
|
||||
|
||||
public class UGQModifyAccountSlot
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
public int Count { get; set; }
|
||||
}
|
||||
|
||||
public class UGQReserveAccountGrade
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public UgqGradeType GradeType { get; set; }
|
||||
|
||||
public DateTime ReserveTime { get; set; }
|
||||
}
|
||||
|
||||
public class UGQModifyAccountGrade
|
||||
{
|
||||
public string ReserveId { get; set; }
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public UgqGradeType GradeType { get; set; }
|
||||
|
||||
public DateTime ReserveTime { get; set; }
|
||||
}
|
||||
|
||||
public class UGQModifyCreatorPoint
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
public int Amount { get; set; }
|
||||
public string Reason { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class UGQMetaverseAccount
|
||||
{
|
||||
public string? LoginAccountId { get; set; }
|
||||
public string? UserGuid { get; set; }
|
||||
public string? Nickname { get; set; }
|
||||
}
|
||||
|
||||
public class UGQReserveAccountGradeResult
|
||||
{
|
||||
public string ReserveId { get; set; }
|
||||
public string UserGuid { get; set; }
|
||||
|
||||
public string AccountId { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public UgqGradeType CurrentGradeType { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public UgqGradeType ReserveGradeType { get; set; }
|
||||
public DateTime ReserveTime { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
}
|
||||
|
||||
public class UGQAllReserveAccountGradeItemResponse
|
||||
{
|
||||
public long TotalCount { get; set; }
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public int TotalPages { get; set; }
|
||||
public List<UGQReserveAccountGradeResult> Accounts { get; set; }
|
||||
}
|
||||
|
||||
public class UGQReserveAccountGradeItemResponse
|
||||
{
|
||||
public List<UGQReserveAccountGradeResult> Accounts { get; set; }
|
||||
}
|
||||
100
UGQApiServer/Models/DevelopmentApiModels.cs
Normal file
100
UGQApiServer/Models/DevelopmentApiModels.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using UGQDatabase.Models;
|
||||
|
||||
namespace UGQApiServer.Models;
|
||||
|
||||
#pragma warning disable CS8618
|
||||
|
||||
|
||||
public class GetGameDBAccountByEmail
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public string? LoginAccountId { get; set; }
|
||||
public string? UserGuid { get; set; }
|
||||
public string? Nickname { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class AddGameDBAccountRequest
|
||||
{
|
||||
public string LoginAccountId { get; set; }
|
||||
public string Nickname { get; set; }
|
||||
}
|
||||
|
||||
public class AddGameDBAccountByEmailRequest
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public string Nickname { get; set; }
|
||||
}
|
||||
|
||||
public class AddGameDBAccountResponse
|
||||
{
|
||||
public string LoginAccountId { get; set; }
|
||||
public string UserGuid { get; set; }
|
||||
public string Nickname { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class AddFakeUgcNpcReqeust
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
public string UgcNpcNickname { get; set; }
|
||||
}
|
||||
|
||||
public class AddCreatorPointReqeust
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
public long QuestId { get; set; }
|
||||
public long Revision { get; set; }
|
||||
public int Amount { get; set; }
|
||||
}
|
||||
|
||||
public class SettleUpCreatorPointReqeust
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
public int Amount { get; set; }
|
||||
}
|
||||
|
||||
public class GameQuestDataFakeUpdateReqeust
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
public string QuestContentId { get; set; }
|
||||
}
|
||||
|
||||
public class ChangeCurrencyAmountRequest
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
public double Amount { get; set; }
|
||||
}
|
||||
|
||||
public class ChangeCurrencyAmountResponse
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public CurrencyType CurrencyType { get; set; }
|
||||
public double CurrentAmount { get; set; }
|
||||
}
|
||||
|
||||
public class ValidateQuestRequest
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
}
|
||||
|
||||
public class ValidateQuestItem
|
||||
{
|
||||
public QuestContentEntity QuestContent { get; set; }
|
||||
public List<QuestDialogEntity> QuestDialogs { get; set; }
|
||||
public List<string> Errors { get; set; }
|
||||
}
|
||||
|
||||
public class ValidateQuestResponse
|
||||
{
|
||||
public List<ValidateQuestItem> Items { get; set; }
|
||||
}
|
||||
|
||||
public class CopyGameScriptDataRequest
|
||||
{
|
||||
public long QuestId { get; set; }
|
||||
}
|
||||
|
||||
144
UGQApiServer/Models/MetaDataModels.cs
Normal file
144
UGQApiServer/Models/MetaDataModels.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using MetaAssets;
|
||||
|
||||
namespace UGQApiServer.Models;
|
||||
|
||||
#pragma warning disable CS8618
|
||||
|
||||
|
||||
public class UGQNpcMetaData
|
||||
{
|
||||
public int? NpcId { get; set; }
|
||||
public string? UgcNpcGuid { get; set; }
|
||||
public string NpcName { get; set; }
|
||||
public string NpcTitle { get; set; }
|
||||
public EGenderType NpcGender { get; set; }
|
||||
public Locaion Location { get; set; }
|
||||
}
|
||||
|
||||
public class Locaion
|
||||
{
|
||||
public int x { get; set; }
|
||||
public int y { get; set; }
|
||||
}
|
||||
|
||||
public class UGQNpcActionMetaData
|
||||
{
|
||||
public int ActionId { get; set; }
|
||||
public string ActionName { get; set; }
|
||||
public string Url { get; set; }
|
||||
}
|
||||
|
||||
public class UGQNpcMetaDataList
|
||||
{
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public bool HasNextPage { get; set; }
|
||||
public List<UGQNpcMetaData> Npcs { get; set; }
|
||||
}
|
||||
|
||||
public class TaskAction
|
||||
{
|
||||
public int ActionId { get; set; }
|
||||
public string ActionName { get; set; }
|
||||
public bool Disabled { get; set; }
|
||||
}
|
||||
|
||||
public class TaskActionList
|
||||
{
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public bool HasNextPage { get; set; }
|
||||
public List<TaskAction> TaskActions { get; set; }
|
||||
}
|
||||
|
||||
public class DialogType
|
||||
{
|
||||
public int TypeId { get; set; }
|
||||
public string TypeName { get; set; }
|
||||
public bool Disabled { get; set; }
|
||||
}
|
||||
|
||||
public class DialogTypeList
|
||||
{
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public bool HasNextPage { get; set; }
|
||||
public List<DialogType> DialogTypes { get; set; }
|
||||
}
|
||||
|
||||
public class DialogAction
|
||||
{
|
||||
public int? ActionId { get; set; }
|
||||
public string ActionName { get; set; }
|
||||
public bool Disabled { get; set; }
|
||||
}
|
||||
|
||||
public class DialogActionList
|
||||
{
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public bool HasNextPage { get; set; }
|
||||
public List<DialogAction> DialogActions { get; set; }
|
||||
}
|
||||
|
||||
public class TaskActionValue
|
||||
{
|
||||
public int? ValueId { get; set; }
|
||||
public string? UgcValueGuid { get; set; }
|
||||
public string ValueName { get; set; }
|
||||
}
|
||||
|
||||
public class TaskActionValueList
|
||||
{
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public bool HasNextPage { get; set; }
|
||||
public List<TaskActionValue> Values { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class DialogActionValue
|
||||
{
|
||||
public int? ValueId { get; set; }
|
||||
public string ValueName { get; set; }
|
||||
}
|
||||
|
||||
public class DialogActionValueList
|
||||
{
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public bool HasNextPage { get; set; }
|
||||
public List<DialogActionValue> Values { get; set; }
|
||||
}
|
||||
|
||||
public class NpcActionValue
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string FileName { get; set; }
|
||||
}
|
||||
|
||||
public class MapDataValue
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int MapSize_X { get; set; }
|
||||
public int MapSize_Y { get; set; }
|
||||
public string ImageFileName { get; set; }
|
||||
}
|
||||
|
||||
public class NpcActionValueList
|
||||
{
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public bool HasNextPage { get; set; }
|
||||
public List<NpcActionValue> Values { get; set; }
|
||||
}
|
||||
|
||||
public class MapDataValueList
|
||||
{
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public bool HasNextPage { get; set; }
|
||||
public List<MapDataValue> Values { get; set; }
|
||||
}
|
||||
124
UGQApiServer/Models/QuestEditorModels.cs
Normal file
124
UGQApiServer/Models/QuestEditorModels.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using UGQDataAccess;
|
||||
using UGQDatabase.Models;
|
||||
|
||||
namespace UGQApiServer.Models;
|
||||
|
||||
#pragma warning disable CS8618
|
||||
|
||||
public class OldDevLoginRequest
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
}
|
||||
|
||||
public class DevLoginRequest
|
||||
{
|
||||
public string LoginAccountId { get; set; }
|
||||
}
|
||||
|
||||
public class LoginRequest
|
||||
{
|
||||
public string WebPortalToken { get; set; }
|
||||
}
|
||||
|
||||
public class IgmLoginResponse
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
public string Nickname { get; set; }
|
||||
public string AccessToken { get; set; }
|
||||
}
|
||||
|
||||
public class LoginResponse
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
public string Nickname { get; set; }
|
||||
public string AccessToken { get; set; }
|
||||
public string RefreshToken { get; set; }
|
||||
}
|
||||
|
||||
public class RefreshTokenRequest
|
||||
{
|
||||
public string AccessToken { get; set; }
|
||||
public string RefreshToken { get; set; }
|
||||
}
|
||||
|
||||
public class RefreshTokenResponse
|
||||
{
|
||||
public string AccessToken { get; set; }
|
||||
public string RefreshToken { get; set; }
|
||||
}
|
||||
|
||||
public class UGQAccount
|
||||
{
|
||||
public string UserGuid { get; set; }
|
||||
public string Nickname { get; set; }
|
||||
public string AccountId { get; set; }
|
||||
public int SlotCount { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public UgqGradeType GradeType { get; set; }
|
||||
public double CreatorPoint { get; set; }
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
public class UGQAccountDetail : UGQAccount
|
||||
{
|
||||
public int TotalQuests { get; set; }
|
||||
}
|
||||
|
||||
public class UGQAccountCurrency
|
||||
{
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public CurrencyType CurrencyType { get; set; }
|
||||
public double Amount { get; set; }
|
||||
}
|
||||
|
||||
public class UGQSlotPrice
|
||||
{
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public CurrencyType CurrencyType { get; set; }
|
||||
public double Price { get; set; }
|
||||
}
|
||||
|
||||
public class UGQAccountItem : UGQAccount
|
||||
{
|
||||
public int QuestCount { get; set; }
|
||||
}
|
||||
|
||||
public class UGQAccountMoney : UGQAccount
|
||||
{
|
||||
public double Sapphire { get; set; }
|
||||
}
|
||||
|
||||
public class UGQSummaryList
|
||||
{
|
||||
public List<UGQSummary> UGQSummaries { get; set; }
|
||||
}
|
||||
|
||||
public class UGQAddQuestDialogResponse
|
||||
{
|
||||
public UGQContent QuestContent { get; set; }
|
||||
public UGQDialog QuestDialog { get; set; }
|
||||
}
|
||||
|
||||
public class TagIdResponse
|
||||
{
|
||||
public int TagId { get; set; }
|
||||
public string TagName { get; set; }
|
||||
}
|
||||
|
||||
public class UserBeacon
|
||||
{
|
||||
public string BeaconId { get; set; }
|
||||
public string BeaconName { get; set; }
|
||||
}
|
||||
|
||||
public class UserBeaconsResponse
|
||||
{
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public bool HasNextPage { get; set; }
|
||||
public List<UserBeacon> UserBeacons { get; set; }
|
||||
}
|
||||
|
||||
208
UGQApiServer/Models/UGQModels.cs
Normal file
208
UGQApiServer/Models/UGQModels.cs
Normal file
@@ -0,0 +1,208 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Bson;
|
||||
|
||||
|
||||
using UGQDatabase.Models;
|
||||
using UGQDataAccess.Service;
|
||||
using UGQDataAccess.Repository.Models;
|
||||
|
||||
namespace UGQApiServer.Models;
|
||||
|
||||
#pragma warning disable CS8618
|
||||
|
||||
public class UGQStateGroup
|
||||
{
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public QuestContentState State { get; set; }
|
||||
|
||||
public int Count { get; set; }
|
||||
}
|
||||
|
||||
public class UGQSummary
|
||||
{
|
||||
public string QuestContentId { get; set; }
|
||||
public long QuestId { get; set; }
|
||||
public long Revision { get; set; }
|
||||
|
||||
public string UserGuid { get; set; }
|
||||
public string Author { get; set; }
|
||||
|
||||
public int BeaconId { get; set; }
|
||||
public string? UgcBeaconGuid { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public UgqGradeType GradeType { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
public List<string> Languages { get; set; }
|
||||
public string Description { get; set; }
|
||||
public int Cost { get; set; }
|
||||
public string TitleImagePath { get; set; }
|
||||
public string BannerImagePath { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public QuestContentState State { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
public UGQStats Stats { get; set; }
|
||||
|
||||
public string BeaconName { get; set; }
|
||||
public string Savelanguage { get; set; }
|
||||
}
|
||||
|
||||
public class UGQQuestProfitStats
|
||||
{
|
||||
public string QuestContentId { get; set; }
|
||||
public long QuestId { get; set; }
|
||||
public long Revision { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
public string TitleImagePath { get; set; }
|
||||
public string BannerImagePath { get; set; }
|
||||
|
||||
public int Completed { get; set; }
|
||||
public double TotalProfit { get; set; }
|
||||
}
|
||||
|
||||
public class UGQText
|
||||
{
|
||||
public string Kr { get; set; }
|
||||
public string En { get; set; }
|
||||
public string Jp { get; set; }
|
||||
}
|
||||
|
||||
public class UGQPresetImage
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string ImagePath { get; set; }
|
||||
}
|
||||
|
||||
public class UGQDialogSequenceAction
|
||||
{
|
||||
public DialogTalker Talker { get; set; }
|
||||
public int Type { get; set; }
|
||||
public UGQText Talk { get; set; }
|
||||
public int Condition { get; set; }
|
||||
public int ConditionValue { get; set; }
|
||||
public int NextSequence { get; set; }
|
||||
public int NpcAction { get; set; }
|
||||
public string TypeName { get; set; }
|
||||
public string ConditioneName { get; set; }
|
||||
public string ConditioneValueName { get; set; }
|
||||
}
|
||||
|
||||
public class UGQDialogSequence
|
||||
{
|
||||
public int SequenceId { get; set; }
|
||||
public List<UGQDialogSequenceAction> Actions { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class UGQDialog
|
||||
{
|
||||
public string DialogId { get; set; }
|
||||
public List<UGQDialogSequence> Sequences { get; set; }
|
||||
}
|
||||
|
||||
public class UGQTask
|
||||
{
|
||||
public UGQText GoalText { get; set; }
|
||||
public int ActionId { get; set; }
|
||||
public int ActionValue { get; set; }
|
||||
public bool IsShowNpcLocation { get; set; }
|
||||
public string? UgcActionValueGuid { get; set; }
|
||||
public string? UgcActionValueName { get; set; }
|
||||
public string? DialogId { get; set; }
|
||||
|
||||
public string ActionName { get; set; }
|
||||
public string ActionValueName { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class UGQContent
|
||||
{
|
||||
public string QuestContentId { get; set; }
|
||||
public long QuestId { get; set; }
|
||||
public long Revision { get; set; }
|
||||
public int BeaconId { get; set; }
|
||||
public string? UgcBeaconGuid { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public UgqGradeType GradeType { get; set; }
|
||||
|
||||
public UGQText Title { get; set; }
|
||||
public List<string> Languages { get; set; }
|
||||
public string TitleImagePath { get; set; }
|
||||
public string BannerImagePath { get; set; }
|
||||
public UGQText Description { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public QuestContentState State { get; set; }
|
||||
public int Cost { get; set; }
|
||||
public List<UGQTask> Tasks { get; set; }
|
||||
public DateTimeOffset LastUpdated { get; set; }
|
||||
|
||||
public string BeaconName { get; set; }
|
||||
}
|
||||
|
||||
public class UGQBoard
|
||||
{
|
||||
public int QuestContentId { get; set; }
|
||||
public long QuestId { get; set; }
|
||||
public long Revision { get; set; }
|
||||
public string Title { get; set; }
|
||||
public List<string> Languages { get; set; }
|
||||
public string TitleImagePath { get; set; }
|
||||
public string BannerImagePath { get; set; }
|
||||
public string Description { get; set; }
|
||||
public UGQStats Stats { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public QuestContentState State { get; set; }
|
||||
public int Cost { get; set; }
|
||||
public DateTimeOffset LastUpdated { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class UGQCreatorPointHistory
|
||||
{
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public CreatorPointHistoryKind Kind { get; set; }
|
||||
// public string DetailReason { get; set; }
|
||||
public double Amount { get; set; }
|
||||
public double TotalAmount { get; set; }
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
public class UGQCreatorPointHistoryResult
|
||||
{
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public int TotalPages { get; set; }
|
||||
public List<UGQCreatorPointHistory> Items { get; set; }
|
||||
}
|
||||
|
||||
public class UGQSummaryResponse
|
||||
{
|
||||
public int TotalCount { get; set; }
|
||||
public List<UGQStateGroup> StateGroups { get; set; }
|
||||
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public int TotalPages { get; set; }
|
||||
public List<UGQSummary> Summaries { get; set; }
|
||||
}
|
||||
|
||||
public class UGQQuestProfitStatsResult
|
||||
{
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public int TotalPages { get; set; }
|
||||
public List<UGQQuestProfitStats> Items { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using ControlCenter.NamedPipeHost.Manager;
|
||||
using ControlCenter.NamedPipeHost.NamedPipe;
|
||||
using ServerControlCenter;
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
namespace UGQApiServer.NamedPipePacketHandler;
|
||||
|
||||
public class ForceStopServerMessageReceiver : NamedPipeReceiver<A2S_REQ_FORCE_STOP_SERVER>
|
||||
{
|
||||
private readonly ServerInfoManager m_info_manager;
|
||||
|
||||
public ForceStopServerMessageReceiver(ServerInfoManager infoManager)
|
||||
{
|
||||
m_info_manager = infoManager;
|
||||
}
|
||||
|
||||
public override async Task Handle(A2S_REQ_FORCE_STOP_SERVER message, string message_id)
|
||||
{
|
||||
Log.getLogger().debug($"{nameof(ForceStopServerMessageReceiver)}: Receive - message_id[{message_id}] / message[{message}]");
|
||||
|
||||
m_info_manager.setServerStatus(ServerStatus.Stop);
|
||||
|
||||
// 정보 전달 대기
|
||||
await Task.Delay(1_000);
|
||||
|
||||
// process 종료
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using ControlCenter.NamedPipeHost.Manager;
|
||||
using ControlCenter.NamedPipeHost.NamedPipe;
|
||||
using ServerControlCenter;
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
namespace UGQApiServer.NamedPipePacketHandler;
|
||||
|
||||
public class StopServerMessageReceiver : NamedPipeReceiver<A2S_REQ_STOP_SERVER>
|
||||
{
|
||||
private readonly ServerInfoManager m_info_manager;
|
||||
|
||||
public StopServerMessageReceiver(ServerInfoManager infoManager)
|
||||
{
|
||||
m_info_manager = infoManager;
|
||||
}
|
||||
|
||||
public override async Task Handle(A2S_REQ_STOP_SERVER message, string message_id)
|
||||
{
|
||||
Log.getLogger().debug($"{nameof(StopServerMessageReceiver)}: Receive - message_id[{message_id}] / message[{message}]");
|
||||
|
||||
m_info_manager.setServerStatus(ServerStatus.Stop);
|
||||
|
||||
// 정보 전달 대기
|
||||
await Task.Delay(1_000);
|
||||
|
||||
// process 종료
|
||||
Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
174
UGQApiServer/Program.cs
Normal file
174
UGQApiServer/Program.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using Amazon.DynamoDBv2.DocumentModel;
|
||||
using CommandLine;
|
||||
using ControlCenter.NamedPipeHost.Extensions;
|
||||
using Google.Protobuf;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using NLog.Web;
|
||||
using ServerCommon;
|
||||
using ServerCore; using ServerBase;
|
||||
using UGQApiServer.Converter;
|
||||
using UGQApiServer.Extensions;
|
||||
using UGQApiServer.Settings;
|
||||
using UGQApiServer.Storage;
|
||||
using UGQApiServer.UGQData;
|
||||
using UGQApiServer.Auth;
|
||||
using UGQApiServer.BackGroundService;
|
||||
using UGQDataAccess.Logs;
|
||||
|
||||
namespace UGQApiServer
|
||||
{
|
||||
public class CommandLineOptions
|
||||
{
|
||||
[Option('t', "serverType", Required = true, HelpText = "UGQ API Server Type: [UgqApi, UgqAdmin, UgqIngame, UgqAllInOne]")]
|
||||
public UgqApiType m_type { get; set; }
|
||||
|
||||
[Option('p', "port", Required = true, HelpText = "UGQ API Server Port")]
|
||||
public int m_port { get; set; }
|
||||
|
||||
[Option('d', "develop", Default = false, Required = false, HelpText = "UGQ API Server Development Mode")]
|
||||
public bool m_develop { get; set; }
|
||||
}
|
||||
|
||||
/*
|
||||
message UgqGameTaskDataForClient
|
||||
{
|
||||
int32 taskNum = 1;
|
||||
UgqGameTextDataForClient goalText = 2;
|
||||
optional string dialogueId = 3;
|
||||
}
|
||||
*/
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
var parsing = Parser.Default.ParseArguments<CommandLineOptions>(args);
|
||||
if (parsing?.Value == null) return;
|
||||
|
||||
var options = parsing.Value;
|
||||
var apiAllowSettings = new ApiAllowSettings(options.m_type);
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
Console.WriteLine($"Env: {builder.Environment.EnvironmentName}");
|
||||
|
||||
// kestrel
|
||||
options.m_port = usingKestrel(builder, options.m_port);
|
||||
|
||||
Log.NLogFileName = "./Config/nlog-UGQApiServer.config";
|
||||
Log.initLog("UGQApiServer", "Developer");
|
||||
UgqApiBusinessLogger.initBusinessLog();
|
||||
|
||||
Log.getLogger().info("startup");
|
||||
|
||||
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Logging.SetMinimumLevel(LogLevel.Trace);
|
||||
builder.Host.UseNLog();
|
||||
|
||||
builder.Services.AddSingleton(apiAllowSettings);
|
||||
builder.Services.AddSingleton(options);
|
||||
builder.Services.AddSingleton<AuthSql>();
|
||||
|
||||
builder.Services.AddSingleton<ReserveAccountGradeBackGroundService>();
|
||||
builder.Services.AddHostedService(provider => provider.GetRequiredService<ReserveAccountGradeBackGroundService>());
|
||||
builder.Services.AddOnTimeBackgroundService();
|
||||
|
||||
builder.Services.AddOnTimeTask("logging", new DateTime(1, 1, 1, 0, 1, 0), new Task(DailyEmptyLogging));
|
||||
|
||||
await builder.Services.AddUGQApiExtention(builder.Configuration);
|
||||
|
||||
var assemblies = new List<Assembly>(AppDomain.CurrentDomain.GetAssemblies());
|
||||
var serverType = changeServerTypeFromUgcTypeToControlCenterType(options.m_type);
|
||||
builder.Services.AddNamedPipelineClientManager(assemblies, serverType, ServiceCategory.Caliverse.ToString(), NetworkHelper.getEthernetLocalIPv4(), options.m_port);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// app.UseHttpLogging();
|
||||
|
||||
app.MapHealthChecks("/healthcheck");
|
||||
|
||||
UGQConverter.init(app.Services.GetRequiredService<IStorageService>(), app.Services.GetRequiredService<UGQMetaData>());
|
||||
InGameConverter.init(app.Services.GetRequiredService<IStorageService>(), app.Services.GetRequiredService<UGQMetaData>());
|
||||
|
||||
bool isDevelopment = app.Environment.IsDevelopment() || options.m_develop ||
|
||||
(builder.Environment.EnvironmentName == "AWSDev");
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (isDevelopment == true)
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
// Account api 때문에 모든 설정에서 보여야 함
|
||||
// if (apiAllowSettings.AllowWebApi) c.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
|
||||
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
|
||||
if (apiAllowSettings.AllowInGameApi) c.SwaggerEndpoint("/swagger/ingame/swagger.json", "InGame");
|
||||
if (apiAllowSettings.AllowAdminApi) c.SwaggerEndpoint("/swagger/admin/swagger.json", "Admin");
|
||||
if (isDevelopment) c.SwaggerEndpoint("/swagger/development/swagger.json", "Development");
|
||||
});
|
||||
}
|
||||
|
||||
app.UseRequestLocalization();
|
||||
|
||||
|
||||
app.UseCors("Everything");
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.UseNamedPipelineClientManager();
|
||||
await app.RunAsync();
|
||||
}
|
||||
|
||||
private static void DailyEmptyLogging()
|
||||
{
|
||||
List<ILogInvoker> business_logs = [
|
||||
new UgqApiEmptyLoginBusinessLog(),
|
||||
];
|
||||
var log_action = new LogActionEx(LogActionType.TestBusinessLog);
|
||||
var empty_business_refresh_with_log_actor = new EmptyBusinessWithLogActor();
|
||||
BusinessLogger.collectLogs(log_action, empty_business_refresh_with_log_actor, business_logs);
|
||||
}
|
||||
|
||||
private static int usingKestrel(WebApplicationBuilder builder, int port)
|
||||
{
|
||||
port = port <= 0 ? Default.DefaultPort : port;
|
||||
|
||||
builder.WebHost.ConfigureKestrel(options =>
|
||||
{
|
||||
// port 설정
|
||||
options.Listen(IPAddress.Any, port, o =>
|
||||
{
|
||||
// https 사용 설정
|
||||
//o.UseHttps("*.pfx", "pwd");
|
||||
|
||||
// protocols 설정
|
||||
o.Protocols = HttpProtocols.Http1;
|
||||
});
|
||||
});
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
private static ServerControlCenter.ServerType changeServerTypeFromUgcTypeToControlCenterType(UgqApiType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
UgqApiType.UgqApi or UgqApiType.UgqAllInOne => ServerControlCenter.ServerType.UgqApi,
|
||||
UgqApiType.UgqIngame => ServerControlCenter.ServerType.UgqIngame,
|
||||
UgqApiType.UgqAdmin => ServerControlCenter.ServerType.UgqAdmin,
|
||||
_ => ServerControlCenter.ServerType.None
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
33
UGQApiServer/Properties/launchSettings.json
Normal file
33
UGQApiServer/Properties/launchSettings.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "-t UgqAllInOne -p 11000",
|
||||
"workingDirectory": "..\\..\\bin\\Debug",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "http://localhost:11000"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:42071",
|
||||
"sslPort": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
10
UGQApiServer/Settings/DynamoDbSettings.cs
Normal file
10
UGQApiServer/Settings/DynamoDbSettings.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace UGQApiServer.Settings;
|
||||
|
||||
public class DynamoDbSettings
|
||||
{
|
||||
public string Url { get; set; } = null!;
|
||||
public string TablePostfix { get; set; } = null!;
|
||||
public string AccessKey { get; set; } = null!;
|
||||
public string SecretKey { get; set; } = null!;
|
||||
public string Region { get; set; } = null!;
|
||||
}
|
||||
12
UGQApiServer/Settings/JWTSettings.cs
Normal file
12
UGQApiServer/Settings/JWTSettings.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace UGQApiServer.Settings;
|
||||
|
||||
#pragma warning disable CS8618
|
||||
|
||||
public class JWTSettings
|
||||
{
|
||||
public string ValidAudience { get; set; }
|
||||
public string ValidIssuer { get; set; }
|
||||
public string Secret { get; set; }
|
||||
|
||||
public int TokenValidityInMinutes { get; set; }
|
||||
}
|
||||
15
UGQApiServer/Settings/S3Settings.cs
Normal file
15
UGQApiServer/Settings/S3Settings.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace UGQDataAccess.Settings;
|
||||
|
||||
#pragma warning disable CS8618
|
||||
|
||||
public class S3Settings
|
||||
{
|
||||
public string AccessKey { get; set; }
|
||||
|
||||
public string SecretAccessKey { get; set; }
|
||||
|
||||
public string BucketName { get; set; }
|
||||
public string RootFolder { get; set; }
|
||||
|
||||
public string DownloadUrl { get; set; }
|
||||
}
|
||||
12
UGQApiServer/Storage/IStorageService.cs
Normal file
12
UGQApiServer/Storage/IStorageService.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace UGQApiServer.Storage;
|
||||
|
||||
|
||||
public interface IStorageService
|
||||
{
|
||||
|
||||
string fileUrl(string filename);
|
||||
Task<bool> uploadFile(string filename, IFormFile file);
|
||||
Task<bool> deleteFile(string filename);
|
||||
}
|
||||
|
||||
|
||||
40
UGQApiServer/Storage/LocalStorageService.cs
Normal file
40
UGQApiServer/Storage/LocalStorageService.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
namespace UGQApiServer.Storage;
|
||||
|
||||
|
||||
public class LocalStorageService : IStorageService
|
||||
{
|
||||
public LocalStorageService()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public string fileUrl(string filename)
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
|
||||
public async Task<bool> uploadFile(string filename, IFormFile file)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
string path = Path.Combine(Directory.GetCurrentDirectory(), "files");
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
using (var stream = new FileStream(Path.Combine(path, filename), FileMode.Create))
|
||||
{
|
||||
file.CopyTo(stream);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public async Task<bool> deleteFile(string filename)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
System.IO.File.Delete(filename);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
63
UGQApiServer/Storage/S3StorageService.cs
Normal file
63
UGQApiServer/Storage/S3StorageService.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Amazon.S3;
|
||||
using UGQDataAccess.Settings;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Amazon.S3.Transfer;
|
||||
using Amazon.S3.Model;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
|
||||
namespace UGQApiServer.Storage;
|
||||
|
||||
public class S3StorageService : IStorageService
|
||||
{
|
||||
readonly AmazonS3Client _s3Client;
|
||||
readonly S3Settings _settings;
|
||||
|
||||
public S3StorageService(IOptions<S3Settings> settings)
|
||||
{
|
||||
_settings = settings.Value;
|
||||
_s3Client = new AmazonS3Client(_settings.AccessKey, _settings.SecretAccessKey, Amazon.RegionEndpoint.USEast1);
|
||||
}
|
||||
|
||||
public string fileUrl(string filename)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename) == true)
|
||||
return string.Empty;
|
||||
|
||||
return $"{_settings.DownloadUrl}/{_settings.RootFolder}/{filename}";
|
||||
}
|
||||
|
||||
public async Task<bool> uploadFile(string filename, IFormFile file)
|
||||
{
|
||||
using (var newMemoryStream = new MemoryStream())
|
||||
{
|
||||
file.CopyTo(newMemoryStream);
|
||||
|
||||
var uploadRequest = new TransferUtilityUploadRequest
|
||||
{
|
||||
InputStream = newMemoryStream,
|
||||
Key = $"{_settings.RootFolder}/{filename}",
|
||||
BucketName = _settings.BucketName,
|
||||
};
|
||||
|
||||
var fileTransferUtility = new TransferUtility(_s3Client);
|
||||
await fileTransferUtility.UploadAsync(uploadRequest);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> deleteFile(string filename)
|
||||
{
|
||||
var deleteObjectRequest = new DeleteObjectRequest
|
||||
{
|
||||
BucketName = _settings.BucketName,
|
||||
Key = $"{_settings.RootFolder}/{filename}",
|
||||
};
|
||||
|
||||
await _s3Client.DeleteObjectAsync(deleteObjectRequest);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
51
UGQApiServer/UGQApiServer.csproj
Normal file
51
UGQApiServer/UGQApiServer.csproj
Normal file
@@ -0,0 +1,51 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Exe</OutputType>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<Deterministic>true</Deterministic>
|
||||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
||||
<Configurations>Debug;Release;Shipping</Configurations>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<NoWarn>1591</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<NoWarn>1591</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Shipping|AnyCPU'">
|
||||
<NoWarn>1591</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Asp.Versioning.Mvc" />
|
||||
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" />
|
||||
<PackageReference Include="AWSSDK.S3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Tools\ControlCenter\ControlCenter.NamedPipeHost\ControlCenter.NamedPipeHost.csproj" />
|
||||
<ProjectReference Include="..\UGQDataAccess\UGQDataAccess.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
42
UGQApiServer/UGQData/UGQBannerImageList.cs
Normal file
42
UGQApiServer/UGQData/UGQBannerImageList.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Amazon.S3;
|
||||
|
||||
|
||||
using MetaAssets;
|
||||
|
||||
|
||||
using UGQApiServer.Models;
|
||||
using UGQApiServer.Settings;
|
||||
using UGQApiServer.Storage;
|
||||
using UGQDatabase.Models;
|
||||
using UGQDataAccess.Settings;
|
||||
|
||||
|
||||
namespace UGQApiServer.UGQData;
|
||||
|
||||
|
||||
public class UGQBannerImageList
|
||||
{
|
||||
readonly AmazonS3Client _s3Client;
|
||||
readonly S3Settings _settings;
|
||||
|
||||
public UGQBannerImageList(IOptions<S3Settings> settings)
|
||||
{
|
||||
_settings = settings.Value;
|
||||
_s3Client = new AmazonS3Client(_settings.AccessKey, _settings.SecretAccessKey, Amazon.RegionEndpoint.USEast1);
|
||||
}
|
||||
|
||||
public async Task<List<string>> getFiles()
|
||||
{
|
||||
var res = await _s3Client.ListObjectsAsync(_settings.BucketName, "preset");
|
||||
return res.S3Objects.Where(x => x.Key.EndsWith(".png"))
|
||||
.Select(x => x.Key).ToList();
|
||||
}
|
||||
|
||||
}
|
||||
35
UGQApiServer/UGQData/UGQDataHelper.cs
Normal file
35
UGQApiServer/UGQData/UGQDataHelper.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
namespace UGQApiServer.UGQData;
|
||||
|
||||
public static class UGQDataHelper
|
||||
{
|
||||
public static string? getText(LangEnum langEnum, string textId)
|
||||
{
|
||||
MetaData.Instance._textTable.TryGetValue(textId, out var textData);
|
||||
if (textData == null)
|
||||
return null;
|
||||
|
||||
string text;
|
||||
switch (langEnum)
|
||||
{
|
||||
case LangEnum.Ko:
|
||||
text = textData.SourceString;
|
||||
break;
|
||||
case LangEnum.En:
|
||||
text = textData.en;
|
||||
break;
|
||||
case LangEnum.Jp:
|
||||
text = textData.ja;
|
||||
break;
|
||||
default:
|
||||
text = textData.SourceString;
|
||||
break;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
484
UGQApiServer/UGQData/UGQMetaData.cs
Normal file
484
UGQApiServer/UGQData/UGQMetaData.cs
Normal file
@@ -0,0 +1,484 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Xml.Linq;
|
||||
|
||||
|
||||
using Amazon.DynamoDBv2.Model;
|
||||
|
||||
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
using MetaAssets;
|
||||
|
||||
|
||||
using UGQApiServer.Validation;
|
||||
using UGQDataAccess.Service;
|
||||
using UGQDatabase.Models;
|
||||
|
||||
|
||||
namespace UGQApiServer.UGQData;
|
||||
|
||||
public enum PresetKind
|
||||
{
|
||||
None = 0,
|
||||
Title = 1,
|
||||
Banner = 2,
|
||||
}
|
||||
|
||||
public enum PresetCategory
|
||||
{
|
||||
None = 0,
|
||||
Comedy = 1,
|
||||
Drama = 2,
|
||||
Horror = 3,
|
||||
Mystery = 4,
|
||||
Romance = 5,
|
||||
Thriller = 6,
|
||||
All = 7,
|
||||
}
|
||||
|
||||
|
||||
#pragma warning disable CS8618
|
||||
|
||||
public class UGQActionValue
|
||||
{
|
||||
public int? ValueId { get; set; }
|
||||
public string? UgcValueGuid { get; set; }
|
||||
public string ValueName { get; set; } = string.Empty;
|
||||
public string TextId { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class UGQTaskAction
|
||||
{
|
||||
public int ActionId { get; set; }
|
||||
public string ActionName { get; set; } = string.Empty;
|
||||
public string TextId { get; set; } = string.Empty;
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
public List<UGQActionValue> TaskActions { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class UGQDialogCondition
|
||||
{
|
||||
public int? ConditionId { get; set; }
|
||||
public string ConditionName { get; set; } = string.Empty;
|
||||
public string TextId { get; set; } = string.Empty;
|
||||
public List<UGQActionValue> DialogConditionValues { get; set; }
|
||||
}
|
||||
|
||||
public class UGQDialogType
|
||||
{
|
||||
public int TypeId { get; set; }
|
||||
public string TypeName { get; set; } = string.Empty;
|
||||
public string TextId { get; set; } = string.Empty;
|
||||
public List<UGQDialogCondition> DialogConditions { get; set; }
|
||||
}
|
||||
|
||||
#pragma warning restore CS8618
|
||||
|
||||
public class UGQMetaData
|
||||
{
|
||||
public const int TALK_ACTION_ID = 1;
|
||||
|
||||
public Dictionary<int, UGQTaskAction> TackActions = new(); // key: TackActionType
|
||||
public Dictionary<int, UGQDialogType> DialogActions = new(); // key: DialogType
|
||||
|
||||
public IReadOnlyDictionary<int, MetaAssets.UGQPresetImageMetaData> UGQPresetImageMetaDataListbyId =>
|
||||
MetaData.Instance.UGQPresetImageMetaDataListbyId;
|
||||
|
||||
public Dictionary<(PresetKind, PresetCategory), List<MetaAssets.UGQPresetImageMetaData>> PresetImages = new();
|
||||
|
||||
InGameService _inGameService;
|
||||
|
||||
public UGQMetaData(InGameService inGameService)
|
||||
{
|
||||
_inGameService = inGameService;
|
||||
}
|
||||
|
||||
List<UGQActionValue> getInputValues(MetaAssets.EUGQValueSource source)
|
||||
{
|
||||
List<UGQActionValue> values = new();
|
||||
|
||||
switch(source)
|
||||
{
|
||||
case MetaAssets.EUGQValueSource.UGQ_NPC:
|
||||
values = MetaData.Instance._npcTable
|
||||
.Where(x => x.Value.UGQ == true)
|
||||
.Select(x => new UGQActionValue
|
||||
{
|
||||
ValueId = x.Value.npc_id,
|
||||
TextId = x.Value.name,
|
||||
}).ToList();
|
||||
break;
|
||||
case MetaAssets.EUGQValueSource.CLOTH_ITEM:
|
||||
values = MetaData.Instance._ItemTable
|
||||
.Where(x => x.Value.UGQAction == MetaAssets.EUGQAction.CLOTH)
|
||||
.Select(x => new UGQActionValue
|
||||
{
|
||||
ValueId = x.Value.ItemId,
|
||||
TextId = x.Value.Name,
|
||||
}).ToList();
|
||||
break;
|
||||
case MetaAssets.EUGQValueSource.UGQ_ITEM:
|
||||
values = MetaData.Instance._ItemTable
|
||||
.Where(x => x.Value.UGQAction != MetaAssets.EUGQAction.NOTUSE)
|
||||
.Select(x => new UGQActionValue
|
||||
{
|
||||
ValueId = x.Value.ItemId,
|
||||
TextId = x.Value.Name,
|
||||
}).ToList();
|
||||
break;
|
||||
case MetaAssets.EUGQValueSource.UGQ_ATTRIBUTE_DEFINITION:
|
||||
values = MetaData.Instance._AttributeDefinitionMetaTable
|
||||
.Where(x => x.Value.UGQ == true)
|
||||
.Select(x => new UGQActionValue
|
||||
{
|
||||
ValueId = x.Value.ID,
|
||||
TextId = x.Value.Name,
|
||||
}).ToList();
|
||||
break;
|
||||
case MetaAssets.EUGQValueSource.RANGE_1_30:
|
||||
for (int i = 1; i <= 30; i++)
|
||||
{
|
||||
values.Add(new UGQActionValue
|
||||
{
|
||||
ValueId = i,
|
||||
ValueName = i.ToString(),
|
||||
});
|
||||
}
|
||||
break;
|
||||
case MetaAssets.EUGQValueSource.UGQ_SOCIAL_ACTION:
|
||||
values = MetaData.Instance._SocialActionTable
|
||||
.Where(x => x.Value.UGQ == true)
|
||||
.Select(x => new UGQActionValue
|
||||
{
|
||||
ValueId = x.Value.SocialActionId,
|
||||
TextId = x.Value.Content,
|
||||
}).ToList();
|
||||
break;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
void initTaskActions()
|
||||
{
|
||||
foreach(var taskInput in MetaData.Instance.UGQInputTaskMetaDataList)
|
||||
{
|
||||
var action = new UGQTaskAction
|
||||
{
|
||||
ActionId = taskInput.Id,
|
||||
ActionName = taskInput.Name,
|
||||
TextId = taskInput.Name,
|
||||
Disabled = !taskInput.Enabled,
|
||||
TaskActions = getInputValues(taskInput.ValueSource),
|
||||
};
|
||||
|
||||
TackActions.Add(taskInput.Id, action);
|
||||
}
|
||||
}
|
||||
|
||||
void initDialogTypes()
|
||||
{
|
||||
foreach (var dialogInput in MetaData.Instance.UGQInputDialogMetaDataList)
|
||||
{
|
||||
var conditionValues = getInputValues(dialogInput.ConditionValueSource);
|
||||
|
||||
List<UGQDialogCondition> conditions = new();
|
||||
switch (dialogInput.ConditionSelection)
|
||||
{
|
||||
case MetaAssets.EConditionSelection.USE_STATIC:
|
||||
|
||||
MetaData.Instance.UGQInputDialogConditionMetaDataListbyName.TryGetValue(dialogInput.ConditionName, out var dialogCondition);
|
||||
if(dialogCondition == null)
|
||||
{
|
||||
conditions.Add(new UGQDialogCondition
|
||||
{
|
||||
ConditionId = 0,
|
||||
ConditionName = "UGQInput_Dialog_None",
|
||||
TextId = "UGQInput_Dialog_None",
|
||||
DialogConditionValues = conditionValues
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
conditions.Add(new UGQDialogCondition
|
||||
{
|
||||
ConditionId = dialogCondition.Id,
|
||||
ConditionName = dialogCondition.Text,
|
||||
TextId = dialogCondition.Text,
|
||||
DialogConditionValues = conditionValues
|
||||
});
|
||||
}
|
||||
break;
|
||||
case MetaAssets.EConditionSelection.USE_SOURCE:
|
||||
|
||||
var source = getInputValues(dialogInput.ConditionSource);
|
||||
conditions = source.Select(x => new UGQDialogCondition
|
||||
{
|
||||
ConditionId = x.ValueId,
|
||||
ConditionName = x.ValueName,
|
||||
TextId = x.TextId,
|
||||
DialogConditionValues = conditionValues
|
||||
}).ToList();
|
||||
break;
|
||||
}
|
||||
|
||||
var dialogType = new UGQDialogType
|
||||
{
|
||||
TypeId = dialogInput.TypeId,
|
||||
TypeName = dialogInput.TypeName,
|
||||
TextId = dialogInput.TypeName,
|
||||
DialogConditions = conditions,
|
||||
};
|
||||
|
||||
DialogActions.Add(dialogInput.TypeId, dialogType);
|
||||
}
|
||||
}
|
||||
|
||||
public TextEntity getNpcName(int npcId)
|
||||
{
|
||||
TextEntity getNpcNameEntity(string textId)
|
||||
{
|
||||
MetaData.Instance._textTable.TryGetValue(textId, out var textData);
|
||||
if (textData == null)
|
||||
return new TextEntity();
|
||||
|
||||
return new TextEntity
|
||||
{
|
||||
Kr = textData.SourceString,
|
||||
En = textData.en,
|
||||
Jp = textData.ja,
|
||||
};
|
||||
}
|
||||
|
||||
MetaData.Instance._npcTable.TryGetValue(npcId, out var data);
|
||||
if(data != null)
|
||||
{
|
||||
return getNpcNameEntity(data.name);
|
||||
}
|
||||
|
||||
return new TextEntity
|
||||
{
|
||||
Kr = "",
|
||||
En = "",
|
||||
Jp = "",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void init()
|
||||
{
|
||||
initTaskActions();
|
||||
initDialogTypes();
|
||||
|
||||
foreach (var preset in MetaData.Instance.UGQPresetImageMetaDataListbyId.Values)
|
||||
{
|
||||
var pathSplit = preset.FileName.Split('/');
|
||||
|
||||
if (Enum.TryParse(pathSplit[1], true, out PresetCategory category) == false)
|
||||
continue;
|
||||
|
||||
PresetKind kind = PresetKind.None;
|
||||
if (preset.FileName.EndsWith("normal.png") == true)
|
||||
kind = PresetKind.Banner;
|
||||
else if (preset.FileName.EndsWith("hot.png") == true)
|
||||
kind = PresetKind.Title;
|
||||
|
||||
if (kind == PresetKind.None)
|
||||
continue;
|
||||
|
||||
if (PresetImages.TryGetValue((kind, category), out var list) == false)
|
||||
{
|
||||
list = new();
|
||||
PresetImages.Add((kind, category), list);
|
||||
}
|
||||
list.Add(preset);
|
||||
|
||||
if (PresetImages.TryGetValue((kind, PresetCategory.All), out var allList) == false)
|
||||
{
|
||||
allList = new();
|
||||
PresetImages.Add((kind, PresetCategory.All), allList);
|
||||
}
|
||||
allList.Add(preset);
|
||||
}
|
||||
}
|
||||
|
||||
public List<MetaAssets.NpcMetaData> getAssignableNpcs()
|
||||
{
|
||||
return MetaData.Instance._npcTable
|
||||
.Where(x => x.Value.UGQ == true)
|
||||
.Select(x => x.Value).ToList();
|
||||
}
|
||||
|
||||
public string getBeaconName(int beaconId, LangEnum langEnum)
|
||||
{
|
||||
MetaData.Instance._npcTable.TryGetValue(beaconId, out var npcData);
|
||||
if (npcData == null)
|
||||
return string.Empty;
|
||||
|
||||
return UGQDataHelper.getText(langEnum, npcData.name) ?? string.Empty;
|
||||
}
|
||||
|
||||
public string getTaskActionName(int actionId, LangEnum langEnum)
|
||||
{
|
||||
TackActions.TryGetValue(actionId, out var taskAction);
|
||||
if (taskAction == null)
|
||||
return string.Empty;
|
||||
|
||||
return UGQDataHelper.getText(langEnum, taskAction.TextId) ?? string.Empty;
|
||||
}
|
||||
|
||||
public string getTaskActionValueName(int actionId, int actionValueId, LangEnum langEnum)
|
||||
{
|
||||
TackActions.TryGetValue(actionId, out var taskAction);
|
||||
if (taskAction == null)
|
||||
return "";
|
||||
|
||||
var actionValue = taskAction.TaskActions.Where(x => x.ValueId == actionValueId).FirstOrDefault();
|
||||
if (actionValue == null)
|
||||
return "";
|
||||
|
||||
return UGQDataHelper.getText(langEnum, actionValue.TextId) ?? string.Empty;
|
||||
}
|
||||
|
||||
public string getDialogTypeName(int typeId, LangEnum langEnum)
|
||||
{
|
||||
DialogActions.TryGetValue(typeId, out var dialogType);
|
||||
if (dialogType == null)
|
||||
return "";
|
||||
|
||||
return UGQDataHelper.getText(langEnum, dialogType.TextId) ?? string.Empty;
|
||||
}
|
||||
|
||||
public string getDialogConditionName(int typeId, int conditionId, LangEnum langEnum)
|
||||
{
|
||||
DialogActions.TryGetValue(typeId, out var dialogType);
|
||||
if (dialogType == null)
|
||||
return "";
|
||||
|
||||
var dialogCondition = dialogType.DialogConditions.Where(x => x.ConditionId == conditionId).FirstOrDefault();
|
||||
if (dialogCondition == null)
|
||||
return "";
|
||||
|
||||
return UGQDataHelper.getText(langEnum, dialogCondition.TextId) ?? string.Empty;
|
||||
}
|
||||
|
||||
public string getDialogConditionValueName(int typeId, int conditionId, int conditionValue, LangEnum langEnum)
|
||||
{
|
||||
DialogActions.TryGetValue(typeId, out var dialogType);
|
||||
if (dialogType == null)
|
||||
return "";
|
||||
|
||||
var dialogCondition = dialogType.DialogConditions.Where(x => x.ConditionId == conditionId).FirstOrDefault();
|
||||
if (dialogCondition == null)
|
||||
return "";
|
||||
|
||||
var dialogConditionValue = dialogCondition.DialogConditionValues.Where(x => x.ValueId == conditionValue).FirstOrDefault();
|
||||
if (dialogConditionValue == null)
|
||||
return "";
|
||||
|
||||
return UGQDataHelper.getText(langEnum, dialogConditionValue.TextId) ?? string.Empty;
|
||||
}
|
||||
|
||||
public ValidationErrorKind isValidNpcId(int npcId)
|
||||
{
|
||||
if (MetaData.Instance._npcTable.TryGetValue(npcId, out var result) == false)
|
||||
return ValidationErrorKind.NotFoundBeaconId;
|
||||
|
||||
return ValidationErrorKind.Success;
|
||||
}
|
||||
|
||||
public ValidationErrorKind isValidDialogTypeId(int dialogTypeId)
|
||||
{
|
||||
DialogActions.TryGetValue((int)dialogTypeId, out var dialogType);
|
||||
if (dialogType == null)
|
||||
return ValidationErrorKind.InvalidDialogType;
|
||||
|
||||
return ValidationErrorKind.Success;
|
||||
}
|
||||
|
||||
public ValidationErrorKind isValidDialogConditionId(int dialogTypeId, int conditionId)
|
||||
{
|
||||
// DialogActions<6E><73><EFBFBD><EFBFBD> dialogType <20>˻<EFBFBD>
|
||||
DialogActions.TryGetValue((int)dialogTypeId, out var dialogType);
|
||||
if (dialogType == null)
|
||||
return ValidationErrorKind.InvalidDialogType;
|
||||
|
||||
var dialogCondition = dialogType.DialogConditions.Where(x => x.ConditionId == conditionId).FirstOrDefault();
|
||||
if (dialogCondition == null)
|
||||
return ValidationErrorKind.InvalidDialogConditionId;
|
||||
|
||||
return ValidationErrorKind.Success;
|
||||
}
|
||||
|
||||
public ValidationErrorKind isValidDialogActionValue(int dialogTypeId, int conditionId, int conditionValue)
|
||||
{
|
||||
// DialogActions<6E><73><EFBFBD><EFBFBD> dialogType, dialogActionType <20>˻<EFBFBD>
|
||||
// dialogActionId<49><64> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> üũ
|
||||
DialogActions.TryGetValue((int)dialogTypeId, out var dialogType);
|
||||
if (dialogType == null)
|
||||
return ValidationErrorKind.InvalidDialogType;
|
||||
|
||||
var dialogCondition = dialogType.DialogConditions.Where(x => x.ConditionId == conditionId).FirstOrDefault();
|
||||
if (dialogCondition == null)
|
||||
return ValidationErrorKind.InvalidDialogConditionId;
|
||||
|
||||
// DialogConditionValues<65><73> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> üũ
|
||||
if (dialogCondition.DialogConditionValues.Count > 0)
|
||||
{
|
||||
var dialogConditionValue = dialogCondition.DialogConditionValues.Where(x => x.ValueId == conditionValue).FirstOrDefault();
|
||||
if (dialogConditionValue == null)
|
||||
return ValidationErrorKind.InvalidDialogConditionValue;
|
||||
}
|
||||
|
||||
return ValidationErrorKind.Success;
|
||||
}
|
||||
|
||||
public ValidationErrorKind isValidNpcActionId(int actionId)
|
||||
{
|
||||
// TackActionType
|
||||
MetaData.Instance.UGQBeaconActionDataListbyId.TryGetValue(actionId, out var npcAction);
|
||||
if (npcAction == null)
|
||||
return ValidationErrorKind.InvalidNpcActionId;
|
||||
|
||||
return ValidationErrorKind.Success;
|
||||
}
|
||||
|
||||
public ValidationErrorKind isValidTackActionId(int actionId)
|
||||
{
|
||||
// TackActionType
|
||||
TackActions.TryGetValue(actionId, out var tackAction);
|
||||
if (tackAction == null)
|
||||
return ValidationErrorKind.InvalidTaskActionId;
|
||||
|
||||
if (tackAction.Disabled == true)
|
||||
return ValidationErrorKind.InvalidTaskActionValue;
|
||||
|
||||
return ValidationErrorKind.Success;
|
||||
}
|
||||
|
||||
public ValidationErrorKind isValidTackActionValue(int actionId, int actionValue)
|
||||
{
|
||||
// TackActions<6E><73><EFBFBD><EFBFBD> TackActionType <20>˻<EFBFBD>
|
||||
TackActions.TryGetValue(actionId, out var tackAction);
|
||||
if (tackAction == null)
|
||||
return ValidationErrorKind.InvalidTaskActionId;
|
||||
|
||||
// actionValue<75><65> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> üũ
|
||||
var taskActionValue = tackAction.TaskActions.Where(x => x.ValueId == actionValue).FirstOrDefault();
|
||||
if (taskActionValue == null)
|
||||
return ValidationErrorKind.InvalidTaskActionValue;
|
||||
|
||||
return ValidationErrorKind.Success;
|
||||
}
|
||||
|
||||
public MetaAssets.UGQPresetImageMetaData? getPresetImage(int id)
|
||||
{
|
||||
UGQPresetImageMetaDataListbyId.TryGetValue(id, out var presetImage);
|
||||
return presetImage;
|
||||
}
|
||||
}
|
||||
|
||||
478
UGQApiServer/Validation/UGQValidator.cs
Normal file
478
UGQApiServer/Validation/UGQValidator.cs
Normal file
@@ -0,0 +1,478 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
|
||||
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Pipelines.Sockets.Unofficial.Arenas;
|
||||
|
||||
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
using UGQDataAccess;
|
||||
using UGQDatabase.Models;
|
||||
using UGQApiServer.Converter;
|
||||
using UGQApiServer.Models;
|
||||
using UGQApiServer.UGQData;
|
||||
|
||||
|
||||
namespace UGQApiServer.Validation;
|
||||
|
||||
public enum ValidationErrorKind
|
||||
{
|
||||
Success,
|
||||
RequireBeaconId, // <20><><EFBFBD><EFBFBD> <20>Է<EFBFBD> <20>ʿ<EFBFBD>
|
||||
NotFoundBeaconId, // <20>Էµ<D4B7> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>߸<EFBFBD><DFB8><EFBFBD> <20><><EFBFBD>̵<EFBFBD>
|
||||
FirstTaskIsNotTalkAction, // ù<><C3B9><EFBFBD><EFBFBD> <20>½<EFBFBD>ũ <20><>ȭ <20><EFBFBD><D7BC><EFBFBD> <20>ƴ<EFBFBD>
|
||||
FirstTaskIsNotSameBeacon, // ù<><C3B9><EFBFBD><EFBFBD> <20>½<EFBFBD>ũ<EFBFBD><C5A9> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ٸ<EFBFBD>
|
||||
InvalidCostAmount, // <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
|
||||
|
||||
|
||||
InvalidDialogType, // <20>Է<EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
InvalidDialogConditionId, // <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
InvalidDialogConditionValue, // <20><><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD>
|
||||
InvalidDialogNextSequence, // <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD>
|
||||
InvalidSequenceCount, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 3~30<33><30> <20><><EFBFBD><EFBFBD>
|
||||
DontHaveEndSequence, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>ᰡ <20><><EFBFBD><EFBFBD>
|
||||
NotSetPlayerTalker, // Player Talker<65><72> <20><><EFBFBD>õǾ<C3B5> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
InvalidTalkerOrder, // Npc <20>ڿ<EFBFBD> Player<65><72> <20><> <20><> <20><><EFBFBD><EFBFBD>
|
||||
|
||||
|
||||
InvalidTaskActionId, // Task <20><EFBFBD> Ÿ<><C5B8> <20><><EFBFBD><EFBFBD>
|
||||
InvalidTaskActionValue, // Task <20><EFBFBD> <20><> <20><><EFBFBD><EFBFBD>
|
||||
InvalidTaskCount, // Task <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 3~20<32><30> <20><><EFBFBD><EFBFBD>
|
||||
DontHaveTitleImage, // Ÿ<><C5B8>Ʋ <20>̹<EFBFBD><CCB9><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
DontHaveBannerImage, // <20><><EFBFBD><EFBFBD> <20>̹<EFBFBD><CCB9><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
|
||||
InvalidNpcActionId, // Npc <20><EFBFBD> Ÿ<><C5B8> <20><><EFBFBD><EFBFBD>
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class ValidationError
|
||||
{
|
||||
public ValidationErrorKind Kind { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class QuestDialogValidatorError : ValidationError
|
||||
{
|
||||
public int TaskIndex { get; set; } = -1;
|
||||
public int SequenceId { get; set; } = -1; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><EFBFBD><DFBB><EFBFBD> SequenceId.
|
||||
public int SequenceActionIndex { get; set; } = -1; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><EFBFBD><DFBB><EFBFBD> SequenceAction <20><>ġ. 0 <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>. -1<><31> <20><EFBFBD> <20><><EFBFBD><EFBFBD> <20>ƴ<EFBFBD>.
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// Kind<6E><64> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DEBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ؼ<EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
|
||||
string position = "";
|
||||
if (SequenceActionIndex != -1)
|
||||
{
|
||||
position = $"[TaskIndex: {TaskIndex}, SequenceId: {SequenceId}, SequenceActionIndex: {SequenceActionIndex}] ";
|
||||
}
|
||||
else if (SequenceId != -1)
|
||||
{
|
||||
position = $"[TaskIndex: {TaskIndex}, SequenceId: {SequenceId}] ";
|
||||
}
|
||||
else
|
||||
{
|
||||
position = $"[TaskIndex: {TaskIndex}] ";
|
||||
}
|
||||
|
||||
var textId = $"UGQ_Validator_Error_{Kind.ToString()}";
|
||||
var message = UGQDataHelper.getText(langEnum, textId);
|
||||
|
||||
return $"{position}{message}";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class QuestContentValidatorError : ValidationError
|
||||
{
|
||||
public int TaskIndex { get; set; } = -1; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><EFBFBD><DFBB><EFBFBD> Task <20><>ġ. 0 <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// Kind<6E><64> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DEBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ؼ<EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
LangEnum langEnum = Culture.ToLangEnum(CultureInfo.CurrentCulture.Name);
|
||||
|
||||
string position = "";
|
||||
if (TaskIndex != -1)
|
||||
{
|
||||
position = $"[TaskIndex: {TaskIndex}] ";
|
||||
}
|
||||
else
|
||||
{
|
||||
position = "";
|
||||
}
|
||||
|
||||
var textId = $"UGQ_Validator_Error_{Kind.ToString()}";
|
||||
var message = UGQDataHelper.getText(langEnum, textId);
|
||||
|
||||
return $"{position}{message ?? "Unkonwn"}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class UGQValidator
|
||||
{
|
||||
UGQMetaData _ugqMetadata;
|
||||
|
||||
int MIN_SEQUENCE = 3;
|
||||
int MAX_SEQUENCE = 30;
|
||||
|
||||
int MIN_TASK = 3;
|
||||
int MAX_TASK = 20;
|
||||
|
||||
|
||||
public UGQValidator(UGQMetaData ugqMetadata)
|
||||
{
|
||||
_ugqMetadata = ugqMetadata;
|
||||
}
|
||||
|
||||
public List<ValidationError> Validate(QuestContentEntity questContent, List<QuestDialogEntity> questDialogs)
|
||||
{
|
||||
List<ValidationError> errors = new();
|
||||
|
||||
ValidateQuestContent(questContent, errors);
|
||||
|
||||
|
||||
// <20><><EFBFBD>̾<EFBFBD><CCBE>α<EFBFBD> üũ
|
||||
foreach (var (index, task) in questContent.Tasks.Select((task, index) => (index, task)))
|
||||
{
|
||||
var dialogIndex = questDialogs.FindIndex(x => x.Id == task.DialogId);
|
||||
|
||||
if (task.ActionId == UGQMetaData.TALK_ACTION_ID)
|
||||
{
|
||||
if (dialogIndex == -1)
|
||||
{
|
||||
// Talk <20><EFBFBD><D7BC><EFBFBD> <20><><EFBFBD><EFBFBD> Dialog<6F><67> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||||
QuestContentValidatorError error = new QuestContentValidatorError
|
||||
{
|
||||
Kind = ValidationErrorKind.InvalidSequenceCount,
|
||||
TaskIndex = index,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
continue;
|
||||
}
|
||||
|
||||
var questDialog = questDialogs[dialogIndex];
|
||||
|
||||
// sequence <20><><EFBFBD><EFBFBD> üũ
|
||||
if (questDialog.Sequences.Count < MIN_SEQUENCE || questDialog.Sequences.Count >= MAX_SEQUENCE)
|
||||
{
|
||||
QuestContentValidatorError error = new QuestContentValidatorError
|
||||
{
|
||||
Kind = ValidationErrorKind.InvalidSequenceCount,
|
||||
TaskIndex = index,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
|
||||
ValidateQuestDialog(index, questDialog, errors);
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
int getCost(UgqGradeType gradeType)
|
||||
{
|
||||
switch (gradeType)
|
||||
{
|
||||
case UgqGradeType.Amature:
|
||||
return MetaHelper.GameConfigMeta.UgqUsageFeeAmateur;
|
||||
case UgqGradeType.RisingStar:
|
||||
return MetaHelper.GameConfigMeta.UgqUsageFeeRisingStar;
|
||||
case UgqGradeType.Master1:
|
||||
return MetaHelper.GameConfigMeta.UgqUsageFeeMaster1;
|
||||
case UgqGradeType.Master2:
|
||||
return MetaHelper.GameConfigMeta.UgqUsageFeeMaster2;
|
||||
case UgqGradeType.Master3:
|
||||
return MetaHelper.GameConfigMeta.UgqUsageFeeMaster3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ValidateQuestContent(QuestContentEntity questContent, List<ValidationError> errors)
|
||||
{
|
||||
if(questContent.BeaconId == 0 && string.IsNullOrEmpty(questContent.UgcBeaconGuid) == true)
|
||||
{
|
||||
QuestContentValidatorError error = new QuestContentValidatorError
|
||||
{
|
||||
Kind = ValidationErrorKind.RequireBeaconId,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
|
||||
if(questContent.BeaconId != 0)
|
||||
{
|
||||
var errorKind = _ugqMetadata.isValidNpcId(questContent.BeaconId);
|
||||
if (errorKind != ValidationErrorKind.Success)
|
||||
{
|
||||
QuestContentValidatorError error = new QuestContentValidatorError
|
||||
{
|
||||
Kind = errorKind,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (questContent.Tasks.Count < MIN_TASK || questContent.Tasks.Count >= MAX_TASK)
|
||||
{
|
||||
QuestContentValidatorError error = new QuestContentValidatorError
|
||||
{
|
||||
Kind = ValidationErrorKind.InvalidTaskCount,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(questContent.TitleImagePath) == true)
|
||||
{
|
||||
QuestContentValidatorError error = new QuestContentValidatorError
|
||||
{
|
||||
Kind = ValidationErrorKind.DontHaveTitleImage,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(questContent.BannerImagePath) == true)
|
||||
{
|
||||
QuestContentValidatorError error = new QuestContentValidatorError
|
||||
{
|
||||
Kind = ValidationErrorKind.DontHaveBannerImage,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
|
||||
//if (questContent.Cost != getCost(questContent.GradeType))
|
||||
//{
|
||||
// QuestContentValidatorError error = new QuestContentValidatorError
|
||||
// {
|
||||
// Kind = ValidationErrorKind.InvalidCostAmount,
|
||||
// };
|
||||
|
||||
// errors.Add(error);
|
||||
//}
|
||||
|
||||
if (questContent.Tasks[0].ActionId != 1)
|
||||
{
|
||||
QuestContentValidatorError error = new QuestContentValidatorError
|
||||
{
|
||||
Kind = ValidationErrorKind.FirstTaskIsNotTalkAction,
|
||||
TaskIndex = 0,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
|
||||
bool validTalkBeacon = false;
|
||||
if (questContent.BeaconId != 0 && questContent.Tasks[0].ActionValue == questContent.BeaconId)
|
||||
validTalkBeacon = true;
|
||||
|
||||
if (string.IsNullOrEmpty(questContent.UgcBeaconGuid) == false && questContent.Tasks[0].UgcActionValueGuid == questContent.UgcBeaconGuid)
|
||||
validTalkBeacon = true;
|
||||
|
||||
if(validTalkBeacon == false)
|
||||
{
|
||||
QuestContentValidatorError error = new QuestContentValidatorError
|
||||
{
|
||||
Kind = ValidationErrorKind.FirstTaskIsNotSameBeacon,
|
||||
TaskIndex = 0,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
|
||||
|
||||
foreach (var (index, task) in questContent.Tasks.Select((task, index) => (index, task)))
|
||||
{
|
||||
if (string.IsNullOrEmpty(task.UgcActionValueGuid) == false)
|
||||
{
|
||||
var errorKind = _ugqMetadata.isValidTackActionId(task.ActionId);
|
||||
if (errorKind != ValidationErrorKind.Success)
|
||||
{
|
||||
QuestContentValidatorError error = new QuestContentValidatorError
|
||||
{
|
||||
Kind = errorKind,
|
||||
TaskIndex = index,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var errorKind = _ugqMetadata.isValidTackActionValue(task.ActionId, task.ActionValue);
|
||||
if (errorKind != ValidationErrorKind.Success)
|
||||
{
|
||||
QuestContentValidatorError error = new QuestContentValidatorError
|
||||
{
|
||||
Kind = errorKind,
|
||||
TaskIndex = index,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ValidateQuestDialogSequenceAction(int taskIndex, List<int> sequenceIds, int sequenceId, int actionIdex, DialogSequenceActionEntity sequenceAction, List<ValidationError> errors)
|
||||
{
|
||||
if (sequenceAction.Talker == DialogTalker.Player)
|
||||
{
|
||||
// Talker<65><72> <20>÷<EFBFBD><C3B7>̾<EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> üũ
|
||||
|
||||
var errorKind = _ugqMetadata.isValidDialogActionValue(sequenceAction.Type, sequenceAction.Condition, sequenceAction.ConditionValue);
|
||||
if (errorKind != ValidationErrorKind.Success)
|
||||
{
|
||||
QuestDialogValidatorError error = new QuestDialogValidatorError
|
||||
{
|
||||
TaskIndex = taskIndex,
|
||||
Kind = errorKind,
|
||||
SequenceId = sequenceId,
|
||||
SequenceActionIndex = actionIdex,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
}
|
||||
else if(sequenceAction.Talker == DialogTalker.Npc && sequenceAction.NpcAction != 0)
|
||||
{
|
||||
var errorKind = _ugqMetadata.isValidNpcActionId(sequenceAction.NpcAction);
|
||||
if (errorKind != ValidationErrorKind.Success)
|
||||
{
|
||||
QuestDialogValidatorError error = new QuestDialogValidatorError
|
||||
{
|
||||
TaskIndex = taskIndex,
|
||||
Kind = errorKind,
|
||||
SequenceId = sequenceId,
|
||||
SequenceActionIndex = actionIdex,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (sequenceId != -1)
|
||||
{
|
||||
if (sequenceIds.Any(x => x == sequenceId) == false)
|
||||
{
|
||||
QuestDialogValidatorError error = new QuestDialogValidatorError
|
||||
{
|
||||
TaskIndex = taskIndex,
|
||||
Kind = ValidationErrorKind.InvalidDialogNextSequence,
|
||||
SequenceId = sequenceId,
|
||||
SequenceActionIndex = actionIdex,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ValidateQuestDialogSequence(int taskIndex, List<int> sequenceIds, DialogSequenceEntity sequence, List<ValidationError> errors)
|
||||
{
|
||||
foreach (var (index, action) in sequence.Actions.Select((action, index) => (index, action)))
|
||||
{
|
||||
ValidateQuestDialogSequenceAction(taskIndex, sequenceIds, sequence.SequenceId, index, action, errors);
|
||||
}
|
||||
|
||||
int playerTalkerCount = 0;
|
||||
int npcTalkerCount = 0;
|
||||
bool invalidTalkerOrder = false;
|
||||
|
||||
foreach (var (index, action) in sequence.Actions.Select((action, index) => (index, action)))
|
||||
{
|
||||
if (action.Talker == DialogTalker.Player)
|
||||
{
|
||||
playerTalkerCount++;
|
||||
}
|
||||
else if (action.Talker == DialogTalker.Npc)
|
||||
{
|
||||
// Npc<70>ε<EFBFBD>, playerTalkerCount<6E><74> 0<><30><EFBFBD><EFBFBD> ũ<>ٸ<EFBFBD>, Player<65>ڿ<EFBFBD> Npc<70><63> <20><><EFBFBD>Դٴ<D4B4> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> ó<><C3B3>
|
||||
if (playerTalkerCount > 0)
|
||||
invalidTalkerOrder = true;
|
||||
|
||||
npcTalkerCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (playerTalkerCount < 1)
|
||||
{
|
||||
QuestDialogValidatorError error = new QuestDialogValidatorError
|
||||
{
|
||||
TaskIndex = taskIndex,
|
||||
Kind = ValidationErrorKind.NotSetPlayerTalker,
|
||||
SequenceId = sequence.SequenceId,
|
||||
};
|
||||
}
|
||||
|
||||
if (invalidTalkerOrder == true)
|
||||
{
|
||||
QuestDialogValidatorError error = new QuestDialogValidatorError
|
||||
{
|
||||
TaskIndex = taskIndex,
|
||||
Kind = ValidationErrorKind.InvalidTalkerOrder,
|
||||
SequenceId = sequence.SequenceId,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void ValidateQuestDialog(int taskIndex, QuestDialogEntity questDialog, List<ValidationError> errors)
|
||||
{
|
||||
var sequenceIds = questDialog.Sequences.Select(x => x.SequenceId).ToList();
|
||||
|
||||
List<int> npcActionIds = new();
|
||||
var dialogActions = questDialog.Sequences.Select(x => x.Actions).ToList();
|
||||
foreach(var item in dialogActions)
|
||||
npcActionIds.AddRange(item.Select(x => x.NpcAction).ToList());
|
||||
|
||||
bool hasEnd = false;
|
||||
foreach (var sequence in questDialog.Sequences)
|
||||
{
|
||||
ValidateQuestDialogSequence(taskIndex, sequenceIds, sequence, errors);
|
||||
|
||||
foreach (var action in sequence.Actions)
|
||||
{
|
||||
if(action.NextSequence == -1)
|
||||
hasEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasEnd == false)
|
||||
{
|
||||
QuestDialogValidatorError error = new QuestDialogValidatorError
|
||||
{
|
||||
TaskIndex = taskIndex,
|
||||
Kind = ValidationErrorKind.DontHaveEndSequence,
|
||||
};
|
||||
|
||||
errors.Add(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
38
UGQApiServer/appsettings.AWSDev.json
Normal file
38
UGQApiServer/appsettings.AWSDev.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"UGQDatabase": {
|
||||
"ConnectionString": "mongodb://ip-172-50-167-39.us-west-2.compute.internal:27017",
|
||||
"DatabaseName": "UGQ",
|
||||
"MinConnectionPoolSize": 100,
|
||||
"MaxConnectionPoolSize": 500,
|
||||
"WaitQueueTimeoutSecs": 120
|
||||
},
|
||||
"SSOAccount": {
|
||||
"WebPortalTokenSecret": "zgoRtipbFcgQp0VGP8VZW8QhW4ll1swfvASqwr78",
|
||||
"SsoAccountDb": "Server=dev-caliverse-db.cluster-custom-czac0we0qoyx.us-west-2.rds.amazonaws.com;Port=3306;User ID=external_ro;Password=bQNEXbRWQTtV6bwlqktGyBiuf2KqYF;Database=caliverse"
|
||||
},
|
||||
"JWT": {
|
||||
"ValidAudience": "",
|
||||
"ValidIssuer": "",
|
||||
"Secret": "5bb3238d7f19456329597a9cc57da0f19d57c8c8412c5978cf938396b82060253c706217719e748908cb87a228302c29240183caa63a1c3db20b94e638520740",
|
||||
"TokenValidityInMinutes": 60
|
||||
},
|
||||
"S3": {
|
||||
"AccessKey": "AKIA4G3CB4Z5Y3NL2ANF",
|
||||
"SecretAccessKey": "dD0ivF111vwJByvLpqOpZkGPwmhOuNSZzxzTEY3s",
|
||||
"BucketName": "metaverse-ugq-image",
|
||||
"RootFolder": "Dev",
|
||||
"DownloadUrl": "https://d11pmt9vsv778p.cloudfront.net"
|
||||
},
|
||||
"DynamoDb": {
|
||||
"Url": "http://121.135.164.17:8000",
|
||||
"TablePostfix": "Dev",
|
||||
"AccessKey": "AKIA4G3CB4Z5T6JUPHJN",
|
||||
"SecretKey": "G82Bq5tCUTvSPe9InGayH8kONbtEnLxMrgzrAbCn",
|
||||
"Region": "us-west-2"
|
||||
},
|
||||
"Redis": "121.135.164.17:6379,password=KT-i5#i%-%LxKfZ5YJj6,AsyncTimeout=30000,SyncTimeout=30000,ssl=false,abortConnect=false",
|
||||
"NamedPipe": {
|
||||
"enable": false
|
||||
},
|
||||
"EnableAllowWhenSingleLogin": true
|
||||
}
|
||||
25
UGQApiServer/appsettings.Development.json
Normal file
25
UGQApiServer/appsettings.Development.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"UGQDatabase": {
|
||||
"ConnectionString": "mongodb://root:root@127.0.0.1:27017",
|
||||
// "ConnectionString": "mongodb://127.0.0.1:27017",
|
||||
"DatabaseName": "UGQ",
|
||||
"MinConnectionPoolSize": 100,
|
||||
"MaxConnectionPoolSize": 500,
|
||||
"WaitQueueTimeoutSecs": 120
|
||||
},
|
||||
"JWT": {
|
||||
"ValidAudience": "",
|
||||
"ValidIssuer": "",
|
||||
"Secret": "5bb3238d7f19456329597a9cc57da0f19d57c8c8412c5978cf938396b82060253c706217719e748908cb87a228302c29240183caa63a1c3db20b94e638520740",
|
||||
"TokenValidityInMinutes": 3000
|
||||
},
|
||||
"NamedPipe": {
|
||||
"enable": false
|
||||
}
|
||||
}
|
||||
45
UGQApiServer/appsettings.json
Normal file
45
UGQApiServer/appsettings.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"UGQDatabase": {
|
||||
"ConnectionString": "mongodb://root:root@127.0.0.1:27018",
|
||||
"DatabaseName": "UGQ",
|
||||
"MinConnectionPoolSize": 100,
|
||||
"MaxConnectionPoolSize": 500,
|
||||
"WaitQueueTimeoutSecs": 120
|
||||
},
|
||||
"SSOAccount": {
|
||||
"WebPortalTokenSecret": "Gudx7xjCbCKxZsLEWr7HL5auSkScVPTUaYnZEztN",
|
||||
"SsoAccountDb": "Server=localhost;Port=13306;User ID=external_ro;Password=bQNEXbRWQTtV6bwlqktGyBiuf2KqYF;Database=caliverse"
|
||||
},
|
||||
"JWT": {
|
||||
"ValidAudience": "",
|
||||
"ValidIssuer": "",
|
||||
"Secret": "5bb3238d7f19456329597a9cc57da0f19d57c8c8412c5978cf938396b82060253c706217719e748908cb87a228302c29240183caa63a1c3db20b94e638520740",
|
||||
"TokenValidityInMinutes": 3000
|
||||
},
|
||||
"S3": {
|
||||
"AccessKey": "AKIA4G3CB4Z5Y3NL2ANF",
|
||||
"SecretAccessKey": "dD0ivF111vwJByvLpqOpZkGPwmhOuNSZzxzTEY3s",
|
||||
"BucketName": "metaverse-ugq-image",
|
||||
"RootFolder": "Dev",
|
||||
"DownloadUrl": "https://d11pmt9vsv778p.cloudfront.net"
|
||||
},
|
||||
"DynamoDb": {
|
||||
"Url": "http://localhost:8000",
|
||||
"TablePostfix": "Dev",
|
||||
"AccessKey": "AKIA4G3CB4Z5T6JUPHJN",
|
||||
"SecretKey": "G82Bq5tCUTvSPe9InGayH8kONbtEnLxMrgzrAbCn",
|
||||
"Region": "us-west-2"
|
||||
},
|
||||
"Redis": "127.0.0.1:6379,password=KT-i5#i%-%LxKfZ5YJj6,AsyncTimeout=30000,SyncTimeout=30000,ssl=false,abortConnect=false",
|
||||
"NamedPipe": {
|
||||
"enable": true
|
||||
},
|
||||
"EnableAllowWhenSingleLogin": true
|
||||
}
|
||||
Reference in New Issue
Block a user