Files
caliverse_server/UGQApiServer/Controllers/DevelopmentController.cs
2025-05-01 07:20:41 +09:00

599 lines
23 KiB
C#

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
}