♻️ Centralized data models (wip)

This commit is contained in:
2025-09-27 14:09:28 +08:00
parent 51b6f7309e
commit e70d8371f8
206 changed files with 1352 additions and 2128 deletions

View File

@@ -1,4 +1,5 @@
using DysonNetwork.Pass.Auth;
using DysonNetwork.Shared.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -10,7 +11,7 @@ namespace DysonNetwork.Pass.Wallet;
public class OrderController(PaymentService payment, AuthService auth, AppDatabase db) : ControllerBase
{
[HttpGet("{id:guid}")]
public async Task<ActionResult<Order>> GetOrderById(Guid id)
public async Task<ActionResult<SnWalletOrder>> GetOrderById(Guid id)
{
var order = await db.PaymentOrders.FindAsync(id);
@@ -22,7 +23,7 @@ public class OrderController(PaymentService payment, AuthService auth, AppDataba
[HttpPost("{id:guid}/pay")]
[Authorize]
public async Task<ActionResult<Order>> PayOrder(Guid id, [FromBody] PayOrderRequest request)
public async Task<ActionResult<SnWalletOrder>> PayOrder(Guid id, [FromBody] PayOrderRequest request)
{
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();

View File

@@ -1,126 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
using DysonNetwork.Shared.Data;
using NodaTime;
using NodaTime.Serialization.Protobuf;
namespace DysonNetwork.Pass.Wallet;
public class WalletCurrency
{
public const string SourcePoint = "points";
public const string GoldenPoint = "golds";
}
public enum OrderStatus
{
Unpaid,
Paid,
Cancelled,
Finished,
Expired
}
public class Order : ModelBase
{
public const string InternalAppIdentifier = "internal";
public Guid Id { get; set; } = Guid.NewGuid();
public OrderStatus Status { get; set; } = OrderStatus.Unpaid;
[MaxLength(128)] public string Currency { get; set; } = null!;
[MaxLength(4096)] public string? Remarks { get; set; }
[MaxLength(4096)] public string? AppIdentifier { get; set; }
[MaxLength(4096)] public string? ProductIdentifier { get; set; }
[Column(TypeName = "jsonb")] public Dictionary<string, object>? Meta { get; set; }
public decimal Amount { get; set; }
public Instant ExpiredAt { get; set; }
public Guid? PayeeWalletId { get; set; }
public Wallet? PayeeWallet { get; set; } = null!;
public Guid? TransactionId { get; set; }
public Transaction? Transaction { get; set; }
public Shared.Proto.Order ToProtoValue() => new()
{
Id = Id.ToString(),
Status = (Shared.Proto.OrderStatus)Status,
Currency = Currency,
Remarks = Remarks,
AppIdentifier = AppIdentifier,
ProductIdentifier = ProductIdentifier,
Meta = Meta == null
? null
: Google.Protobuf.ByteString.CopyFrom(System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(Meta)),
Amount = Amount.ToString(CultureInfo.InvariantCulture),
ExpiredAt = ExpiredAt.ToTimestamp(),
PayeeWalletId = PayeeWalletId?.ToString(),
TransactionId = TransactionId?.ToString(),
Transaction = Transaction?.ToProtoValue(),
};
public static Order FromProtoValue(Shared.Proto.Order proto) => new()
{
Id = Guid.Parse(proto.Id),
Status = (OrderStatus)proto.Status,
Currency = proto.Currency,
Remarks = proto.Remarks,
AppIdentifier = proto.AppIdentifier,
ProductIdentifier = proto.ProductIdentifier,
Meta = proto.HasMeta
? System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(proto.Meta.ToByteArray())
: null,
Amount = decimal.Parse(proto.Amount),
ExpiredAt = proto.ExpiredAt.ToInstant(),
PayeeWalletId = proto.PayeeWalletId is not null ? Guid.Parse(proto.PayeeWalletId) : null,
TransactionId = proto.TransactionId is not null ? Guid.Parse(proto.TransactionId) : null,
Transaction = proto.Transaction is not null ? Transaction.FromProtoValue(proto.Transaction) : null,
};
}
public enum TransactionType
{
System,
Transfer,
Order
}
public class Transaction : ModelBase
{
public Guid Id { get; set; } = Guid.NewGuid();
[MaxLength(128)] public string Currency { get; set; } = null!;
public decimal Amount { get; set; }
[MaxLength(4096)] public string? Remarks { get; set; }
public TransactionType Type { get; set; }
// When the payer is null, it's pay from the system
public Guid? PayerWalletId { get; set; }
public Wallet? PayerWallet { get; set; }
// When the payee is null, it's pay for the system
public Guid? PayeeWalletId { get; set; }
public Wallet? PayeeWallet { get; set; }
public Shared.Proto.Transaction ToProtoValue() => new()
{
Id = Id.ToString(),
Currency = Currency,
Amount = Amount.ToString(CultureInfo.InvariantCulture),
Remarks = Remarks,
Type = (Shared.Proto.TransactionType)Type,
PayerWalletId = PayerWalletId?.ToString(),
PayeeWalletId = PayeeWalletId?.ToString(),
};
public static Transaction FromProtoValue(Shared.Proto.Transaction proto) => new()
{
Id = Guid.Parse(proto.Id),
Currency = proto.Currency,
Amount = decimal.Parse(proto.Amount),
Remarks = proto.Remarks,
Type = (TransactionType)proto.Type,
PayerWalletId = proto.PayerWalletId is not null ? Guid.Parse(proto.PayerWalletId) : null,
PayeeWalletId = proto.PayeeWalletId is not null ? Guid.Parse(proto.PayeeWalletId) : null,
};
}

View File

@@ -1,6 +1,7 @@
using System.Globalization;
using System.Text.Json;
using DysonNetwork.Pass.Localization;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto;
using DysonNetwork.Shared.Stream;
using Microsoft.EntityFrameworkCore;

View File

@@ -1,3 +1,4 @@
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto;
using Grpc.Core;
using NodaTime;

View File

@@ -1,405 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
using DysonNetwork.Shared.Data;
using Microsoft.EntityFrameworkCore;
using NodaTime;
using NodaTime.Serialization.Protobuf;
namespace DysonNetwork.Pass.Wallet;
public record class SubscriptionTypeData(
string Identifier,
string? GroupIdentifier,
string Currency,
decimal BasePrice,
int? RequiredLevel = null
)
{
public static readonly Dictionary<string, SubscriptionTypeData> SubscriptionDict =
new()
{
[SubscriptionType.Twinkle] = new SubscriptionTypeData(
SubscriptionType.Twinkle,
SubscriptionType.StellarProgram,
WalletCurrency.SourcePoint,
0,
1
),
[SubscriptionType.Stellar] = new SubscriptionTypeData(
SubscriptionType.Stellar,
SubscriptionType.StellarProgram,
WalletCurrency.SourcePoint,
1200,
3
),
[SubscriptionType.Nova] = new SubscriptionTypeData(
SubscriptionType.Nova,
SubscriptionType.StellarProgram,
WalletCurrency.SourcePoint,
2400,
6
),
[SubscriptionType.Supernova] = new SubscriptionTypeData(
SubscriptionType.Supernova,
SubscriptionType.StellarProgram,
WalletCurrency.SourcePoint,
3600,
9
)
};
public static readonly Dictionary<string, string> SubscriptionHumanReadable =
new()
{
[SubscriptionType.Twinkle] = "Stellar Program Twinkle",
[SubscriptionType.Stellar] = "Stellar Program",
[SubscriptionType.Nova] = "Stellar Program Nova",
[SubscriptionType.Supernova] = "Stellar Program Supernova"
};
}
public abstract class SubscriptionType
{
/// <summary>
/// DO NOT USE THIS TYPE DIRECTLY,
/// this is the prefix of all the stellar program subscriptions.
/// </summary>
public const string StellarProgram = "solian.stellar";
/// <summary>
/// No actual usage, just tells there is a free level named twinkle.
/// Applies to every registered user by default, so there is no need to create a record in db for that.
/// </summary>
public const string Twinkle = "solian.stellar.twinkle";
public const string Stellar = "solian.stellar.primary";
public const string Nova = "solian.stellar.nova";
public const string Supernova = "solian.stellar.supernova";
}
public abstract class SubscriptionPaymentMethod
{
/// <summary>
/// The solar points / solar dollars.
/// </summary>
public const string InAppWallet = "solian.wallet";
/// <summary>
/// afdian.com
/// aka. China patreon
/// </summary>
public const string Afdian = "afdian";
}
public enum SubscriptionStatus
{
Unpaid,
Active,
Expired,
Cancelled
}
/// <summary>
/// The subscription is for the Stellar Program in most cases.
/// The paid subscription in another word.
/// </summary>
[Index(nameof(Identifier))]
public class Subscription : ModelBase
{
public Guid Id { get; set; } = Guid.NewGuid();
public Instant BegunAt { get; set; }
public Instant? EndedAt { get; set; }
/// <summary>
/// The type of the subscriptions
/// </summary>
[MaxLength(4096)]
public string Identifier { get; set; } = null!;
/// <summary>
/// The field is used to override the activation status of the membership.
/// Might be used for refund handling and other special cases.
///
/// Go see the IsAvailable field if you want to get real the status of the membership.
/// </summary>
public bool IsActive { get; set; } = true;
/// <summary>
/// Indicates is the current user got the membership for free,
/// to prevent giving the same discount for the same user again.
/// </summary>
public bool IsFreeTrial { get; set; }
public SubscriptionStatus Status { get; set; } = SubscriptionStatus.Unpaid;
[MaxLength(4096)] public string PaymentMethod { get; set; } = null!;
[Column(TypeName = "jsonb")] public PaymentDetails PaymentDetails { get; set; } = null!;
public decimal BasePrice { get; set; }
public Guid? CouponId { get; set; }
public Coupon? Coupon { get; set; }
public Instant? RenewalAt { get; set; }
public Guid AccountId { get; set; }
public Account.Account Account { get; set; } = null!;
[NotMapped]
public bool IsAvailable
{
get
{
if (!IsActive) return false;
var now = SystemClock.Instance.GetCurrentInstant();
if (BegunAt > now) return false;
if (EndedAt.HasValue && now > EndedAt.Value) return false;
if (RenewalAt.HasValue && now > RenewalAt.Value) return false;
if (Status != SubscriptionStatus.Active) return false;
return true;
}
}
[NotMapped]
public decimal FinalPrice
{
get
{
if (IsFreeTrial) return 0;
if (Coupon == null) return BasePrice;
var now = SystemClock.Instance.GetCurrentInstant();
if (Coupon.AffectedAt.HasValue && now < Coupon.AffectedAt.Value ||
Coupon.ExpiredAt.HasValue && now > Coupon.ExpiredAt.Value) return BasePrice;
if (Coupon.DiscountAmount.HasValue) return BasePrice - Coupon.DiscountAmount.Value;
if (Coupon.DiscountRate.HasValue) return BasePrice * (decimal)(1 - Coupon.DiscountRate.Value);
return BasePrice;
}
}
/// <summary>
/// Returns a reference object that contains a subset of subscription data
/// suitable for client-side use, with sensitive information removed.
/// </summary>
public SubscriptionReferenceObject ToReference()
{
return new SubscriptionReferenceObject
{
Id = Id,
Identifier = Identifier,
BegunAt = BegunAt,
EndedAt = EndedAt,
IsActive = IsActive,
IsAvailable = IsAvailable,
IsFreeTrial = IsFreeTrial,
Status = Status,
BasePrice = BasePrice,
FinalPrice = FinalPrice,
RenewalAt = RenewalAt,
AccountId = AccountId
};
}
public Shared.Proto.Subscription ToProtoValue() => new()
{
Id = Id.ToString(),
BegunAt = BegunAt.ToTimestamp(),
EndedAt = EndedAt?.ToTimestamp(),
Identifier = Identifier,
IsActive = IsActive,
IsFreeTrial = IsFreeTrial,
Status = (Shared.Proto.SubscriptionStatus)Status,
PaymentMethod = PaymentMethod,
PaymentDetails = PaymentDetails.ToProtoValue(),
BasePrice = BasePrice.ToString(CultureInfo.InvariantCulture),
CouponId = CouponId?.ToString(),
Coupon = Coupon?.ToProtoValue(),
RenewalAt = RenewalAt?.ToTimestamp(),
AccountId = AccountId.ToString(),
IsAvailable = IsAvailable,
FinalPrice = FinalPrice.ToString(CultureInfo.InvariantCulture),
CreatedAt = CreatedAt.ToTimestamp(),
UpdatedAt = UpdatedAt.ToTimestamp()
};
public static Subscription FromProtoValue(Shared.Proto.Subscription proto) => new()
{
Id = Guid.Parse(proto.Id),
BegunAt = proto.BegunAt.ToInstant(),
EndedAt = proto.EndedAt?.ToInstant(),
Identifier = proto.Identifier,
IsActive = proto.IsActive,
IsFreeTrial = proto.IsFreeTrial,
Status = (SubscriptionStatus)proto.Status,
PaymentMethod = proto.PaymentMethod,
PaymentDetails = PaymentDetails.FromProtoValue(proto.PaymentDetails),
BasePrice = decimal.Parse(proto.BasePrice),
CouponId = proto.HasCouponId ? Guid.Parse(proto.CouponId) : null,
Coupon = proto.Coupon is not null ? Coupon.FromProtoValue(proto.Coupon) : null,
RenewalAt = proto.RenewalAt?.ToInstant(),
AccountId = Guid.Parse(proto.AccountId),
CreatedAt = proto.CreatedAt.ToInstant(),
UpdatedAt = proto.UpdatedAt.ToInstant()
};
}
/// <summary>
/// A reference object for Subscription that contains only non-sensitive information
/// suitable for client-side use.
/// </summary>
public class SubscriptionReferenceObject : ModelBase
{
public Guid Id { get; set; }
public string Identifier { get; set; } = null!;
public Instant BegunAt { get; set; }
public Instant? EndedAt { get; set; }
public bool IsActive { get; set; }
public bool IsAvailable { get; set; }
public bool IsFreeTrial { get; set; }
public SubscriptionStatus Status { get; set; }
public decimal BasePrice { get; set; }
public decimal FinalPrice { get; set; }
public Instant? RenewalAt { get; set; }
public Guid AccountId { get; set; }
/// <summary>
/// Gets the human-readable name of the subscription type if available.
/// </summary>
[NotMapped]
public string? DisplayName => SubscriptionTypeData.SubscriptionHumanReadable.TryGetValue(Identifier, out var name)
? name
: null;
public Shared.Proto.SubscriptionReferenceObject ToProtoValue() => new()
{
Id = Id.ToString(),
Identifier = Identifier,
BegunAt = BegunAt.ToTimestamp(),
EndedAt = EndedAt?.ToTimestamp(),
IsActive = IsActive,
IsAvailable = IsAvailable,
IsFreeTrial = IsFreeTrial,
Status = (Shared.Proto.SubscriptionStatus)Status,
BasePrice = BasePrice.ToString(CultureInfo.CurrentCulture),
FinalPrice = FinalPrice.ToString(CultureInfo.CurrentCulture),
RenewalAt = RenewalAt?.ToTimestamp(),
AccountId = AccountId.ToString(),
DisplayName = DisplayName,
CreatedAt = CreatedAt.ToTimestamp(),
UpdatedAt = UpdatedAt.ToTimestamp()
};
public static SubscriptionReferenceObject FromProtoValue(Shared.Proto.SubscriptionReferenceObject proto) => new()
{
Id = Guid.Parse(proto.Id),
Identifier = proto.Identifier,
BegunAt = proto.BegunAt.ToInstant(),
EndedAt = proto.EndedAt?.ToInstant(),
IsActive = proto.IsActive,
IsAvailable = proto.IsAvailable,
IsFreeTrial = proto.IsFreeTrial,
Status = (SubscriptionStatus)proto.Status,
BasePrice = decimal.Parse(proto.BasePrice),
FinalPrice = decimal.Parse(proto.FinalPrice),
RenewalAt = proto.RenewalAt?.ToInstant(),
AccountId = Guid.Parse(proto.AccountId),
CreatedAt = proto.CreatedAt.ToInstant(),
UpdatedAt = proto.UpdatedAt.ToInstant()
};
}
public class PaymentDetails
{
public string Currency { get; set; } = null!;
public string? OrderId { get; set; }
public Shared.Proto.PaymentDetails ToProtoValue() => new()
{
Currency = Currency,
OrderId = OrderId,
};
public static PaymentDetails FromProtoValue(Shared.Proto.PaymentDetails proto) => new()
{
Currency = proto.Currency,
OrderId = proto.OrderId,
};
}
/// <summary>
/// A discount that can applies in purchases among the Solar Network.
/// For now, it can be used in the subscription purchase.
/// </summary>
public class Coupon : ModelBase
{
public Guid Id { get; set; } = Guid.NewGuid();
/// <summary>
/// The items that can apply this coupon.
/// Leave it to null to apply to all items.
/// </summary>
[MaxLength(4096)]
public string? Identifier { get; set; }
/// <summary>
/// The code that human-readable and memorizable.
/// Leave it blank to use it only with the ID.
/// </summary>
[MaxLength(1024)]
public string? Code { get; set; }
public Instant? AffectedAt { get; set; }
public Instant? ExpiredAt { get; set; }
/// <summary>
/// The amount of the discount.
/// If this field and the rate field are both not null,
/// the amount discount will be applied and the discount rate will be ignored.
/// Formula: <code>final price = base price - discount amount</code>
/// </summary>
public decimal? DiscountAmount { get; set; }
/// <summary>
/// The percentage of the discount.
/// If this field and the amount field are both not null,
/// this field will be ignored.
/// Formula: <code>final price = base price * (1 - discount rate)</code>
/// </summary>
public double? DiscountRate { get; set; }
/// <summary>
/// The max usage of the current coupon.
/// Leave it to null to use it unlimited.
/// </summary>
public int? MaxUsage { get; set; }
public Shared.Proto.Coupon ToProtoValue() => new()
{
Id = Id.ToString(),
Identifier = Identifier,
Code = Code,
AffectedAt = AffectedAt?.ToTimestamp(),
ExpiredAt = ExpiredAt?.ToTimestamp(),
DiscountAmount = DiscountAmount?.ToString(),
DiscountRate = DiscountRate,
MaxUsage = MaxUsage,
CreatedAt = CreatedAt.ToTimestamp(),
UpdatedAt = UpdatedAt.ToTimestamp()
};
public static Coupon FromProtoValue(Shared.Proto.Coupon proto) => new()
{
Id = Guid.Parse(proto.Id),
Identifier = proto.Identifier,
Code = proto.Code,
AffectedAt = proto.AffectedAt?.ToInstant(),
ExpiredAt = proto.ExpiredAt?.ToInstant(),
DiscountAmount = proto.HasDiscountAmount ? decimal.Parse(proto.DiscountAmount) : null,
DiscountRate = proto.DiscountRate,
MaxUsage = proto.MaxUsage,
CreatedAt = proto.CreatedAt.ToInstant(),
UpdatedAt = proto.UpdatedAt.ToInstant()
};
}

View File

@@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore;
using NodaTime;
using System.ComponentModel.DataAnnotations;
using DysonNetwork.Pass.Wallet.PaymentHandlers;
using DysonNetwork.Shared.Models;
namespace DysonNetwork.Pass.Wallet;
@@ -14,7 +15,7 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
{
[HttpGet]
[Authorize]
public async Task<ActionResult<List<Subscription>>> ListSubscriptions(
public async Task<ActionResult<List<SnSubscription>>> ListSubscriptions(
[FromQuery] int offset = 0,
[FromQuery] int take = 20
)
@@ -40,7 +41,7 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
[HttpGet("fuzzy/{prefix}")]
[Authorize]
public async Task<ActionResult<Subscription>> GetSubscriptionFuzzy(string prefix)
public async Task<ActionResult<SnSubscription>> GetSubscriptionFuzzy(string prefix)
{
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
@@ -56,7 +57,7 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
[HttpGet("{identifier}")]
[Authorize]
public async Task<ActionResult<Subscription>> GetSubscription(string identifier)
public async Task<ActionResult<SnSubscription>> GetSubscription(string identifier)
{
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
@@ -79,7 +80,7 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
[HttpPost]
[Authorize]
public async Task<ActionResult<Subscription>> CreateSubscription(
public async Task<ActionResult<SnSubscription>> CreateSubscription(
[FromBody] CreateSubscriptionRequest request,
[FromHeader(Name = "X-Noop")] bool noop = false
)
@@ -118,7 +119,7 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
[HttpPost("{identifier}/cancel")]
[Authorize]
public async Task<ActionResult<Subscription>> CancelSubscription(string identifier)
public async Task<ActionResult<SnSubscription>> CancelSubscription(string identifier)
{
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
@@ -135,7 +136,7 @@ public class SubscriptionController(SubscriptionService subscriptions, AfdianPay
[HttpPost("{identifier}/order")]
[Authorize]
public async Task<ActionResult<Order>> CreateSubscriptionOrder(string identifier)
public async Task<ActionResult<SnWalletOrder>> CreateSubscriptionOrder(string identifier)
{
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();

View File

@@ -1,3 +1,4 @@
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
using NodaTime;
using Quartz;

View File

@@ -4,6 +4,7 @@ using System.Text.Json;
using DysonNetwork.Pass.Localization;
using DysonNetwork.Pass.Wallet.PaymentHandlers;
using DysonNetwork.Shared.Cache;
using DysonNetwork.Shared.Models;
using DysonNetwork.Shared.Proto;
using Google.Protobuf.WellKnownTypes;
using Microsoft.EntityFrameworkCore;

View File

@@ -1,66 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Text.Json.Serialization;
using DysonNetwork.Shared.Data;
using NodaTime.Serialization.Protobuf;
namespace DysonNetwork.Pass.Wallet;
public class Wallet : ModelBase
{
public Guid Id { get; set; } = Guid.NewGuid();
public ICollection<WalletPocket> Pockets { get; set; } = new List<WalletPocket>();
public Guid AccountId { get; set; }
public Account.Account Account { get; set; } = null!;
public Shared.Proto.Wallet ToProtoValue()
{
var proto = new Shared.Proto.Wallet
{
Id = Id.ToString(),
AccountId = AccountId.ToString(),
};
foreach (var pocket in Pockets)
{
proto.Pockets.Add(pocket.ToProtoValue());
}
return proto;
}
public static Wallet FromProtoValue(Shared.Proto.Wallet proto) => new()
{
Id = Guid.Parse(proto.Id),
AccountId = Guid.Parse(proto.AccountId),
Pockets = proto.Pockets.Select(WalletPocket.FromProtoValue).ToList(),
};
}
public class WalletPocket : ModelBase
{
public Guid Id { get; set; } = Guid.NewGuid();
[MaxLength(128)] public string Currency { get; set; } = null!;
public decimal Amount { get; set; }
public Guid WalletId { get; set; }
[JsonIgnore] public Wallet Wallet { get; set; } = null!;
public Shared.Proto.WalletPocket ToProtoValue() => new()
{
Id = Id.ToString(),
Currency = Currency,
Amount = Amount.ToString(CultureInfo.CurrentCulture),
WalletId = WalletId.ToString(),
};
public static WalletPocket FromProtoValue(Shared.Proto.WalletPocket proto) => new()
{
Id = Guid.Parse(proto.Id),
Currency = proto.Currency,
Amount = decimal.Parse(proto.Amount),
WalletId = Guid.Parse(proto.WalletId),
};
}

View File

@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
using DysonNetwork.Pass.Permission;
using DysonNetwork.Shared.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -40,7 +41,7 @@ public class WalletController(AppDatabase db, WalletService ws, PaymentService p
[HttpGet("transactions")]
[Authorize]
public async Task<ActionResult<List<Transaction>>> GetTransactions(
public async Task<ActionResult<List<SnWalletTransaction>>> GetTransactions(
[FromQuery] int offset = 0, [FromQuery] int take = 20
)
{
@@ -67,7 +68,7 @@ public class WalletController(AppDatabase db, WalletService ws, PaymentService p
[HttpGet("orders")]
[Authorize]
public async Task<ActionResult<List<Order>>> GetOrders(
public async Task<ActionResult<List<SnWalletOrder>>> GetOrders(
[FromQuery] int offset = 0, [FromQuery] int take = 20
)
{
@@ -104,7 +105,7 @@ public class WalletController(AppDatabase db, WalletService ws, PaymentService p
[HttpPost("balance")]
[Authorize]
[RequiredPermission("maintenance", "wallets.balance.modify")]
public async Task<ActionResult<Transaction>> ModifyWalletBalance([FromBody] WalletBalanceRequest request)
public async Task<ActionResult<SnWalletTransaction>> ModifyWalletBalance([FromBody] WalletBalanceRequest request)
{
var wallet = await ws.GetWalletAsync(request.AccountId);
if (wallet is null) return NotFound("Wallet was not found.");

View File

@@ -1,3 +1,4 @@
using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Pass.Wallet;
@@ -27,7 +28,7 @@ public class WalletService(AppDatabase db)
return wallet;
}
public async Task<(WalletPocket wallet, bool isNewlyCreated)> GetOrCreateWalletPocketAsync(
public async Task<(SnWalletPocket wallet, bool isNewlyCreated)> GetOrCreateWalletPocketAsync(
Guid walletId,
string currency,
decimal? initialAmount = null
@@ -36,7 +37,7 @@ public class WalletService(AppDatabase db)
var pocket = await db.WalletPockets.FirstOrDefaultAsync(p => p.Currency == currency && p.WalletId == walletId);
if (pocket != null) return (pocket, false);
pocket = new WalletPocket
pocket = new SnWalletPocket
{
Currency = currency,
Amount = initialAmount ?? 0,