초기커밋

This commit is contained in:
2025-05-01 07:20:41 +09:00
commit 98bb2e3c5c
2747 changed files with 646947 additions and 0 deletions

View File

@@ -0,0 +1,277 @@
using System.Text;
using BrokerCore.BrokerBusinessLog;
using ServerCommon;
using ServerCore; using ServerBase;
namespace BrokerCore.Entity.Actions;
using MetaAssets;
using ServerCommon.BusinessLogDomain;
public class BrokerMailSendAction : EntityActionBase
{
private readonly DynamoDbClient m_dynamo_db_client;
public BrokerMailSendAction(EntityBase owner, DynamoDbClient dynamoDbClient) : base(owner)
{
m_dynamo_db_client = dynamoDbClient;
}
public override void onClear()
{
}
public override Task<Result> onInit()
{
return Task.FromResult(new Result());
}
string makeMailGuid(DateTime now)
{
var guid = Guid.NewGuid().ToString("N");
return $"{now.ToString("yyyy/MM/dd/HH:mm:ss:ff")}-{guid}";
}
private void setMailAttribute(MailSendOption option)
{
DateTime now = DateTimeHelper.Current;
var expire_date = now.AddSeconds(MetaHelper.GameConfigMeta.SystemMailStoragePeriod);
string mail_guid = makeMailGuid(now);
var mail_attribute = getOwner().getEntityAttributeNotNull<MailAttribute>();
mail_attribute.MailGuid = mail_guid;
mail_attribute.SenderNickName = option.SenderNickName;
mail_attribute.SenderGuid = option.SenderGuid;
mail_attribute.ReceiverNickName = option.ReceiverNickName;
mail_attribute.ReceiverGuid = option.ReceiverUserGuid;
mail_attribute.Title = option.Title;
mail_attribute.Text = option.Text;
mail_attribute.CreateTime = now;
mail_attribute.ExpireTime = expire_date;
mail_attribute.IsSystemMail = option.IsSystemMail;
mail_attribute.IsRead = false;
mail_attribute.IsGetItem = false;
mail_attribute.IsTextByMetaData = option.IsTextByMetaData;
mail_attribute.ItemList = option.ItemList;
mail_attribute.newEntityAttribute();
}
public async Task<Result> sendMail(MailSendOption mailSendOption, Func<Task> operationSuccess,
Func<Task> operationFail)
{
var result = new Result();
var user_guid = mailSendOption.ReceiverUserGuid;
StringBuilder str_builder = new();
str_builder.Append($"guid : {user_guid}, nickname : {mailSendOption.ReceiverNickName}");
str_builder.Append($" accountId : {mailSendOption.AccountId}");
setMailAttribute(mailSendOption);
var (doc_result, doc) = await getOwner().getEntityAttributeNotNull<MailAttribute>().toDocBase()!;
if (doc_result.isFail() || doc is null)
{
Log.getLogger().error($"fail create system mail, {str_builder.ToString()}");
return doc_result;
}
var mail_doc = doc as ReceivedMailDoc;
NullReferenceCheckHelper.throwIfNull(mail_doc,
() => $"mail_doc is null !!! - receiverUserGuid:{mailSendOption.ReceiverUserGuid}");
var log_actor = getOwner() as IWithLogActor;
NullReferenceCheckHelper.throwIfNull(log_actor,
() => $"log_actor is null !!! - receiverUserGuid:{mailSendOption.ReceiverUserGuid}");
var batch = new QueryBatchEx<QueryRunnerWithDocument>(log_actor, LogActionType.MailSend, m_dynamo_db_client,
true);
batch.addQuery(new DBQEntityWrite(mail_doc));
batch.addQuery(new QueryFinal(),
async (query) =>
{
// actionSuccess 실행하기
await operationSuccess();
return QueryBatchBase.QueryResultType.Success;
},
async (query, errorResult) =>
{
await operationFail();
return QueryBatchBase.QueryResultType.QueryFailed;
}
);
var query_result = await batch.doQueryWithStopwatch();
if (query_result.isFail())
{
Log.getLogger().error($"broker system mail send error, {str_builder.ToString()}");
return query_result;
}
var mail_attrib = mail_doc.getAttrib<MailAttrib>();
NullReferenceCheckHelper.throwIfNull(mail_attrib,
() => $"mail_attrib is null !!! - receiverUserGuid:{mailSendOption.ReceiverUserGuid}");
// 비즈니스 로그 작성
var log_data = new MailLogData();
setMailData(log_data, mail_attrib);
BusinessLogger.collectLog(log_actor, new MailBusinessLog(log_data));
Log.getLogger().info($"system mail send success, {str_builder.ToString()}");
return result;
}
private void setMailData(in MailLogData logData, in MailAttrib mailAttrib)
{
logData.MailGuid = mailAttrib.MailGuid;
logData.Title = mailAttrib.Title;
logData.Text = mailAttrib.Text;
logData.IsSystemMail = mailAttrib.IsSystemMail;
logData.IsReadMail = mailAttrib.IsRead;
logData.IsGetItem = mailAttrib.IsGetItem;
logData.SenderNickname = mailAttrib.SenderNickName;
logData.SenderGuid = mailAttrib.SenderGuid;
logData.ReceiverNickname = mailAttrib.ReceiverNickName;
logData.ReceiverGuid = mailAttrib.ReceiverGuid;
logData.CreateTime = mailAttrib.CreateTime;
logData.ExpireTime = mailAttrib.ExpireTime;
logData.IsTextByMetaData = mailAttrib.IsTextByMetaData;
logData.packageOrderId = mailAttrib.packageOrderId;
logData.ItemList = mailAttrib.ItemList;
}
public MailSendOption createSystemSendMailOptionByMeta(
ProductMetaData productMeta,
SystemMailMetaData mailMeta,
string receiverUserGuid,
string receiverNickName, int amount)
{
DateTime now = DateTimeHelper.Current;
var expire_date = now.AddSeconds(MetaHelper.GameConfigMeta.SystemMailStoragePeriod);
// if (productMeta.Storage_Period_First != 0)
// {
// expire_date = DateTime.UtcNow.AddMinutes(productMeta.Storage_Period_First);
// }
var mail_send_option = new MailSendOption
{
ReceiverUserGuid = receiverUserGuid,
ReceiverNickName = receiverNickName,
IsSystemMail = true,
IsTextByMetaData = true,
Title = mailMeta.Mail_Title,
Text = mailMeta.Mail_Desc,
ExpireDate = expire_date,
ItemList = getMailItems(productMeta, amount).ToList(),
PackageOrderId = string.Empty,
};
return mail_send_option;
}
private static IEnumerable<ServerCommon.MailItem> getMailItems(ProductMetaData productMetaData, int amount)
{
if (productMetaData.ItemID_First != 0)
{
IEnumerable<ServerCommon.MailItem> total_items = Enumerable.Range(0, amount).Select(i =>
{
return productMetaData.First_List.Select(itemMeta => new ServerCommon.MailItem()
{
ItemId = (UInt32)itemMeta.Id,
Count = itemMeta.Value,
ProductId = (UInt32)productMetaData.Id,
isRepeatProduct = false
});
}).SelectMany(x => x);
return total_items;
}
return new List<ServerCommon.MailItem>();
}
}
public class BrokerMailRecvAction : EntityActionBase
{
private readonly DynamoDbClient m_dynamo_db_client;
public BrokerMailRecvAction(EntityBase owner, DynamoDbClient dynamoDbClient) : base(owner)
{
m_dynamo_db_client = dynamoDbClient;
}
public override void onClear()
{
throw new NotImplementedException();
}
public override Task<Result> onInit()
{
return Task.FromResult(new Result());
}
public async Task<(Result, List<ReceivedMailDoc>)> findReceivedMailDoc(string userGuid)
{
var err_msg = string.Empty;
var dynamo_db_client = m_dynamo_db_client;
var received_mail_docs = new List<ReceivedMailDoc>();
var doc = new ReceivedMailDoc(userGuid);
var event_tid = Guid.NewGuid().ToString("N");
var query_config = dynamo_db_client.makeQueryConfigForReadByPKOnly(doc.getPK());
var (result, read_docs) =
await dynamo_db_client.simpleQueryDocTypesWithQueryOperationConfig<ReceivedMailDoc>(query_config);
if (result.isSuccess())
{
foreach (var recv_mail_doc in read_docs)
{
var mail_attrib = recv_mail_doc.getAttrib<MailAttrib>();
NullReferenceCheckHelper.throwIfNull(mail_attrib,
() => $"mail_attrib is null !!! - userGuid:{userGuid}");
if (DateTimeHelper.Current > mail_attrib.ExpireTime)
{
var to_delete_result =
await dynamo_db_client.simpleDeleteDocumentWithDocType(recv_mail_doc, event_tid);
if (to_delete_result.isFail())
{
err_msg =
$"Failed to simpleDeleteDocumentWithDocType() !!!, can't received mail : Mail:{mail_attrib.toBasicString()} - userGuid:{userGuid}";
Log.getLogger().error(err_msg);
}
else
{
err_msg =
$"Mail deleted with expired retention period !!! : deletedMail:{mail_attrib.toBasicString()} - userGuid:{userGuid}";
Log.getLogger().info(err_msg);
}
continue;
}
received_mail_docs.Add(recv_mail_doc);
}
}
return (result, received_mail_docs);
}
public async Task<Result> deleteMail(string mailGuid)
{
var result = new Result();
var err_msg = string.Empty;
var mail_doc = new ReceivedMailDoc(mailGuid);
var event_tid = Guid.NewGuid().ToString("N");
result = await m_dynamo_db_client.simpleDeleteDocumentWithDocType(mail_doc, event_tid);
if (result.isFail())
{
err_msg =
$"Failed to simpleDeleteDocumentWithDocType() !!!, can't received mail : Mail:{mail_doc.toBasicString()}";
Log.getLogger().error(err_msg);
return result;
}
return result;
}
}

View File

@@ -0,0 +1,163 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
namespace BrokerCore.Entity;
using ServerCommon;
//==========================================
// 유저의 인증 처리 Action 정의
//==========================================
public class UserAuthAction : EntityActionBase
{
private readonly DynamoDbClient m_dynamo_db_client;
public UserAuthAction(EntityBase owner, DynamoDbClient dynamoDbClient) : base(owner)
{
this.m_dynamo_db_client = dynamoDbClient;
}
public override Task<Result> onInit()
{
return Task.FromResult(new Result());
}
public override void onClear()
{
throw new NotImplementedException();
}
public async Task<(Result, AccountBaseDoc?)> findAccountDoc(string accountId)
{
return await EntityHelper.findDocByPk<AccountBaseDoc>(accountId, getOwner(), m_dynamo_db_client);
}
public async Task<(Result, UserBaseDoc?)> findUserDoc(string userGuid)
{
return await EntityHelper.findDocByPk<UserBaseDoc>(userGuid, getOwner(), m_dynamo_db_client);
}
public async Task<(Result, NicknameDoc?)> findNicknameDoc(string nickname)
{
return await EntityHelper.findDocByPk<NicknameDoc>(nickname, getOwner(), m_dynamo_db_client);
}
public async Task<Result> findAndSetAllAttributeByAccountId(string accountId)
{
var result = await findAndSetAccount(accountId);
if (result.isFail())
{
return result;
}
var user_guid = getOwner().getEntityAttributeNotNull<AccountAttribute>().UserGuid;
result = await findAndSetUser(user_guid);
if (result.isFail())
{
return result;
}
result = await findAndSetNickName(user_guid);
if (result.isFail())
{
return result;
}
return result;
}
public async Task<Result> findAndSetAllAttributeByUserGuid(string userGuid)
{
var result = await findAndSetUser(userGuid);
if (result.isFail())
{
return result;
}
result = await findAndSetNickName(userGuid);
if (result.isFail())
{
return result;
}
var account_id = getOwner().getEntityAttributeNotNull<UserAttribute>().AccountId;
result = await findAndSetAccount(account_id);
if (result.isFail())
{
return result;
}
return result;
}
// account로 AccountDoc을 읽고, AccountAttribute에 저장한다.
public async Task<Result> findAndSetAccount(string accountId)
{
var (result, doc) = await findAccountDoc(accountId);
if (result.isFail())
{
return result;
}
if (doc is null)
{
result.setFail(ServerErrorCode.AccountIdInvalid, accountId);
return result;
}
var attribute = getOwner().getEntityAttributeNotNull<AccountAttribute>();
attribute.copyEntityAttributeFromDoc(doc);
return result;
}
// UserBaseDoc을 읽고 UserAttribute에 저장
public async Task<Result> findAndSetUser(string userGuid)
{
var (result, doc) = await findUserDoc(userGuid);
if (result.isFail())
{
return result;
}
if (doc is null)
{
result.setFail(ServerErrorCode.UserGuidInvalid,
$"userGuid:{userGuid} not found - {getOwner().toBasicString()}");
return result;
}
var attribute = getOwner().getEntityAttributeNotNull<UserAttribute>();
attribute.copyEntityAttributeFromDoc(doc);
return result;
}
// NicknameDoc을 읽고, NicknameAttribute에 저장
public async Task<Result> findAndSetNickName(string userGuid)
{
var (result, doc) = await findNicknameDoc(userGuid);
if (result.isFail())
{
return result;
}
if (doc is null)
{
result.setFail(ServerErrorCode.NotFoundNickName,
$"nickname not found by userGuid:{userGuid} - {getOwner().toBasicString()}");
return result;
}
var attribute = getOwner().getEntityAttributeNotNull<NicknameAttribute>();
attribute.copyEntityAttributeFromDoc(doc);
return result;
}
}

View File

@@ -0,0 +1,60 @@

using ServerCore;
using ServerBase;
using ServerCommon;
namespace BrokerCore.Entity;
using Actions;
public class BrokerMailEntity : EntityBase, IWithLogActor
{
private readonly EntityBase m_parent;
private readonly IServerLogic m_server_logic;
// !! 주의 현상태에서는 EntityBase는 반드시 UserBase여야 한다.
// !! MailAttribute가 AccountAttribute를 참조하고, AccountAttribute는 UserBase를 참조하기 때문
public BrokerMailEntity(EntityBase parent, IServerLogic serverLogic)
: base(EntityType.Mail, parent)
{
this.m_parent = parent;
this.m_server_logic = serverLogic;
}
//====================================================================================================
// mail에 동록된 모든 Attribute와 Action을 초기화
//====================================================================================================
public override async Task<Result> onInit()
{
addEntityAttribute(new MailAttribute(this, m_parent));
addEntityAction(new BrokerMailSendAction(this, m_server_logic.getDynamoDbClient()));
addEntityAction(new BrokerMailRecvAction(this, m_server_logic.getDynamoDbClient()));
var mail_attribute = this.getEntityAttributeNotNull<MailAttribute>();
var result = await mail_attribute.onInit();
if (result.isFail())
{
return result;
}
var mail_send_action = this.getEntityActionNotNull<BrokerMailSendAction>();
result = await mail_send_action.onInit();
if (result.isFail())
{
return result;
}
return await base.onInit();
}
public ILogActor toLogActor()
{
var log_actor = m_parent as IWithLogActor;
NullReferenceCheckHelper.throwIfNull(log_actor,
() => $"m_parent is not IWithLogActor");
return log_actor.toLogActor();
}
}

View File

@@ -0,0 +1,28 @@
namespace BrokerCore.Entity;
using ServerCommon;
using ServerCore; using ServerBase;
public static class EntityExtensions
{
public static TEntityAttributeBase getEntityAttributeNotNull<TEntityAttributeBase>(this EntityBase entity)
where TEntityAttributeBase : EntityAttributeBase
{
TEntityAttributeBase? attribute = entity.getEntityAttribute<TEntityAttributeBase>();
// todo: 이렇게 널 처리를 하면 일관되게 처리할 수 있지만, null이 발생한 코드 위치가 항상 여기로 찍힌다.
// getEntityAttributeNotNull를 호출한 위치에서 로그를 찍을 수 있는 지 고민해보자.
NullReferenceCheckHelper.throwIfNull(attribute,
() => $"entity attribute {nameof(TEntityAttributeBase)} not found - {entity.toBasicString()}");
return attribute;
}
public static TEntityActionBase getEntityActionNotNull<TEntityActionBase>(this EntityBase entity)
where TEntityActionBase : EntityActionBase
{
TEntityActionBase? action = entity.getEntityAction<TEntityActionBase>();
NullReferenceCheckHelper.throwIfNull(action,
() => $"entity action {nameof(TEntityActionBase)} not found - {entity.toBasicString()}");
return action;
}
}

View File

@@ -0,0 +1,42 @@

using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
namespace BrokerCore.Entity;
public static class EntityHelper
{
public static async Task<(Result, TDoc?)> findDocByPk<TDoc>(string pk, EntityBase owner, DynamoDbClient dynamoDbClient)
where TDoc : DynamoDbDocBase, new()
{
ArgumentNullException.ThrowIfNull(owner, $"owner is null !!!");
var (result, user_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<TDoc>(pk);
if (result.isFail())
{
return (result, null);
}
ArgumentNullException.ThrowIfNull(user_primary_key, $"user_primary_key is null !!! - {owner.toBasicString()}");
var query_config = dynamoDbClient.makeQueryConfigForReadByPKSK(user_primary_key.PK);
(result, var found_doc) =
await dynamoDbClient.simpleQueryDocTypeWithQueryOperationConfig<TDoc>(query_config, false);
if (result.isFail())
{
return (result, null);
}
return (result, found_doc);
}
}

View File

@@ -0,0 +1,65 @@
namespace BrokerCore.Entity.Actions;
using ServerCommon;
using ServerCore; using ServerBase;
public record class MailSendOption
{
//==================
// 기본 속성들
//==================
public string ReceiverUserGuid { get; set; } = string.Empty;
public string ReceiverNickName { get; set; } = string.Empty;
public string SenderNickName { get; set; } = string.Empty;
public string SenderGuid { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public string Text { get; set; } = string.Empty;
public List<string> ContentsArguments { get; set; } = new List<string>();
public bool IsTextByMetaData { get; set; } = true;
public List<ServerCommon.MailItem> ItemList { get; set; } = new List<ServerCommon.MailItem>();
public DateTime ExpireDate { get; set; } = DateTimeHelper.MaxTime;
public string PackageOrderId { get; set; } = string.Empty;
//==================
// 추가 옵션들
//==================
public bool IsSystemMail { get; set; } = true;
public string PlanetId { get; set; } = string.Empty;
public string AccountId { get; set; } = string.Empty;
//==================
// 기본 생성자
//==================
public MailSendOption() { }
//==================
// 필수 항목을 포함한 생성자
//==================
public MailSendOption(string receiverUserGuid, string receiverNickName, string title, string text)
{
ReceiverUserGuid = receiverUserGuid;
ReceiverNickName = receiverNickName;
Title = title;
Text = text;
}
//==================
// 아이템 목록 추가 메서드
//==================
public void addItems(List<MailItem>? mailItems)
{
if (mailItems != null)
{
ItemList.AddRange(mailItems);
}
}
//==================
// 기본값 검증 메서드
//==================
public bool validate()
{
return !string.IsNullOrEmpty(ReceiverUserGuid) && !string.IsNullOrEmpty(Title);
}
}

View File

@@ -0,0 +1,75 @@
namespace BrokerCore.Entity;
using Actions;
using BrokerBusinessLog;
using ServerCommon;
using ServerCore; using ServerBase;
public sealed class PlanetUserEntity : EntityBase, IWithLogActor
{
private readonly DynamoDbClient m_dynamo_db_client;
private readonly IServerLogic m_server_logic;
private string m_planet_id = string.Empty;
// private BrokerMail m_broker_mail = null!;
public string AccountId
=> this.getEntityAttributeNotNull<AccountAttribute>().AccountId;
public string UserGuid
=> this.getEntityAttributeNotNull<UserAttribute>().UserGuid;
public string Nickname
=> this.getEntityAttributeNotNull<NicknameAttribute>().Nickname;
public PlanetUserEntity(IServerLogic serverLogic) : base(EntityType.PlanetUser)
{
m_server_logic = serverLogic;
m_dynamo_db_client = serverLogic.getDynamoDbClient();
}
public override OwnerEntityType onGetOwnerEntityType()
{
return OwnerEntityType.User;
}
public override async Task<Result> onInit()
{
try
{
addEntityAttribute(new UserAttribute(this));
addEntityAttribute(new NicknameAttribute(this));
addEntityAction(new UserAuthAction(this, m_dynamo_db_client));
addEntityAttribute(new AccountAttribute(this));
}
catch (Exception ex)
{
Log.getLogger().Error(ex, $"{nameof(PlanetUserEntity)}.onInit() Exception => {ex.Message}");
}
return await base.onInit();
}
public void setPlanetId(string planetId)
{
m_planet_id = planetId;
}
// IWithLogActor 구현
public ILogActor toLogActor()
{
var account = this.getEntityAttributeNotNull<AccountAttribute>();
var nickname = this.getEntityAttributeNotNull<NicknameAttribute>();
var log_actor = new PlanetUserLogActor
{
ServerType = m_server_logic.getServerType().toServerType(),
PlanetId = m_planet_id,
AccountType = account.AccountType,
AccountId = account.AccountId,
AccountIdString = account.AccountIdString,
UserNickname = nickname.Nickname,
UserGuid = account.UserGuid,
};
return log_actor;
}
}