using System.Text.Json;
using MiAssessment.Admin.Business.Data;
using MiAssessment.Admin.Business.Entities;
using MiAssessment.Admin.Business.Models;
using MiAssessment.Admin.Business.Models.Config;
using MiAssessment.Admin.Business.Services.Interfaces;
using MiAssessment.Core.Interfaces;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace MiAssessment.Admin.Business.Services;
///
/// 后台配置服务实现
/// 从 Admin 数据库读取配置
///
public class AdminConfigService : IAdminConfigService
{
private readonly AdminConfigDbContext _adminDbContext;
private readonly IRedisService _redisService;
private readonly ILogger _logger;
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false
};
public AdminConfigService(
AdminConfigDbContext adminDbContext,
IRedisService redisService,
ILogger logger)
{
_adminDbContext = adminDbContext;
_redisService = redisService;
_logger = logger;
}
///
public async Task GetConfigAsync(string key) where T : class
{
var jsonValue = await GetConfigRawAsync(key);
if (string.IsNullOrEmpty(jsonValue))
{
return null;
}
try
{
return JsonSerializer.Deserialize(jsonValue, JsonOptions);
}
catch (JsonException ex)
{
_logger.LogWarning(ex, "Failed to deserialize config {Key}", key);
return null;
}
}
///
public async Task GetConfigRawAsync(string key)
{
// 尝试从缓存获取
var cacheKey = GetCacheKey(key);
try
{
var cachedValue = await _redisService.GetStringAsync(cacheKey);
if (!string.IsNullOrEmpty(cachedValue))
{
return cachedValue;
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to get config from cache: {Key}", key);
}
// 从数据库获取
var config = await _adminDbContext.AdminConfigs
.Where(c => c.ConfigKey == key)
.Select(c => c.ConfigValue)
.FirstOrDefaultAsync();
// 存入缓存(10分钟过期)
if (!string.IsNullOrEmpty(config))
{
try
{
await _redisService.SetStringAsync(cacheKey, config, TimeSpan.FromMinutes(10));
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to cache config: {Key}", key);
}
}
return config;
}
///
public async Task UpdateConfigAsync(string key, T config) where T : class
{
var jsonValue = JsonSerializer.Serialize(config, JsonOptions);
return await UpdateConfigRawAsync(key, jsonValue);
}
///
public async Task UpdateConfigRawAsync(string key, string jsonValue)
{
// 验证配置
var validationError = await ValidateConfigAsync(key, jsonValue);
if (!string.IsNullOrEmpty(validationError))
{
throw new BusinessException(BusinessErrorCodes.ValidationFailed, validationError);
}
// 查找现有配置
var existingConfig = await _adminDbContext.AdminConfigs
.FirstOrDefaultAsync(c => c.ConfigKey == key);
if (existingConfig != null)
{
// 更新现有配置
existingConfig.ConfigValue = jsonValue;
existingConfig.UpdatedAt = DateTime.Now;
}
else
{
// 创建新配置
var newConfig = new AdminConfig
{
ConfigKey = key,
ConfigValue = jsonValue,
CreatedAt = DateTime.Now
};
_adminDbContext.AdminConfigs.Add(newConfig);
}
var result = await _adminDbContext.SaveChangesAsync() > 0;
// 清理缓存
if (result)
{
await ClearConfigCacheAsync(key);
}
return result;
}
///
public Task ValidateConfigAsync(string key, string jsonValue)
{
try
{
// 验证JSON格式
using var doc = JsonDocument.Parse(jsonValue);
// 根据配置键进行特定验证
return key switch
{
ConfigKeys.WeixinPaySetting => Task.FromResult(ValidateWeixinPaySetting(jsonValue)),
ConfigKeys.AlipayPaySetting => Task.FromResult(ValidateAlipaySetting(jsonValue)),
ConfigKeys.MiniprogramSetting => Task.FromResult(ValidateMiniprogramSetting(jsonValue)),
ConfigKeys.H5Setting => Task.FromResult(ValidateH5Setting(jsonValue)),
_ => Task.FromResult(null)
};
}
catch (JsonException)
{
return Task.FromResult("配置格式无效,请提供有效的JSON数据");
}
}
///
public async Task ClearConfigCacheAsync(string key)
{
var cacheKey = GetCacheKey(key);
try
{
await _redisService.DeleteAsync(cacheKey);
_logger.LogInformation("Cleared config cache: {Key}", key);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to clear config cache: {Key}", key);
}
}
#region Private Validation Methods
///
/// 验证支付宝配置
///
private string? ValidateAlipaySetting(string jsonValue)
{
try
{
var setting = JsonSerializer.Deserialize(jsonValue, JsonOptions);
if (setting?.Merchants == null || setting.Merchants.Count == 0)
{
return null; // 空配置是允许的
}
foreach (var merchant in setting.Merchants)
{
// 验证AppId必填
if (string.IsNullOrWhiteSpace(merchant.AppId))
{
return $"商户\"{merchant.Name}\"的AppId不能为空";
}
// 验证商户名称必填
if (string.IsNullOrWhiteSpace(merchant.Name))
{
return "商户名称不能为空";
}
}
return null;
}
catch (JsonException)
{
return "支付宝配置格式无效";
}
}
///
/// 验证微信支付配置
///
private string? ValidateWeixinPaySetting(string jsonValue)
{
try
{
var setting = JsonSerializer.Deserialize(jsonValue, JsonOptions);
if (setting?.Merchants == null || setting.Merchants.Count == 0)
{
return null; // 空配置是允许的
}
var prefixes = new HashSet();
foreach (var merchant in setting.Merchants)
{
// 验证前缀长度
if (string.IsNullOrEmpty(merchant.OrderPrefix) || merchant.OrderPrefix.Length != 3)
{
return $"商户\"{merchant.Name}\"的前缀必须是3位字符";
}
// 验证前缀唯一性
if (!prefixes.Add(merchant.OrderPrefix))
{
return $"商户前缀\"{merchant.OrderPrefix}\"重复,每个商户的前缀必须唯一";
}
}
return null;
}
catch (JsonException)
{
return "微信支付配置格式无效";
}
}
///
/// 验证小程序配置
///
private string? ValidateMiniprogramSetting(string jsonValue)
{
try
{
var setting = JsonSerializer.Deserialize(jsonValue, JsonOptions);
if (setting?.Miniprograms == null || setting.Miniprograms.Count == 0)
{
return null; // 空配置是允许的
}
var hasDefault = false;
var prefixes = new HashSet();
foreach (var miniprogram in setting.Miniprograms)
{
// 检查是否有默认小程序
if (miniprogram.IsDefault == 1)
{
hasDefault = true;
}
// 验证订单前缀(如果有)
if (!string.IsNullOrEmpty(miniprogram.OrderPrefix))
{
if (miniprogram.OrderPrefix.Length != 2)
{
return $"小程序\"{miniprogram.Name}\"的订单前缀必须是2位字符";
}
if (!prefixes.Add(miniprogram.OrderPrefix))
{
return $"订单前缀\"{miniprogram.OrderPrefix}\"重复,每个小程序的前缀必须唯一";
}
}
}
if (!hasDefault)
{
return "请至少设置一个默认小程序";
}
return null;
}
catch (JsonException)
{
return "小程序配置格式无效";
}
}
///
/// 验证H5配置
///
private string? ValidateH5Setting(string jsonValue)
{
try
{
var setting = JsonSerializer.Deserialize(jsonValue, JsonOptions);
if (setting?.H5Apps == null || setting.H5Apps.Count == 0)
{
return null; // 空配置是允许的
}
var hasDefault = false;
var prefixes = new HashSet();
foreach (var h5app in setting.H5Apps)
{
// 检查是否有默认H5应用
if (h5app.IsDefault == 1)
{
hasDefault = true;
}
// 验证订单前缀(如果有)
if (!string.IsNullOrEmpty(h5app.OrderPrefix))
{
if (h5app.OrderPrefix.Length != 2)
{
return $"H5应用\"{h5app.Name}\"的订单前缀必须是2位字符";
}
if (!prefixes.Add(h5app.OrderPrefix))
{
return $"订单前缀\"{h5app.OrderPrefix}\"重复,每个H5应用的前缀必须唯一";
}
}
}
if (!hasDefault)
{
return "请至少设置一个默认H5应用";
}
return null;
}
catch (JsonException)
{
return "H5配置格式无效";
}
}
#endregion
#region Private Helper Methods
private static string GetCacheKey(string key) => $"config:{key}";
#endregion
}