초기커밋
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
using BrokerCore.DbEntity;
|
||||
using BrokerCore.Entity;
|
||||
using BrokerCore.Repository;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
// using Polly;
|
||||
// using Polly.Retry;
|
||||
using MetaAssets;
|
||||
|
||||
using ServerCommon;
|
||||
|
||||
namespace BrokerCore.Services;
|
||||
using Common;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Repository.Context;
|
||||
using ServerCore; using ServerBase;
|
||||
|
||||
//=========================================================================================
|
||||
// 플래닛의 아이템을 칼리버스 재화로 교환하는 주문 생성
|
||||
// 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<CurrencyExchangeCreationStrategy>? m_logger;
|
||||
|
||||
public CurrencyExchangeCreationStrategy(
|
||||
IServerLogic serverLogic,
|
||||
PlanetUserEntity planetUserEntity,
|
||||
MetaverseBrokerDbContext brokerDbContext,
|
||||
PlanetItemExchangeOrderRepo planetItemExchangeRepo,
|
||||
PlanetItemExchangeOrderAmountTotalLimitRepo planetItemExchangeOrderAmountTotalLimit,
|
||||
PlanetItemExchangeOrderAmountUserLimitRepo planetItemExchangeOrderAmountUserLimit,
|
||||
EchoSystemService echoSystemService,
|
||||
ILogger<CurrencyExchangeCreationStrategy>? 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<Result>(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<AccountAttribute>().AccountId;
|
||||
|
||||
private string getUserGuid() =>
|
||||
m_planet_user_entity.getEntityAttributeNotNull<AccountAttribute>().UserGuid;
|
||||
|
||||
private string getUserNickname() =>
|
||||
m_planet_user_entity.getEntityAttributeNotNull<NicknameAttribute>().Nickname;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace BrokerCore.Services;
|
||||
using Common;
|
||||
using DbEntity;
|
||||
using Entity;
|
||||
using Repository;
|
||||
|
||||
// 화폐(Currency) 주문 완료 전략
|
||||
public class CurrencyOrderCompletionStrategy : IOrderCompletionStrategy
|
||||
{
|
||||
private readonly PlanetItemExchangeOrderRepo m_planet_item_exchange_repo;
|
||||
|
||||
public CurrencyOrderCompletionStrategy(PlanetItemExchangeOrderRepo planetItemExchangeOrderRepo)
|
||||
{
|
||||
m_planet_item_exchange_repo = planetItemExchangeOrderRepo;
|
||||
}
|
||||
|
||||
public async Task<PlanetItemExchangeOrder> completeOrder(PlanetUserEntity planetUserEntity,
|
||||
PlanetItemExchangeOrder orderCreated,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var (result, order_completed) =
|
||||
await m_planet_item_exchange_repo.findAndUpdateStatus(orderCreated.OrderId, ExchangeOrderStatus.Completed, cancellationToken);
|
||||
Guard.Against.resultFail(result);
|
||||
Guard.Against.isNull(order_completed, ServerErrorCode.InvalidRequest,
|
||||
()=>"CurrencyOrderCompletionStrategy => Order not found");
|
||||
return order_completed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
namespace BrokerCore.Services;
|
||||
|
||||
using Common;
|
||||
|
||||
using DbEntity;
|
||||
|
||||
using Repository;
|
||||
|
||||
using MetaAssets;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
|
||||
using Repository.Context;
|
||||
|
||||
public class ProductExchangeCreationStrategy : IOrderCreationStrategy
|
||||
{
|
||||
private readonly MetaverseBrokerDbContext m_broker_db_context;
|
||||
private readonly PlanetItemExchangeOrderAmountTotalLimitRepo m_planet_item_exchange_order_amount_total_limit;
|
||||
private readonly PlanetItemExchangeOrderAmountUserLimitRepo m_planet_item_exchange_order_amount_user_limit;
|
||||
private readonly PlanetItemExchangeOrderRepo m_planet_item_exchange_repo;
|
||||
|
||||
public ProductExchangeCreationStrategy(
|
||||
MetaverseBrokerDbContext brokerDbContext,
|
||||
PlanetItemExchangeOrderAmountTotalLimitRepo planetItemExchangeOrderAmountTotalLimit,
|
||||
PlanetItemExchangeOrderAmountUserLimitRepo planetItemExchangeOrderAmountUserLimit,
|
||||
PlanetItemExchangeOrderRepo planetItemExchangeRepo)
|
||||
{
|
||||
m_broker_db_context = brokerDbContext;
|
||||
m_planet_item_exchange_order_amount_total_limit = planetItemExchangeOrderAmountTotalLimit;
|
||||
m_planet_item_exchange_order_amount_user_limit = planetItemExchangeOrderAmountUserLimit;
|
||||
m_planet_item_exchange_repo = planetItemExchangeRepo;
|
||||
}
|
||||
|
||||
public async Task<(Result, PlanetItemExchangeOrder)> createOrder(
|
||||
PlanetItemExchangeOrder order,
|
||||
string planetServerType,
|
||||
PlanetItemExchangePolicyMetaData exchangePolicy,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var exchange_date = DateOnly.FromDateTime(order.CreatedAt.ToUniversalTime());
|
||||
var strategy = m_broker_db_context.Database.CreateExecutionStrategy();
|
||||
var execute_result = await strategy.ExecuteAsync<Result>(async () =>
|
||||
{
|
||||
Result result = null!;
|
||||
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);
|
||||
(result, var total_limit, var 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");
|
||||
|
||||
// 신규 주문 정보 삽입
|
||||
await m_planet_item_exchange_repo.add(order, cancellationToken);
|
||||
|
||||
await m_planet_item_exchange_order_amount_total_limit.increaseDailyAmount(total_limit,
|
||||
order.ExchangeMetaAmount, cancellationToken);
|
||||
|
||||
await m_planet_item_exchange_order_amount_user_limit.increaseDailyAmount(user_limit,
|
||||
order.ExchangeMetaAmount, cancellationToken);
|
||||
|
||||
await transaction.CommitAsync(cancellationToken);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
if (transaction.GetDbTransaction().Connection is not null)
|
||||
{
|
||||
await transaction.RollbackAsync(cancellationToken);
|
||||
}
|
||||
return new Result
|
||||
{
|
||||
ErrorCode = ServerErrorCode.InternalServerError,
|
||||
ResultString = $"ProductExchangeCreationStrategy Transaction Error => {ex.Message}"
|
||||
};
|
||||
}
|
||||
return result;
|
||||
});
|
||||
return (execute_result, order);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
using ServerCore;
|
||||
using ServerBase;
|
||||
using ServerCommon;
|
||||
|
||||
|
||||
namespace BrokerCore.Services;
|
||||
|
||||
using BrokerBusinessLog;
|
||||
|
||||
using Common;
|
||||
|
||||
using DbEntity;
|
||||
|
||||
using Entity;
|
||||
using Entity.Actions;
|
||||
|
||||
using MetaAssets;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
|
||||
using Repository;
|
||||
using Repository.Context;
|
||||
|
||||
// 아이템 주문 완료 전략
|
||||
public class ProductOrderCompletionStrategy : IOrderCompletionStrategy
|
||||
{
|
||||
private PlanetUserEntity m_planet_user_entity;
|
||||
private readonly BrokerMetaTableRef m_meta_table_ref;
|
||||
private readonly IServerLogic m_server_logic;
|
||||
private readonly MetaverseBrokerDbContext m_broker_db_context;
|
||||
private readonly PlanetItemExchangeOrderRepo m_planet_item_exchange_repo;
|
||||
|
||||
public ProductOrderCompletionStrategy(
|
||||
BrokerMetaTableRef metaTableRef,
|
||||
IServerLogic serverLogic,
|
||||
MetaverseBrokerDbContext brokerDbContext,
|
||||
PlanetItemExchangeOrderRepo planetItemExchangeRepo
|
||||
)
|
||||
{
|
||||
m_meta_table_ref = metaTableRef;
|
||||
m_server_logic = serverLogic;
|
||||
m_broker_db_context = brokerDbContext;
|
||||
m_planet_item_exchange_repo = planetItemExchangeRepo;
|
||||
}
|
||||
|
||||
public async Task<PlanetItemExchangeOrder> completeOrder(
|
||||
PlanetUserEntity planetUserEntity,
|
||||
PlanetItemExchangeOrder orderCreated,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
m_planet_user_entity = planetUserEntity;
|
||||
// 메타데이터 조회
|
||||
var exchange_policy = getExchangePolicyMeta(orderCreated.ExchangeMetaId);
|
||||
var product_meta = getProductMeta(exchange_policy.CaliverseItemId);
|
||||
var mail_meta = getSystemMailMeta(product_meta.SystemMail_First);
|
||||
|
||||
// 메일 발송 처리
|
||||
var broker_mail = new BrokerMailEntity(m_planet_user_entity, m_server_logic);
|
||||
var init_result = await broker_mail.onInit();
|
||||
Guard.Against.resultFail(init_result, init_result.ErrorCode, ()=>"BrokerMail onInit error");
|
||||
|
||||
var mail_send_action = broker_mail.getEntityActionNotNull<BrokerMailSendAction>();
|
||||
var mail_option = mail_send_action.createSystemSendMailOptionByMeta(product_meta, mail_meta,
|
||||
m_planet_user_entity.UserGuid,
|
||||
m_planet_user_entity.Nickname, orderCreated.ExchangeMetaAmount);
|
||||
|
||||
//==============================================================================================
|
||||
// rdb 트랜잭션 시작
|
||||
// 주문 완료 처리 임시
|
||||
// 메일 발송
|
||||
// 메일 발송 완료 시 주문 완료 처리 커밋
|
||||
// TODO: 재시도 전략 연구?
|
||||
//==============================================================================================
|
||||
PlanetItemExchangeOrder order_completed_result_object = null!;
|
||||
var strategy = m_broker_db_context.Database.CreateExecutionStrategy();
|
||||
var transaction_result = await strategy.ExecuteAsync(async () =>
|
||||
{
|
||||
await using var transaction =
|
||||
await m_broker_db_context.Database.BeginTransactionAsync(System.Data.IsolationLevel.RepeatableRead,
|
||||
cancellationToken);
|
||||
var (order_update_result, order_completed) = await m_planet_item_exchange_repo.findAndUpdateStatus(
|
||||
orderCreated.OrderId, ExchangeOrderStatus.Completed,
|
||||
cancellationToken);
|
||||
if (order_update_result.isFail())
|
||||
{
|
||||
await transaction.RollbackAsync(cancellationToken);
|
||||
return order_update_result;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var mail_send_result = await mail_send_action.sendMail(mail_option,
|
||||
async () => await transaction.CommitAsync(cancellationToken),
|
||||
async () => await transaction.RollbackAsync(cancellationToken)
|
||||
);
|
||||
if (mail_send_result.isFail())
|
||||
{
|
||||
await transaction.RollbackAsync(cancellationToken);
|
||||
return mail_send_result;
|
||||
}
|
||||
else if (order_completed is not null)
|
||||
{
|
||||
order_completed_result_object = order_completed;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (transaction.GetDbTransaction().Connection is not null)
|
||||
{
|
||||
await transaction.RollbackAsync(cancellationToken);
|
||||
}
|
||||
|
||||
return new Result
|
||||
{
|
||||
ErrorCode = ServerErrorCode.InternalServerError,
|
||||
ResultString = $"ProductOrderCompletionStrategy Transaction Error => {ex.Message}"
|
||||
};
|
||||
}
|
||||
|
||||
return new Result();
|
||||
});
|
||||
Guard.Against.resultFail(transaction_result);
|
||||
return order_completed_result_object;
|
||||
}
|
||||
|
||||
private PlanetItemExchangePolicyMetaData getExchangePolicyMeta(string exchangeMetaId)
|
||||
{
|
||||
m_meta_table_ref.MetaTable.PlanetItemExchangePolicyMetaTable.PlanetItemExchangePolicyDataListbyID.TryGetValue(
|
||||
exchangeMetaId, out var policy_meta);
|
||||
Guard.Against.isNull(policy_meta, ServerErrorCode.MetaIdInvalid,
|
||||
()=>"planet_item_exchange_policy_meta not found");
|
||||
return policy_meta;
|
||||
}
|
||||
|
||||
private ProductMetaData getProductMeta(string productMetaIdString)
|
||||
{
|
||||
var product_meta_id = Convert.ToInt32(productMetaIdString);
|
||||
m_meta_table_ref.MetaTable.ProductMetaTable.ProductMetaDataListbyId.TryGetValue(product_meta_id,
|
||||
out var product_meta);
|
||||
Guard.Against.isNull(product_meta, ServerErrorCode.MetaIdInvalid, ()=>"product meta not found");
|
||||
return product_meta;
|
||||
}
|
||||
|
||||
private SystemMailMetaData getSystemMailMeta(string systemMailDataId)
|
||||
{
|
||||
m_meta_table_ref.MetaTable.SystemMailMetaTable.SystemMailMetaDataListbyKey.TryGetValue(systemMailDataId,
|
||||
out var mail_data);
|
||||
Guard.Against.isNull(mail_data, ServerErrorCode.MetaIdInvalid, ()=>"system_mail_meta not found");
|
||||
return mail_data;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user