using System.Text.Json; using Tiku.Domain.Common; namespace Tiku.Domain.Commerce; public sealed class Product : AuditableTenantEntity { public Guid? RegionId { get; set; } public string? LegacyId { get; set; } public string Title { get; set; } = string.Empty; public string? LegacyPriceText { get; set; } public string? Link { get; set; } public ProductType Type { get; set; } = ProductType.Material; public JsonElement Tags { get; set; } = JsonDefaults.Array(); public int SortOrder { get; set; } public string? Cover { get; set; } public string? PreviewIframe { get; set; } public JsonElement DetailImages { get; set; } = JsonDefaults.Array(); public bool IsActive { get; set; } = true; } public sealed class SvipPlan : AuditableTenantEntity { public Guid? RegionId { get; set; } public string? LegacyId { get; set; } public string Name { get; set; } = string.Empty; public int PriceCents { get; set; } public int? OriginalPriceCents { get; set; } public int Days { get; set; } public string? Description { get; set; } public string? PerDayLabel { get; set; } public string? Badge { get; set; } public bool Recommended { get; set; } public bool CouponOnly { get; set; } public string? VpProductId { get; set; } public bool VpEnabled { get; set; } public int SortOrder { get; set; } public bool IsActive { get; set; } = true; } public sealed class Order : AuditableTenantEntity { public Guid? UserId { get; set; } public Guid? PlanId { get; set; } public Guid? RegionId { get; set; } public string? LegacyId { get; set; } public string? LegacyUserId { get; set; } public string? LegacyPlanId { get; set; } public string? LegacyRegionId { get; set; } public string OrderNo { get; set; } = string.Empty; public OrderStatus Status { get; set; } = OrderStatus.Pending; public string? ProductType { get; set; } public string? ProductName { get; set; } public int AmountCents { get; set; } public string? PayMethod { get; set; } public string? PayProvider { get; set; } public string? TradeNo { get; set; } public int? Days { get; set; } public int RefundedAmountCents { get; set; } public DateTimeOffset? PaidAt { get; set; } public JsonElement RawPayload { get; set; } = JsonDefaults.Object(); } public sealed class OrderItem : AuditableTenantEntity { public Guid OrderId { get; set; } public string? LegacyId { get; set; } public string ItemType { get; set; } = string.Empty; public Guid? ItemId { get; set; } public string Name { get; set; } = string.Empty; public int Quantity { get; set; } = 1; public int UnitAmountCents { get; set; } public int TotalAmountCents { get; set; } public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } public sealed class Payment : AuditableTenantEntity { public Guid OrderId { get; set; } public string? LegacyId { get; set; } public string? LegacyOrderId { get; set; } public string Provider { get; set; } = string.Empty; public string? Method { get; set; } public PaymentStatus Status { get; set; } = PaymentStatus.Pending; public int AmountCents { get; set; } public int RefundedAmountCents { get; set; } public string? ProviderTradeNo { get; set; } public DateTimeOffset? PaidAt { get; set; } public JsonElement RawPayload { get; set; } = JsonDefaults.Object(); } public sealed class PaymentEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid? PaymentId { get; set; } public string Provider { get; set; } = string.Empty; public string EventType { get; set; } = string.Empty; public string? EventId { get; set; } public bool? SignatureValid { get; set; } public JsonElement Payload { get; set; } = JsonDefaults.Object(); public DateTimeOffset? ProcessedAt { get; set; } public string? Error { get; set; } public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } public sealed class Entitlement : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid UserId { get; set; } public string EntitlementType { get; set; } = "svip"; public EntitlementScopeType ScopeType { get; set; } = EntitlementScopeType.Tenant; public Guid? ScopeId { get; set; } public string SourceType { get; set; } = "migration"; public Guid? SourceId { get; set; } public string? LegacySourceId { get; set; } public DateTimeOffset StartsAt { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset? ExpiresAt { get; set; } public EntitlementStatus Status { get; set; } = EntitlementStatus.Active; public DateTimeOffset? RevokedAt { get; set; } public Guid? RevokedBy { get; set; } public string? RevokedReason { get; set; } public JsonElement Metadata { get; set; } = JsonDefaults.Object(); public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } public sealed class CodeBatch : AuditableTenantEntity { public Guid? RegionId { get; set; } public Guid? CreatedBy { get; set; } public string? LegacyId { get; set; } public string Name { get; set; } = string.Empty; public string? SaleType { get; set; } public string? Channel { get; set; } public string? CampaignName { get; set; } public int DefaultUnitPriceCents { get; set; } public int CostPriceCents { get; set; } public int TotalCount { get; set; } public int? Days { get; set; } public string? LegacyRegionId { get; set; } public DateTimeOffset? IssuedAt { get; set; } public string? Remark { get; set; } public decimal? CommissionRate { get; set; } } public sealed class ActivationCode : AuditableTenantEntity { public Guid? UsedBy { get; set; } public Guid? AgentUserId { get; set; } public Guid? BatchId { get; set; } public Guid? UsedRegionId { get; set; } public Guid? CouponRedemptionId { get; set; } public string? LegacyId { get; set; } public string Code { get; set; } = string.Empty; public int Days { get; set; } public bool IsUsed { get; set; } public DateTimeOffset? UsedAt { get; set; } public string? SaleType { get; set; } public int? UnitPriceCents { get; set; } public string? SoldTo { get; set; } public string? CouponCode { get; set; } public string? Remark { get; set; } } public sealed class Coupon : AuditableTenantEntity { public Guid? PlanId { get; set; } public string? LegacyId { get; set; } public string Code { get; set; } = string.Empty; public DiscountType? DiscountType { get; set; } public decimal? DiscountValue { get; set; } public DateTimeOffset? ValidFrom { get; set; } public DateTimeOffset? ValidTo { get; set; } public int? MaxUses { get; set; } public int UsedCount { get; set; } public string? Source { get; set; } public string? Remark { get; set; } } public sealed class CouponRedemption : AuditableTenantEntity { public Guid? CouponId { get; set; } public Guid? UserId { get; set; } public Guid? PlanId { get; set; } public Guid? OrderId { get; set; } public Guid? RegionId { get; set; } public string? LegacyId { get; set; } public string? CouponCode { get; set; } public CouponRedemptionStatus Status { get; set; } = CouponRedemptionStatus.Claimed; public int? DiscountAppliedCents { get; set; } public string? Source { get; set; } public string? Remark { get; set; } public DateTimeOffset? ClaimedAt { get; set; } public DateTimeOffset? UsedAt { get; set; } } public sealed class TenantSubscription : AuditableTenantEntity { public string PlanCode { get; set; } = string.Empty; public TenantSubscriptionStatus Status { get; set; } = TenantSubscriptionStatus.Trial; public DateTimeOffset? StartsAt { get; set; } public DateTimeOffset? ExpiresAt { get; set; } public string? BillingCycle { get; set; } = "yearly"; public int AmountCents { get; set; } public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } public sealed class TenantUsageRecord : Entity, ITenantOwned { public Guid TenantId { get; set; } public string MetricKey { get; set; } = string.Empty; public decimal MetricValue { get; set; } public DateOnly PeriodStart { get; set; } public DateOnly PeriodEnd { get; set; } public JsonElement Metadata { get; set; } = JsonDefaults.Object(); public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } public sealed class CommerceRefundRequest : AuditableTenantEntity { public Guid OrderId { get; set; } public Guid? PaymentId { get; set; } public Guid? RequestedBy { get; set; } public Guid? ReviewedBy { get; set; } public Guid? ProcessedBy { get; set; } public string RefundNo { get; set; } = string.Empty; public string? Provider { get; set; } public string? ProviderRefundNo { get; set; } public CommerceRefundStatus Status { get; set; } = CommerceRefundStatus.Requested; public int AmountCents { get; set; } public string? Reason { get; set; } public RefundEntitlementAction EntitlementAction { get; set; } = RefundEntitlementAction.RevokeOnSuccess; public DateTimeOffset RequestedAt { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset? ReviewedAt { get; set; } public DateTimeOffset? ProcessedAt { get; set; } public DateTimeOffset? SucceededAt { get; set; } public DateTimeOffset? FailedAt { get; set; } public DateTimeOffset? CancelledAt { get; set; } public string? FailureReason { get; set; } public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } public sealed class CommerceRefundEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid RefundRequestId { get; set; } public CommerceRefundStatus? FromStatus { get; set; } public CommerceRefundStatus ToStatus { get; set; } = CommerceRefundStatus.Requested; public string EventType { get; set; } = string.Empty; public Guid? ActorUserId { get; set; } public JsonElement Details { get; set; } = JsonDefaults.Object(); public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } public sealed class CommerceReconciliationBatch : AuditableTenantEntity { public Guid? CreatedBy { get; set; } 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; public string? SourceName { get; set; } public string SourceHash { get; set; } = string.Empty; public ReconciliationBatchStatus Status { get; set; } = ReconciliationBatchStatus.Completed; public int TotalCount { get; set; } public int MatchedCount { get; set; } public int MismatchCount { get; set; } public int MissingLocalCount { get; set; } public int MissingProviderCount { get; set; } public int DuplicateCount { get; set; } public int IgnoredCount { get; set; } public int AmountCents { get; set; } public int RefundAmountCents { get; set; } public int FeeCents { get; set; } public DateTimeOffset? CompletedAt { get; set; } public string? Error { get; set; } public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } public sealed class CommerceReconciliationItem : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid BatchId { get; set; } public Guid? OrderId { get; set; } public Guid? PaymentId { get; set; } public Guid? RefundRequestId { get; set; } public int RowNo { get; set; } public string Provider { get; set; } = string.Empty; public ReconciliationTransactionType TransactionType { get; set; } = ReconciliationTransactionType.Payment; public string? ProviderTradeNo { get; set; } public string? ProviderRefundNo { get; set; } public string? OrderNo { get; set; } public string? RefundNo { get; set; } public int AmountCents { get; set; } public int RefundAmountCents { get; set; } public int FeeCents { get; set; } public DateTimeOffset? PaidAt { get; set; } public DateTimeOffset? RefundedAt { get; set; } public string? ProviderStatus { get; set; } public string? LocalStatus { get; set; } public ReconciliationMatchStatus MatchStatus { get; set; } = ReconciliationMatchStatus.Matched; public NotificationSeverity Severity { get; set; } = NotificationSeverity.Info; public string? IssueCode { get; set; } public JsonElement Details { get; set; } = JsonDefaults.Object(); public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } public sealed class CommerceReconciliationIssue : AuditableTenantEntity { 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 Guid? AssignedTo { get; set; } public Guid? CreatedBy { get; set; } public Guid? ResolvedBy { get; set; } public string IssueNo { get; set; } = string.Empty; public string Provider { get; set; } = string.Empty; public ReconciliationTransactionType TransactionType { get; set; } = ReconciliationTransactionType.Payment; public string? IssueCode { get; set; } public ReconciliationIssueMatchStatus MatchStatus { get; set; } = ReconciliationIssueMatchStatus.AmountMismatch; public NotificationSeverity Severity { get; set; } = NotificationSeverity.Warning; public ReconciliationIssueStatus Status { get; set; } = ReconciliationIssueStatus.Open; public ReconciliationResolutionType ResolutionType { get; set; } = ReconciliationResolutionType.None; public string? OrderNo { get; set; } public string? RefundNo { get; set; } public string? ProviderTradeNo { get; set; } public string? ProviderRefundNo { get; set; } public int AmountCents { get; set; } public int RefundAmountCents { get; set; } public DateTimeOffset? ResolvedAt { get; set; } public DateTimeOffset? DueAt { get; set; } public string? Summary { get; set; } public string? ResolutionNote { get; set; } public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } public sealed class CommerceReconciliationIssueEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid IssueId { get; set; } public ReconciliationIssueStatus? FromStatus { get; set; } public ReconciliationIssueStatus? ToStatus { get; set; } public string EventType { get; set; } = string.Empty; public Guid? ActorUserId { get; set; } public string? Note { get; set; } public JsonElement Details { get; set; } = JsonDefaults.Object(); public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } public sealed class CommerceAdjustmentVoucher : AuditableTenantEntity { 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 Guid? CreatedBy { get; set; } public Guid? ReviewedBy { get; set; } public string VoucherNo { get; set; } = string.Empty; public CommerceAdjustmentVoucherStatus Status { get; set; } = CommerceAdjustmentVoucherStatus.Draft; public CommerceAdjustmentDirection Direction { get; set; } = CommerceAdjustmentDirection.IncreaseRevenue; public int AmountCents { get; set; } public string Currency { get; set; } = "CNY"; public string Reason { get; set; } = string.Empty; public string? ProofAssetKey { get; set; } public DateTimeOffset? ReviewedAt { get; set; } public DateTimeOffset? ClosedAt { get; set; } public JsonElement Metadata { get; set; } = JsonDefaults.Object(); } public sealed class CommerceAdjustmentVoucherEvent : Entity, ITenantOwned { public Guid TenantId { get; set; } public Guid VoucherId { get; set; } public CommerceAdjustmentVoucherStatus? FromStatus { get; set; } public CommerceAdjustmentVoucherStatus ToStatus { get; set; } = CommerceAdjustmentVoucherStatus.Draft; public Guid? ActorUserId { get; set; } public string? Note { get; set; } public JsonElement Details { get; set; } = JsonDefaults.Object(); public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; } public enum ProductType { Material, Course, Service, Link, Other } public enum OrderStatus { Pending, Paid, Failed, Closed, PartiallyRefunded, Refunded } public enum PaymentStatus { Pending, Paid, Failed, Cancelled, PartiallyRefunded, Refunded } public enum EntitlementScopeType { Tenant, Region, Module, Subject, QuestionBank } public enum EntitlementStatus { Active, Revoked, Expired } public enum DiscountType { Percent, Fixed } public enum CouponRedemptionStatus { Claimed, Used, Expired, Cancelled } public enum TenantSubscriptionStatus { Trial, Active, PastDue, Cancelled, Expired } public enum CommerceRefundStatus { Requested, Approved, Processing, Succeeded, Failed, Rejected, Cancelled } public enum RefundEntitlementAction { None, RevokeOnSuccess } public enum ReconciliationBillType { Payment, Refund, Combined } public enum ReconciliationSource { ManualUpload, ProviderDownload, Api, Worker } public enum ReconciliationBatchStatus { Preview, Pending, Processing, Completed, CompletedWithIssues, Failed } public enum ReconciliationTransactionType { Payment, Refund } public enum ReconciliationMatchStatus { Matched, AmountMismatch, StatusMismatch, MissingLocal, MissingProvider, Duplicate, Ignored } public enum ReconciliationIssueMatchStatus { AmountMismatch, StatusMismatch, MissingLocal, MissingProvider, Duplicate } public enum NotificationSeverity { Info, Warning, Error, Critical } public enum ReconciliationIssueStatus { Open, Investigating, Resolved, Ignored, Escalated } public enum ReconciliationResolutionType { None, ProviderConfirmed, LocalCorrected, ManualAdjustment, FalsePositive, Duplicate, WriteOff } public enum CommerceAdjustmentVoucherStatus { Draft, PendingReview, Approved, Rejected, Closed, Void } public enum CommerceAdjustmentDirection { IncreaseRevenue, DecreaseRevenue, IncreaseRefund, DecreaseRefund, WriteOff }