using System.ComponentModel.DataAnnotations; using System.Text.Json; using Tiku.Application.Commerce; using Tiku.Domain.Commerce; using Tiku.Domain.Common; using Tiku.Domain.Tenancy; namespace Tiku.Api.Contracts; public sealed class TenantCommerceQueryDto { [StringLength(50)] public string? Provider { get; set; } [StringLength(32)] public string? Status { get; set; } [Range(1, 200)] public int? Limit { get; set; } public Guid? UserId { get; set; } public Guid? RegionId { get; set; } } public sealed class UpsertPaymentAccountDto { [Required] [StringLength(50)] public string Provider { get; set; } = string.Empty; [StringLength(50)] public string? Mode { get; set; } = "TenantCollect"; [StringLength(200)] public string? DisplayName { get; set; } public TenantExternalProviderStatus Status { get; set; } = TenantExternalProviderStatus.Disabled; [StringLength(300)] public string? SecretRef { get; set; } public int? Priority { get; set; } public JsonElement ConfigPublic { get; set; } = JsonDefaults.Object(); public UpsertPaymentAccountCommand ToCommand() { return new UpsertPaymentAccountCommand(Provider, Mode, DisplayName, Status, SecretRef, Priority, ConfigPublic); } } public sealed class UpsertTenantSecretDto { [Required] [StringLength(80)] public string Purpose { get; set; } = "payment"; [Required] [StringLength(50)] public string Provider { get; set; } = string.Empty; [Required] [StringLength(120)] public string SecretKey { get; set; } = string.Empty; [StringLength(300)] public string SecretRef { get; set; } = string.Empty; public TenantSecretStatus Status { get; set; } = TenantSecretStatus.Active; public JsonElement SecretPayload { get; set; } = JsonDefaults.Object(); public DateTimeOffset? ExpiresAt { get; set; } public UpsertTenantSecretCommand ToCommand() { return new UpsertTenantSecretCommand( Purpose, Provider, SecretKey, SecretRef, Status, SecretPayload, ExpiresAt); } } public sealed class CreateCodeBatchDto { [Required] [StringLength(200)] public string Name { get; set; } = string.Empty; [Range(1, 1000)] public int TotalCount { get; set; } [Range(1, 3650)] public int Days { get; set; } public Guid? RegionId { get; set; } [StringLength(50)] public string? SaleType { get; set; } [StringLength(100)] public string? Channel { get; set; } [Range(0, int.MaxValue)] public int? DefaultUnitPriceCents { get; set; } [Range(0, int.MaxValue)] public int? CostPriceCents { get; set; } [StringLength(1000)] public string? Remark { get; set; } public CreateCodeBatchCommand ToCommand() { return new CreateCodeBatchCommand( Name, TotalCount, Days, RegionId, SaleType, Channel, DefaultUnitPriceCents, CostPriceCents, Remark); } } public sealed class RedeemActivationCodeDto { [Required] [StringLength(100)] public string Code { get; set; } = string.Empty; [Required] public Guid UserId { get; set; } public Guid? RegionId { get; set; } public RedeemActivationCodeCommand ToCommand() { return new RedeemActivationCodeCommand(Code, UserId, RegionId); } } public sealed class UpsertPointTaskDto { public Guid? Id { get; set; } [Required] [StringLength(100)] public string TaskKey { get; set; } = string.Empty; [Required] [StringLength(200)] public string Title { get; set; } = string.Empty; [StringLength(1000)] public string? Description { get; set; } public PointActivityTaskType TaskType { get; set; } = PointActivityTaskType.Manual; public PointActivityTaskStatus Status { get; set; } = PointActivityTaskStatus.Active; [Range(1, int.MaxValue)] public int Points { get; set; } [Range(1, 1000)] public int MaxClaimsPerUser { get; set; } = 1; public DateTimeOffset? StartsAt { get; set; } public DateTimeOffset? EndsAt { get; set; } public int SortOrder { get; set; } public JsonElement Rules { get; set; } = JsonDefaults.Object(); public JsonElement Metadata { get; set; } = JsonDefaults.Object(); public UpsertPointTaskCommand ToCommand() { return new UpsertPointTaskCommand( Id, TaskKey, Title, Description, TaskType, Status, Points, MaxClaimsPerUser, StartsAt, EndsAt, SortOrder, Rules, Metadata); } } public sealed class UpsertPointExchangeItemDto { public Guid? Id { get; set; } public Guid? RegionId { get; set; } [Required] [StringLength(100)] public string ItemKey { get; set; } = string.Empty; [Required] [StringLength(200)] public string Name { get; set; } = string.Empty; [StringLength(1000)] public string? Description { get; set; } public PointExchangeItemType ItemType { get; set; } = PointExchangeItemType.Entitlement; public PointExchangeItemStatus Status { get; set; } = PointExchangeItemStatus.Active; [Range(1, int.MaxValue)] public int PointsCost { get; set; } [Range(0, int.MaxValue)] public int? Stock { get; set; } [Range(0, 3650)] public int? Days { get; set; } public int SortOrder { get; set; } public DateTimeOffset? StartsAt { get; set; } public DateTimeOffset? EndsAt { get; set; } public JsonElement FulfillmentPayload { get; set; } = JsonDefaults.Object(); public JsonElement Metadata { get; set; } = JsonDefaults.Object(); public UpsertPointExchangeItemCommand ToCommand() { return new UpsertPointExchangeItemCommand( Id, RegionId, ItemKey, Name, Description, ItemType, Status, PointsCost, Stock, Days, SortOrder, StartsAt, EndsAt, FulfillmentPayload, Metadata); } } public sealed class CreateRefundRequestDto { [Required] public Guid OrderId { get; set; } public Guid? PaymentId { get; set; } [Range(1, int.MaxValue)] public int AmountCents { get; set; } [StringLength(1000)] public string? Reason { get; set; } public RefundEntitlementAction EntitlementAction { get; set; } = RefundEntitlementAction.RevokeOnSuccess; public JsonElement Metadata { get; set; } = JsonDefaults.Object(); public CreateRefundRequestCommand ToCommand() { return new CreateRefundRequestCommand(OrderId, PaymentId, AmountCents, Reason, EntitlementAction, Metadata); } } public sealed class UpdateRefundStatusDto { [Required] public Guid RefundRequestId { get; set; } public CommerceRefundStatus Status { get; set; } [StringLength(1000)] public string? Reason { get; set; } [StringLength(200)] public string? ProviderRefundNo { get; set; } public UpdateRefundStatusCommand ToCommand() { return new UpdateRefundStatusCommand(RefundRequestId, Status, Reason, ProviderRefundNo); } } public sealed class CreateReconciliationBatchDto { [Required] [StringLength(50)] public string Provider { get; set; } = string.Empty; public DateOnly BillDate { get; set; } public ReconciliationBillType BillType { get; set; } = ReconciliationBillType.Combined; public ReconciliationSource Source { get; set; } = ReconciliationSource.ManualUpload; [StringLength(200)] public string? SourceName { get; set; } [Required] [StringLength(200)] public string SourceHash { get; set; } = string.Empty; public JsonElement Metadata { get; set; } = JsonDefaults.Object(); public CreateReconciliationBatchCommand ToCommand() { return new CreateReconciliationBatchCommand(Provider, BillDate, BillType, Source, SourceName, SourceHash, Metadata); } } public sealed class UpdateReconciliationIssueDto { [Required] public Guid IssueId { get; set; } public ReconciliationIssueStatus Status { get; set; } = ReconciliationIssueStatus.Investigating; public ReconciliationResolutionType ResolutionType { get; set; } = ReconciliationResolutionType.None; [StringLength(1000)] public string? Note { get; set; } public Guid? AssignedTo { get; set; } public UpdateReconciliationIssueCommand ToCommand() { return new UpdateReconciliationIssueCommand(IssueId, Status, ResolutionType, Note, AssignedTo); } } public sealed class PreviewReconciliationImportDto { [Required] [StringLength(50)] public string Provider { get; set; } = string.Empty; public DateOnly BillDate { get; set; } public ReconciliationBillType BillType { get; set; } = ReconciliationBillType.Combined; [Required] [StringLength(300)] public string SourceName { get; set; } = string.Empty; public JsonElement Rows { get; set; } = JsonDefaults.Array(); public PreviewReconciliationImportCommand ToPreviewCommand() => new(Provider, BillDate, BillType, SourceName, Rows); public ImportReconciliationCommand ToImportCommand() => new(Provider, BillDate, BillType, SourceName, Rows); } public sealed class CreateAdjustmentVoucherDto { public Guid? IssueId { get; set; } public Guid? BatchId { get; set; } public Guid? ItemId { get; set; } public Guid? OrderId { get; set; } public Guid? PaymentId { get; set; } public Guid? RefundRequestId { get; set; } public CommerceAdjustmentDirection Direction { get; set; } = CommerceAdjustmentDirection.IncreaseRevenue; [Range(1, int.MaxValue)] public int AmountCents { get; set; } [StringLength(10)] public string? Currency { get; set; } [Required] [StringLength(1000)] public string Reason { get; set; } = string.Empty; [StringLength(500)] public string? ProofAssetKey { get; set; } public JsonElement Metadata { get; set; } = JsonDefaults.Object(); public CreateAdjustmentVoucherCommand ToCommand() => new( IssueId, BatchId, ItemId, OrderId, PaymentId, RefundRequestId, Direction, AmountCents, Currency, Reason, ProofAssetKey, Metadata); } public sealed class UpdateAdjustmentVoucherStatusDto { [Required] public Guid VoucherId { get; set; } public CommerceAdjustmentVoucherStatus Status { get; set; } = CommerceAdjustmentVoucherStatus.PendingReview; [StringLength(1000)] public string? Note { get; set; } public UpdateAdjustmentVoucherStatusCommand ToCommand() => new(VoucherId, Status, Note); } public sealed class RequestProviderBillJobDto { [Required] [StringLength(50)] public string Provider { get; set; } = string.Empty; public DateOnly BillDate { get; set; } public ReconciliationBillType BillType { get; set; } = ReconciliationBillType.Combined; public DateTimeOffset? RunAfter { get; set; } public RequestProviderBillJobCommand ToCommand() => new(Provider, BillDate, BillType, RunAfter); } public sealed class RefundNotificationDto { [Required] [StringLength(100)] public string RefundNo { get; set; } = string.Empty; [StringLength(200)] public string? ProviderRefundNo { get; set; } public CommerceRefundStatus Status { get; set; } = CommerceRefundStatus.Succeeded; [StringLength(200)] public string? EventId { get; set; } public JsonElement Payload { get; set; } = JsonDefaults.Object(); public RefundNotificationCommand ToCommand(string provider) => new(provider, RefundNo, ProviderRefundNo, Status, EventId, Payload); } public sealed class UpdatePointExchangeOrderStatusDto { [Required] public Guid OrderId { get; set; } public PointExchangeOrderStatus Status { get; set; } public UpdatePointExchangeOrderStatusCommand ToCommand() { return new UpdatePointExchangeOrderStatusCommand(OrderId, Status); } } public sealed class UpsertTenantCouponDto { public Guid? Id { get; set; } [Required] [StringLength(100)] public string Code { get; set; } = string.Empty; public Guid? PlanId { get; set; } public DiscountType DiscountType { get; set; } = DiscountType.Fixed; [Range(typeof(decimal), "0.01", "999999")] public decimal DiscountValue { get; set; } public DateTimeOffset? ValidFrom { get; set; } public DateTimeOffset? ValidTo { get; set; } [Range(1, int.MaxValue)] public int? MaxUses { get; set; } [StringLength(50)] public string? Source { get; set; } [StringLength(1000)] public string? Remark { get; set; } public UpsertCouponCommand ToCommand() { return new UpsertCouponCommand( Id, Code, PlanId, DiscountType, DiscountValue, ValidFrom, ValidTo, MaxUses, Source, Remark); } }