using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Core.Execution;
/// <summary>A server order reconciled against the original cargo and wallet.</summary>
public sealed record ConfirmedTradeReceipt(string OperationKey,long MarketId,string Commodity,int QuantityDelta,long UnitPrice,
long CreditsBefore,long CreditsAfter,int CargoBefore,int CargoAfter,string CommanderId,long ShipId,DateTimeOffset SessionStartedAt,
DateTimeOffset ConfirmedAt,bool ServerConfirmed,bool ClientApplied)
{
public decimal Validate(TradeAction action,PlayerState player,DateTimeOffset requestedAt)
{
var order=action.Order??throw new InvalidOperationException("交易回执缺少计划订单。");
var buy=action.Kind==TradeActionKind.Buy;
var market=buy?action.Leg.From.MarketId:action.Leg.To.MarketId;
var delta=buy?order.Quantity:-order.Quantity;
if(action.Kind is not(TradeActionKind.Buy or TradeActionKind.Sell)||!Guid.TryParseExact(OperationKey,"N",out _)
||!ServerConfirmed||!ClientApplied||MarketId!=market||Commodity!=order.Commodity||QuantityDelta!=delta||UnitPrice<=0
||CreditsBefore<0||CreditsAfter<0||CargoBefore<0||CargoAfter<0||CargoAfter>player.CargoCapacity
||(long)CargoAfter-CargoBefore!=delta||(decimal)CreditsAfter-CreditsBefore!=-(decimal)UnitPrice*delta
||CommanderId.Length==0||CommanderId!=player.CommanderId||ShipId!=player.CurrentShipId||SessionStartedAt!=player.LoadGameAt
||ConfirmedAt<requestedAt||ConfirmedAt>DateTimeOffset.UtcNow.AddSeconds(1)||DateTimeOffset.UtcNow-ConfirmedAt>TimeSpan.FromMinutes(2)
||!player.InGame||!player.Docked||player.MarketId!=market||!player.CargoReliable||player.CargoCount!=CargoAfter)
throw new InvalidOperationException("交易回执与服务器确认、当前会话、货舱或原生余额不一致,已停止后续操作。");
if(buy?UnitPrice>order.BuyPrice:UnitPrice<order.SellPrice)
throw new InvalidOperationException("实际成交价劣于计划,已停止后续操作。");
return (decimal)CreditsAfter-CreditsBefore;
}
}
public interface IConfirmedTradeCommandDriver:ITradeCommandDriver
{
Task<ConfirmedTradeReceipt> ExecuteTradeAsync(TradeAction action,Func<CancellationToken,Task> beforeInput,CancellationToken token);
}
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Core.Execution;
/// <summary>A server order reconciled against the original cargo and wallet.</summary>
public sealed record ConfirmedTradeReceipt(string OperationKey,long MarketId,string Commodity,int QuantityDelta,long UnitPrice,
long CreditsBefore,long CreditsAfter,int CargoBefore,int CargoAfter,string CommanderId,long ShipId,DateTimeOffset SessionStartedAt,
DateTimeOffset ConfirmedAt,bool ServerConfirmed,bool ClientApplied)
{
public decimal Validate(TradeAction action,PlayerState player,DateTimeOffset requestedAt)
{
var order=action.Order??throw new InvalidOperationException("交易回执缺少计划订单。");
var buy=action.Kind==TradeActionKind.Buy;
var market=buy?action.Leg.From.MarketId:action.Leg.To.MarketId;
var delta=buy?order.Quantity:-order.Quantity;
if(action.Kind is not(TradeActionKind.Buy or TradeActionKind.Sell)||!Guid.TryParseExact(OperationKey,"N",out _)
||!ServerConfirmed||!ClientApplied||MarketId!=market||Commodity!=order.Commodity||QuantityDelta!=delta||UnitPrice<=0
||CreditsBefore<0||CreditsAfter<0||CargoBefore<0||CargoAfter<0||CargoAfter>player.CargoCapacity
||(long)CargoAfter-CargoBefore!=delta||(decimal)CreditsAfter-CreditsBefore!=-(decimal)UnitPrice*delta
||CommanderId.Length==0||CommanderId!=player.CommanderId||ShipId!=player.CurrentShipId||SessionStartedAt!=player.LoadGameAt
||ConfirmedAt<requestedAt||ConfirmedAt>DateTimeOffset.UtcNow.AddSeconds(1)||DateTimeOffset.UtcNow-ConfirmedAt>TimeSpan.FromMinutes(2)
||!player.InGame||!player.Docked||player.MarketId!=market||!player.CargoReliable||player.CargoCount!=CargoAfter)
throw new InvalidOperationException("交易回执与服务器确认、当前会话、货舱或原生余额不一致,已停止后续操作。");
if(buy?UnitPrice>order.BuyPrice:UnitPrice<order.SellPrice)
throw new InvalidOperationException("实际成交价劣于计划,已停止后续操作。");
return (decimal)CreditsAfter-CreditsBefore;
}
}
public interface IConfirmedTradeCommandDriver:ITradeCommandDriver
{
Task<ConfirmedTradeReceipt> ExecuteTradeAsync(TradeAction action,Func<CancellationToken,Task> beforeInput,CancellationToken token);
}