using MetaAssets; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.Logging; using ServerBase; using ServerCommon; namespace BrokerApiCore; //========================================================================================= // 플래닛의 아이템을 칼리버스 재화로 교환하는 주문 생성 // Broker의 rdb(순수 교환 정보)와 DynamoDB(유저의 게임정보)를 사용하여 교환 주문을 생성한다. // 유저의 게임 정보에서 재화가 차감되면 //========================================================================================= public class CurrencyExchangeCreationStrategy : IOrderCreationStrategy { private readonly PlanetUserEntity m_planet_user_entity; private readonly MetaverseBrokerDbContext m_broker_db_context; private readonly PlanetItemExchangeOrderRepo m_planet_item_exchange_repo; private readonly PlanetItemExchangeOrderAmountTotalLimitRepo m_planet_item_exchange_order_amount_total_limit; private readonly PlanetItemExchangeOrderAmountUserLimitRepo m_planet_item_exchange_order_amount_user_limit; private readonly DynamoDbClient m_dynamo_db_client; private readonly EchoSystemService m_echo_system_service; private readonly ILogger? m_logger; public CurrencyExchangeCreationStrategy( IServerLogic serverLogic, PlanetUserEntity planetUserEntity, MetaverseBrokerDbContext brokerDbContext, PlanetItemExchangeOrderRepo planetItemExchangeRepo, PlanetItemExchangeOrderAmountTotalLimitRepo planetItemExchangeOrderAmountTotalLimit, PlanetItemExchangeOrderAmountUserLimitRepo planetItemExchangeOrderAmountUserLimit, EchoSystemService echoSystemService, ILogger? logger = null!) { m_planet_user_entity = planetUserEntity; m_broker_db_context = brokerDbContext; m_planet_item_exchange_repo = planetItemExchangeRepo; m_planet_item_exchange_order_amount_total_limit = planetItemExchangeOrderAmountTotalLimit; m_planet_item_exchange_order_amount_user_limit = planetItemExchangeOrderAmountUserLimit; m_dynamo_db_client = serverLogic.getDynamoDbClient(); m_echo_system_service = echoSystemService; m_logger = logger; } public async Task<(Result, PlanetItemExchangeOrder)> createOrder( PlanetItemExchangeOrder order, string planetServerType, PlanetItemExchangePolicyMetaData exchangePolicy, CancellationToken cancellationToken = default) { var exchange_date = DateOnly.FromDateTime(order.CreatedAt.ToUniversalTime()); var exchange_amount = order.ExchangeMetaAmount; var strategy = m_broker_db_context.Database.CreateExecutionStrategy(); var execute_result = await strategy.ExecuteAsync(async () => { var transaction_result = new Result(); await using var transaction = await m_broker_db_context.Database.BeginTransactionAsync(cancellationToken); try { var daily_limit_checker = new DailyAmountLimitChecker( m_planet_item_exchange_order_amount_total_limit, m_planet_item_exchange_order_amount_user_limit); var (result, total_limit, user_limit) = await daily_limit_checker.dailyLimitCheck(order, exchangePolicy, cancellationToken); // 신규 주문 정보 삽입 await m_planet_item_exchange_repo.add(order, cancellationToken); Guard.Against.resultFail(result); Guard.Against.isNull(total_limit, ServerErrorCode.RdbError, ()=>"PlanetItemExchangeOrderAmountTotalLimit not found"); Guard.Against.isNull(user_limit, ServerErrorCode.RdbError, ()=>"PlanetItemExchangeOrderAmountUserLimit not found"); var (currency_result, _) = await updateCurrency(exchange_amount, exchangePolicy); Guard.Against.resultFail(currency_result); await m_planet_item_exchange_order_amount_total_limit.increaseDailyAmount(total_limit, exchange_amount, cancellationToken); await m_planet_item_exchange_order_amount_user_limit.increaseDailyAmount(user_limit, exchange_amount, cancellationToken); await transaction.CommitAsync(cancellationToken); // 사파이어 처리인 경우 에코시스템에 통보 if (string.Equals(exchangePolicy.CaliverseItemId, CurrencyType.Sapphire.ToString(), StringComparison.OrdinalIgnoreCase)) { var sapphire_delta = exchange_amount * exchangePolicy.CaliverseItemAmount; _ = notifyToEchoSystemEvent(planetServerType, sapphire_delta).ConfigureAwait(false); } } catch { if (transaction.GetDbTransaction().Connection is not null) { await transaction.RollbackAsync(cancellationToken); } throw; } return transaction_result; }); return (execute_result, order); } private async Task<(Result, long)> updateCurrency( int exchangeMetaAmount, PlanetItemExchangePolicyMetaData exchangePolicy) { var currency_delta = exchangeMetaAmount * exchangePolicy.CaliverseItemAmount; CurrencyControlHelper.setDbConnector(m_dynamo_db_client); var (result, current_sapphire_amount_double) = await CurrencyControlHelper.spendMoneyByUserGuid(getUserGuid(), CurrencyType.Sapphire, currency_delta); var current_sapphire_amount = Convert.ToInt64(current_sapphire_amount_double); return (result, current_sapphire_amount); } //=========================================================================================== // 사파이어 처리인 경우 외부에 있는 에코시스템에 이벤트를 통보하여야 한다. //=========================================================================================== private async Task notifyToEchoSystemEvent( string planetServerType, long sapphireDelta) { try { await m_echo_system_service.createAndSetEventPayload( accountId: getUserAccountId(), nickname: getUserNickname(), userGuid: getUserGuid(), sapphireDelta, planetServerType, CaliumEventType.extra_get.ToString() ); _ = await m_echo_system_service.processCaliumEvent(m_planet_user_entity); } catch (Exception ex) { m_logger?.LogError(ex, "Failed to process calium event"); throw; } } private string getUserAccountId() => m_planet_user_entity.getEntityAttributeNotNull().AccountId; private string getUserGuid() => m_planet_user_entity.getEntityAttributeNotNull().UserGuid; private string getUserNickname() => m_planet_user_entity.getEntityAttributeNotNull().Nickname; }