using Microsoft.EntityFrameworkCore; using ServerBase; namespace BrokerApiCore; public class PlanetItemExchangeOrderRepo { private readonly MetaverseBrokerDbContext m_db_context; public PlanetItemExchangeOrderRepo(MetaverseBrokerDbContext dbContext) { m_db_context = dbContext; } //================================================================================ // 교환 주문을 추가한다. // 트랜잭션을 사용한다. //================================================================================ public async Task add(PlanetItemExchangeOrder order, CancellationToken cancellationToken = default) { try { m_db_context.PlanetItemExchangeOrders.Add(order); await m_db_context.SaveChangesAsync(cancellationToken); } catch (Exception e) { return new Result { ErrorCode = ServerErrorCode.RdbError, ResultString = e.Message }; } return new Result(); } // public async Task add(PlanetItemExchangeOrder order, // CancellationToken cancellationToken = default) // { // var result = new Result(); // // // 실행 전략 생성 // var strategy = m_db_context.Database.CreateExecutionStrategy(); // // // 실행 전략을 사용하여 트랜잭션 작업을 실행 // await strategy.ExecuteAsync(async () => // { // // 트랜잭션 시작 // await using var transaction = await m_db_context.Database.BeginTransactionAsync(cancellationToken); // try // { // var order_date_utc = order.CreatedAt.ToUniversalTime().Date; // UTC 기준 주문일의 자정(00:00:00) // // // 단일 쿼리로 total_exchange_count와 user_exchange_count를 함께 조회 // var exchange_counts = await m_db_context.PlanetItemExchangeOrders // .Where(x => x.PlanetId == order.PlanetId && // x.ExchangeMetaId == order.ExchangeMetaId && // x.CreatedAt >= order_date_utc && x.CreatedAt < order_date_utc.AddDays(1)) // .GroupBy(x => 1) // .Select(g => new // { // TotalCount = g.Sum(x => x.ExchangeMetaAmount), // UserCount = g.Where(x => x.UserGuid == order.UserGuid).Sum(x => x.ExchangeMetaAmount) // }) // .FirstOrDefaultAsync(cancellationToken); // // var total_exchange_count = exchange_counts?.TotalCount ?? 0; // var user_exchange_count = exchange_counts?.UserCount ?? 0; // // if (total_exchange_count + order.ExchangeMetaAmount > dailyLimit) // { // result.setFail(ServerErrorCode.ExchangeTotalOrderDailyLimitExceeded, // $"일일 총 구매 제한 초과 => {order.ExchangeMetaId} Total exchange count {total_exchange_count} + order amount {order.ExchangeMetaAmount} > daily total limit {dailyLimit}"); // return; // } // // if (user_exchange_count + order.ExchangeMetaAmount > dailyUserLimit) // { // result.setFail(ServerErrorCode.ExchangeUserOrderDailyLimitExceeded, // $"일일 개인 구매 제한 초과 => {order.ExchangeMetaId} User exchange count {user_exchange_count} + order amount {order.ExchangeMetaAmount} > daily user limit {dailyUserLimit}"); // return; // } // // m_db_context.PlanetItemExchangeOrders.Add(order); // m_ // // await m_db_context.SaveChangesAsync(cancellationToken); // await transaction.CommitAsync(cancellationToken); // } // catch (Exception e) // { // result.setFail(ServerErrorCode.InternalServerError, e.Message); // } // }); // // return result; // } public async Task delete(PlanetItemExchangeOrder order, CancellationToken cancellationToken = default) { var result = new Result(); try { m_db_context.PlanetItemExchangeOrders.Remove(order); await m_db_context.SaveChangesAsync(cancellationToken); } catch (Exception e) { result.setFail(ServerErrorCode.InternalServerError, e.Message); } return result; } //================================================================================ // 교환 주문을 비동기로 업데이트한다. // OrderId를 키로 사용한다. // OrderType과 OrderCompletedAt만 업데이트한다. //================================================================================ public async Task<(Result, PlanetItemExchangeOrder?)> findAndUpdateStatus(string orderId, ExchangeOrderStatus orderStatus, CancellationToken cancellationToken = default) { var result = new Result(); try { var order = await m_db_context.PlanetItemExchangeOrders.Where(x => x.OrderId == orderId) .FirstOrDefaultAsync(cancellationToken); if (order == null) { result.setFail(ServerErrorCode.ExchangeOrderIdNotFound, $"Order not found: {orderId}"); return (result, null); } order.OrderStatus = orderStatus; order.CompletedAt = DateTime.UtcNow; m_db_context.PlanetItemExchangeOrders.Attach(order); m_db_context.Entry(order).Property(x => x.OrderStatus).IsModified = true; m_db_context.Entry(order).Property(x => x.CompletedAt).IsModified = true; await m_db_context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return (new Result(), order); } catch (Exception e) { // await transaction.RollbackAsync(cancellationToken).ConfigureAwait(false); return (new Result { ResultString = e.Message }, null); } } public async Task<(Result, PlanetItemExchangeOrder?)> findOne(string orderId, ExchangeOrderStatus orderStatus, CancellationToken cancellationToken = default) { try { var order = await m_db_context.PlanetItemExchangeOrders .Where(x => x.OrderId == orderId && x.OrderStatus == orderStatus) .FirstOrDefaultAsync(cancellationToken); return (new Result(), order); } catch (Exception e) { return (new Result { ErrorCode = ServerErrorCode.RdbError, ResultString = e.Message }, null); } } // public async Task updateForPending(SapphireExchangeOrder order, // CancellationToken cancellationToken = default) // { // // await using var transaction = await m_db_context.Database.BeginTransactionAsync(cancellationToken); // try // { // m_db_context.SapphireExchangeOrders.Attach(order); // m_db_context.Entry(order).Property(x => x.OrderStatus).IsModified = true; // await m_db_context.SaveChangesAsync(cancellationToken); // // await transaction.CommitAsync(cancellationToken); // return new Result(); // } // catch (Exception e) // { // // await transaction.RollbackAsync(cancellationToken); // return new Result { ResultString = e.Message }; // } // } //========================================================================== //SELECT * // FROM `sapphire_exchange_order` // WHERE `PlanetId` = @planetId // AND `UserGuid` = @userGuid // AND (@orderStatus IS NULL OR `OrderStatus` = @orderStatus) // ORDER BY `CreatedAt` ASC // LIMIT @pageSize OFFSET @offset; //========================================================================== public async Task<(Result, IEnumerable?, int)> findList( string planetId, string metaId, string seasonId, string userGuid, ExchangeOrderStatus? orderStatus = null, int pageIndex = 1, int pageSize = 20, string sortOrder = "asc", CancellationToken cancellationToken = default) { var result = new Result(); try { var query = m_db_context.PlanetItemExchangeOrders.AsQueryable(); if (!string.IsNullOrEmpty(planetId)) { query = query.Where(x => x.PlanetId == planetId); } if (!string.IsNullOrEmpty(metaId)) { query = query.Where(x => x.ExchangeMetaId == metaId); } if (!string.IsNullOrEmpty(seasonId)) { query = query.Where(x => x.SeasonId == seasonId); } if (!string.IsNullOrEmpty(userGuid)) { query = query.Where(x => x.UserGuid == userGuid); } if (orderStatus.HasValue) { query = query.Where(x => x.OrderStatus == orderStatus); } query = sortOrder.ToLower() == "desc" ? query.OrderByDescending(o => o.CreatedAt) : query.OrderBy(o => o.CreatedAt); var orders = await query .Skip((pageIndex - 1) * pageSize) .Take(pageSize) .ToArrayAsync(cancellationToken); var total_count = await query.CountAsync(cancellationToken); return (result, orders, total_count); } catch (Exception e) { result.setFail(ServerErrorCode.RdbError, e.Message); return (result, null, 0); } } //================================================================================ // 사파이어 교환 주문 목록을 비동기로 조회한다. // OrderType과 PlanetId로 검색한다. // Result, IEnumerable<>를 반환한다. // 조건에 맞는 값이 없다면 빈 배열을 반환한다. //================================================================================ public async Task> findPlanetItemExchangeOrders(int pageIndex, int pageSize) { var query = m_db_context.Set().AsNoTracking(); var count = await query.CountAsync(); var items = await query .OrderBy(o => o.CreatedAt) .Skip((pageIndex - 1) * pageSize) .Take(pageSize) .ToListAsync(); return new PaginatedList(items, count, pageIndex, pageSize); } public class PaginatedList : List { public int PageIndex { get; private set; } public int TotalPages { get; private set; } public int TotalCount { get; private set; } public PaginatedList(List items, int count, int pageIndex, int pageSize) { PageIndex = pageIndex; TotalCount = count; this.AddRange(items); } public bool HasPreviousPage => PageIndex > 1; public bool HasNextPage => PageIndex < TotalPages; } }