using System; using System.Threading.Tasks; using AIK.Common.Interface; using AIK.Common.Language; using AIK.Models.ApiModels.Payment; using AIK.Service.IService; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Extensions.Logging; using System.Collections.ObjectModel; namespace AIK.ViewModels.Integral { public partial class IntegralRechargeViewModel : ObservableObject { private const decimal MinRechargeAmount = 0.01m; private const int IntegralRate = 50; private readonly IDataCacheService _dataCacheService; private readonly IPaymentOrderService _paymentOrderService; private readonly IQRCodeService _qrCodeService; private readonly IUserSessionCoordinator _userSessionCoordinator; private readonly ILogger _logger; public IntegralRechargeViewModel( IDataCacheService dataCacheService, IPaymentOrderService paymentOrderService, IQRCodeService qrCodeService, IUserSessionCoordinator userSessionCoordinator, ILogger logger) { _dataCacheService = dataCacheService; _paymentOrderService = paymentOrderService; _qrCodeService = qrCodeService; _userSessionCoordinator = userSessionCoordinator; _logger = logger; AmountOptions = new ObservableCollection { new(50, 2500), new(100, 5000), new(200, 10000), new(300, 15000), new(400, 20000), new(500, 25000) }; } public ObservableCollection AmountOptions { get; } [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(PayCommand))] private decimal? _amount; [ObservableProperty] private int _currentIntegral; [ObservableProperty] private int _expectedIntegral; [ObservableProperty] private string _statusMessage = string.Empty; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(PayCommand))] private bool _isBusy; public event Action? PaymentCreated; partial void OnAmountChanged(decimal? value) { ExpectedIntegral = value.HasValue ? (int)(value.Value * IntegralRate) : 0; foreach (var option in AmountOptions) { option.IsSelected = value.HasValue && option.Amount == value.Value; } } [RelayCommand] private void SelectAmount(RechargeAmountOption option) { Amount = option.Amount; } [RelayCommand(CanExecute = nameof(CanPay))] private async Task PayAsync() { if (!Amount.HasValue || Amount.Value < MinRechargeAmount) { StatusMessage = GetLanguageText("RechargeMinAmountInvalid"); return; } var token = await _dataCacheService.GetLatestUserTokenAsync(); if (string.IsNullOrWhiteSpace(token)) { StatusMessage = GetLanguageText("RechargeLoginRequired"); return; } IsBusy = true; StatusMessage = GetLanguageText("RechargeCreatingOrder"); try { var payment = await _paymentOrderService.CreateIntegralOrderAsync(token, new CreateIntegralOrderRequest { Amount = Amount.Value, #if CN || TEST PayType = 2 #else PayType = 5 #endif }); #if CN || TEST if (payment == null || string.IsNullOrWhiteSpace(payment.code_url)) { StatusMessage = GetLanguageText("RechargePcQrCodeMissing"); return; } var qrBytes = _qrCodeService.GenerateQRCodeWithLogo(payment.code_url, GetLogoPath(), 10); StatusMessage = GetLanguageText("RechargeWechatScanToPay"); PaymentCreated?.Invoke(payment, Amount.Value, qrBytes); #else if (payment == null || string.IsNullOrWhiteSpace(payment.PaypalApproveUrl)) { StatusMessage = GetLanguageText("RechargePaypalParametersMissing"); return; } StatusMessage = GetLanguageText("RechargeOpeningPaypal"); PaymentCreated?.Invoke(payment, Amount.Value, null); #endif } catch (Exception ex) { _logger.LogError(ex, "创建积分充值订单失败"); StatusMessage = GetLanguageText("RechargeCreateOrderFailed"); } finally { IsBusy = false; } } public async Task RefreshIntegralAsync() { await _userSessionCoordinator.RefreshIntegralAsync(); } private bool CanPay() { return !IsBusy && Amount.HasValue && Amount.Value >= MinRechargeAmount; } private static string GetLogoPath() { #if CN || TEST return "AIK.Assets.Images.AIKChina.png"; #else return "AIK.Assets.Images.AIKInternationl.png"; #endif } private static string GetLanguageText(string key) { return AiKLanguage.LanguageKey?.GetValueOrDefault(key) ?? string.Empty; } } }