using System.Text.Json; namespace Tiku.Application.Points; public sealed record PointActor(Guid TenantId, Guid UserId); public sealed record PointLimitQuery(int? Limit = null, string? Status = null, Guid? RegionId = null); public sealed record ClaimPointTaskCommand(string TaskKey, string? SourceType, Guid? SourceId); public sealed record CreatePointExchangeOrderCommand(Guid ItemId); public sealed record PointSummaryItem(int EarnedPoints, int SpentPoints, int BalancePoints); public sealed record PointTaskItem( Guid Id, string TaskKey, string Title, string? Description, string TaskType, string Status, int Points, int MaxClaimsPerUser, int ClaimedCount, bool CanClaim, DateTimeOffset? StartsAt, DateTimeOffset? EndsAt, int SortOrder, JsonElement Rules, JsonElement Metadata); public sealed record PointClaimItem( Guid Id, Guid TaskId, string TaskKey, int Points, string Status, string? SourceType, Guid? SourceId, DateTimeOffset ClaimedAt, JsonElement Metadata); public sealed record PointExchangeItemDto( Guid Id, Guid? RegionId, string ItemKey, string Name, string? Description, string ItemType, string Status, int PointsCost, int? Stock, int? Days, bool CanExchange, int SortOrder, JsonElement FulfillmentPayload, JsonElement Metadata); public sealed record PointExchangeOrderItem( Guid Id, Guid ItemId, string OrderNo, string ItemName, string ItemType, string Status, int PointsCost, DateTimeOffset OrderedAt, DateTimeOffset? CompletedAt, JsonElement FulfillmentSnapshot, JsonElement Metadata); public sealed record PointList(IReadOnlyCollection Items); public interface IPointService { Task GetSummaryAsync(PointActor actor, CancellationToken cancellationToken = default); Task> GetTasksAsync( PointActor actor, PointLimitQuery query, CancellationToken cancellationToken = default); Task ClaimTaskAsync( PointActor actor, ClaimPointTaskCommand command, CancellationToken cancellationToken = default); Task> GetExchangeItemsAsync( PointActor actor, PointLimitQuery query, CancellationToken cancellationToken = default); Task CreateExchangeOrderAsync( PointActor actor, CreatePointExchangeOrderCommand command, CancellationToken cancellationToken = default); Task> GetExchangeOrdersAsync( PointActor actor, PointLimitQuery query, CancellationToken cancellationToken = default); } public sealed class PointException(string message, string code) : Exception(message) { public string Code { get; } = code; }