mi-assessment/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayConfigService.cs
2026-03-18 00:08:28 +08:00

334 lines
14 KiB
C#

using System.Text.Json;
using System.Text.Json.Serialization;
using MiAssessment.Core.Interfaces;
using MiAssessment.Model.Data;
using MiAssessment.Model.Models.Payment;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace MiAssessment.Core.Services;
/// <summary>
/// 微信支付配置服务实现
/// 从数据库读取配置,支持多商户、多小程序
/// </summary>
public class WechatPayConfigService : IWechatPayConfigService
{
private readonly AdminConfigReadDbContext _adminConfigDbContext;
private readonly IRedisService _redisService;
private readonly ILogger<WechatPayConfigService> _logger;
private readonly Random _random = new();
private const string MERCHANTS_CACHE_KEY = "wechatpay:merchants";
private const string MINIPROGRAMS_CACHE_KEY = "wechatpay:miniprograms";
private static readonly TimeSpan CACHE_DURATION = TimeSpan.FromMinutes(5);
public WechatPayConfigService(
AdminConfigReadDbContext adminConfigDbContext,
IRedisService redisService,
ILogger<WechatPayConfigService> logger)
{
_adminConfigDbContext = adminConfigDbContext;
_redisService = redisService;
_logger = logger;
}
private async Task<List<WechatPayMerchantConfig>> LoadMerchantsAsync()
{
var cachedJson = await _redisService.GetStringAsync(MERCHANTS_CACHE_KEY);
if (!string.IsNullOrEmpty(cachedJson))
{
try
{
var cached = JsonSerializer.Deserialize<List<WechatPayMerchantConfig>>(cachedJson);
if (cached != null && cached.Count > 0) return cached;
}
catch { }
}
var merchants = new List<WechatPayMerchantConfig>();
try
{
var settingConfig = await _adminConfigDbContext.AdminConfigs
.Where(c => c.ConfigKey == "weixinpay_setting")
.Select(c => c.ConfigValue)
.FirstOrDefaultAsync();
if (!string.IsNullOrEmpty(settingConfig))
{
var setting = JsonSerializer.Deserialize<DbWeixinPaySetting>(settingConfig, JsonOptions);
if (setting?.Merchants != null)
{
foreach (var m in setting.Merchants.Where(x => x.IsEnabled == "1"))
{
merchants.Add(new WechatPayMerchantConfig
{
Name = m.Name ?? "",
MchId = m.MchId ?? "",
Key = m.ApiKey ?? "",
OrderPrefix = m.OrderPrefix ?? "",
PayVersion = m.PayVersion ?? "V2",
ApiV3Key = m.ApiV3Key,
CertSerialNo = m.CertSerialNo,
PrivateKeyPath = m.PrivateKeyPath,
PrivateKeyContent = m.PrivateKeyContent,
WechatPublicKeyId = m.WechatPublicKeyId,
WechatPublicKeyPath = m.WechatPublicKeyPath,
WechatPublicKeyContent = m.WechatPublicKeyContent,
NotifyUrl = m.NotifyUrl ?? ""
});
}
}
}
var weixinpayConfig = await _adminConfigDbContext.AdminConfigs
.Where(c => c.ConfigKey == "weixinpay")
.Select(c => c.ConfigValue)
.FirstOrDefaultAsync();
if (!string.IsNullOrEmpty(weixinpayConfig))
{
var config = JsonSerializer.Deserialize<DbWeixinPayConfig>(weixinpayConfig, JsonOptions);
if (config != null && !string.IsNullOrEmpty(config.MchId) && !merchants.Any(m => m.MchId == config.MchId))
{
merchants.Add(new WechatPayMerchantConfig
{
Name = "默认商户",
MchId = config.MchId,
AppId = config.AppId ?? "",
Key = config.Keys ?? "",
OrderPrefix = "MYH",
PayVersion = "V2",
NotifyUrl = ""
});
}
}
_logger.LogInformation("从数据库加载了 {Count} 个商户配置", merchants.Count);
if (merchants.Count > 0)
{
await _redisService.SetStringAsync(MERCHANTS_CACHE_KEY, JsonSerializer.Serialize(merchants), CACHE_DURATION);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "加载商户配置失败");
}
return merchants;
}
private async Task<List<MiniprogramConfig>> LoadMiniprogramsAsync()
{
var cachedJson = await _redisService.GetStringAsync(MINIPROGRAMS_CACHE_KEY);
if (!string.IsNullOrEmpty(cachedJson))
{
try
{
var cached = JsonSerializer.Deserialize<List<MiniprogramConfig>>(cachedJson);
if (cached != null && cached.Count > 0) return cached;
}
catch { }
}
var miniprograms = new List<MiniprogramConfig>();
try
{
var settingConfig = await _adminConfigDbContext.AdminConfigs
.Where(c => c.ConfigKey == "miniprogram_setting")
.Select(c => c.ConfigValue)
.FirstOrDefaultAsync();
if (!string.IsNullOrEmpty(settingConfig))
{
var setting = JsonSerializer.Deserialize<DbMiniprogramSetting>(settingConfig, JsonOptions);
if (setting?.Miniprograms != null)
{
foreach (var m in setting.Miniprograms)
{
miniprograms.Add(new MiniprogramConfig
{
Name = m.Name ?? "",
AppId = m.AppId ?? "",
AppSecret = m.AppSecret ?? "",
OrderPrefix = m.OrderPrefix ?? "",
IsDefault = m.IsDefault == 1,
Merchants = m.Merchants ?? new List<string>()
});
}
}
}
_logger.LogInformation("从数据库加载了 {Count} 个小程序配置", miniprograms.Count);
if (miniprograms.Count > 0)
{
await _redisService.SetStringAsync(MINIPROGRAMS_CACHE_KEY, JsonSerializer.Serialize(miniprograms), CACHE_DURATION);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "加载小程序配置失败");
}
return miniprograms;
}
public WechatPayMerchantConfig GetDefaultConfig()
{
var merchants = LoadMerchantsAsync().GetAwaiter().GetResult();
return merchants.FirstOrDefault() ?? new WechatPayMerchantConfig();
}
public WechatPayMerchantConfig GetMerchantByOrderNo(string orderNo)
{
var merchants = LoadMerchantsAsync().GetAwaiter().GetResult();
var miniprograms = LoadMiniprogramsAsync().GetAwaiter().GetResult();
if (string.IsNullOrEmpty(orderNo) || merchants.Count == 0)
return GetDefaultConfig();
var miniprogram = miniprograms.FirstOrDefault(m => m.IsDefault) ?? miniprograms.FirstOrDefault();
WechatPayMerchantConfig? selectedMerchant = null;
string appId = miniprogram?.AppId ?? "";
if (miniprogram != null && miniprogram.Merchants.Count > 0)
{
var associatedMerchants = merchants.Where(m => miniprogram.Merchants.Contains(m.MchId)).ToList();
if (associatedMerchants.Count > 0)
{
selectedMerchant = GetRandomMerchant(associatedMerchants);
_logger.LogDebug("从小程序关联商户中选择: MchId={MchId}", selectedMerchant?.MchId);
}
}
selectedMerchant ??= merchants.FirstOrDefault();
if (selectedMerchant == null) return new WechatPayMerchantConfig();
return new WechatPayMerchantConfig
{
Name = selectedMerchant.Name,
MchId = selectedMerchant.MchId,
AppId = appId,
Key = selectedMerchant.Key,
OrderPrefix = selectedMerchant.OrderPrefix,
Weight = selectedMerchant.Weight,
NotifyUrl = selectedMerchant.NotifyUrl,
PayVersion = selectedMerchant.PayVersion,
ApiV3Key = selectedMerchant.ApiV3Key,
CertSerialNo = selectedMerchant.CertSerialNo,
PrivateKeyPath = selectedMerchant.PrivateKeyPath,
PrivateKeyContent = selectedMerchant.PrivateKeyContent,
WechatPublicKeyId = selectedMerchant.WechatPublicKeyId,
WechatPublicKeyPath = selectedMerchant.WechatPublicKeyPath,
WechatPublicKeyContent = selectedMerchant.WechatPublicKeyContent
};
}
public WechatPayMerchantConfig? GetMerchantByPrefix(string merchantPrefix)
{
if (string.IsNullOrEmpty(merchantPrefix)) return null;
var merchants = LoadMerchantsAsync().GetAwaiter().GetResult();
return merchants.FirstOrDefault(m => !string.IsNullOrEmpty(m.OrderPrefix) && m.OrderPrefix.Equals(merchantPrefix, StringComparison.OrdinalIgnoreCase));
}
public MiniprogramConfig? GetMiniprogramByPrefix(string miniprogramPrefix)
{
if (string.IsNullOrEmpty(miniprogramPrefix)) return null;
var miniprograms = LoadMiniprogramsAsync().GetAwaiter().GetResult();
return miniprograms.FirstOrDefault(m => !string.IsNullOrEmpty(m.OrderPrefix) && m.OrderPrefix.Equals(miniprogramPrefix, StringComparison.OrdinalIgnoreCase));
}
public MiniprogramConfig? GetMiniprogramByDomain(string domain) => GetDefaultMiniprogram();
public MiniprogramConfig? GetDefaultMiniprogram()
{
var miniprograms = LoadMiniprogramsAsync().GetAwaiter().GetResult();
return miniprograms.FirstOrDefault(m => m.IsDefault) ?? miniprograms.FirstOrDefault();
}
public OrderPrefixInfo? ExtractOrderPrefix(string orderNo)
{
if (string.IsNullOrEmpty(orderNo) || (!orderNo.StartsWith("MH_") && !orderNo.StartsWith("FH_")))
return null;
if (orderNo.Length < 6) return null;
return new OrderPrefixInfo
{
MerchantPrefix = orderNo.Substring(3, 3),
MiniprogramPrefix = orderNo.Length >= 8 ? orderNo.Substring(6, 2) : null
};
}
public WechatPayMerchantConfig? GetRandomMerchant(IEnumerable<WechatPayMerchantConfig> merchants)
{
var list = merchants.ToList();
if (list.Count == 0) return null;
if (list.Count == 1) return list[0];
var totalWeight = list.Sum(m => m.Weight > 0 ? m.Weight : 1);
var randomWeight = _random.Next(1, totalWeight + 1);
var currentWeight = 0;
foreach (var merchant in list)
{
currentWeight += merchant.Weight > 0 ? merchant.Weight : 1;
if (randomWeight <= currentWeight) return merchant;
}
return list[0];
}
public (WechatPayMerchantConfig Merchant, string AppId) GetWxPayConfig()
{
var merchant = GetMerchantByOrderNo("");
return (merchant, merchant.AppId);
}
public (WechatPayMerchantConfig? Merchant, string AppId) GetFixedWxPayConfig(string orderPrefix)
{
var merchant = GetMerchantByPrefix(orderPrefix);
if (merchant == null)
{
var config = GetWxPayConfig();
return (config.Merchant, config.AppId);
}
var miniprogram = GetDefaultMiniprogram();
return (merchant, miniprogram?.AppId ?? merchant.AppId);
}
private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true };
private class DbWeixinPaySetting { [JsonPropertyName("merchants")] public List<DbMerchantConfig>? Merchants { get; set; } }
private class DbMerchantConfig
{
[JsonPropertyName("name")] public string? Name { get; set; }
[JsonPropertyName("mch_id")] public string? MchId { get; set; }
[JsonPropertyName("order_prefix")] public string? OrderPrefix { get; set; }
[JsonPropertyName("api_key")] public string? ApiKey { get; set; }
[JsonPropertyName("is_enabled")] public string? IsEnabled { get; set; }
[JsonPropertyName("pay_version")] public string? PayVersion { get; set; }
[JsonPropertyName("api_v3_key")] public string? ApiV3Key { get; set; }
[JsonPropertyName("cert_serial_no")] public string? CertSerialNo { get; set; }
[JsonPropertyName("private_key_path")] public string? PrivateKeyPath { get; set; }
[JsonPropertyName("private_key_content")] public string? PrivateKeyContent { get; set; }
[JsonPropertyName("wechat_public_key_id")] public string? WechatPublicKeyId { get; set; }
[JsonPropertyName("wechat_public_key_path")] public string? WechatPublicKeyPath { get; set; }
[JsonPropertyName("wechat_public_key_content")] public string? WechatPublicKeyContent { get; set; }
[JsonPropertyName("notify_url")] public string? NotifyUrl { get; set; }
}
private class DbWeixinPayConfig
{
[JsonPropertyName("appid")] public string? AppId { get; set; }
[JsonPropertyName("mch_id")] public string? MchId { get; set; }
[JsonPropertyName("keys")] public string? Keys { get; set; }
}
private class DbMiniprogramSetting { [JsonPropertyName("miniprograms")] public List<DbMiniprogramConfig>? Miniprograms { get; set; } }
private class DbMiniprogramConfig
{
[JsonPropertyName("name")] public string? Name { get; set; }
[JsonPropertyName("appid")] public string? AppId { get; set; }
[JsonPropertyName("appsecret")] public string? AppSecret { get; set; }
[JsonPropertyName("order_prefix")] public string? OrderPrefix { get; set; }
[JsonPropertyName("is_default")] public int IsDefault { get; set; }
[JsonPropertyName("merchants")] public List<string>? Merchants { get; set; }
}
}