diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs
index 477457f..87e195f 100644
--- a/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs
+++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs
@@ -13,15 +13,6 @@ public class AdminBusinessDbContext : DbContext
{
}
- #region 后台管理配置
-
- ///
- /// 后台配置表
- ///
- public DbSet AdminConfigs { get; set; } = null!;
-
- #endregion
-
#region 系统配置
///
@@ -153,21 +144,6 @@ public class AdminBusinessDbContext : DbContext
{
base.OnModelCreating(modelBuilder);
- // =============================================
- // AdminConfig 配置
- // =============================================
- modelBuilder.Entity(entity =>
- {
- // ConfigKey 唯一索引
- entity.HasIndex(e => e.ConfigKey)
- .IsUnique()
- .HasDatabaseName("IX_admin_configs_config_key");
-
- // ConfigValue 使用 nvarchar(max)
- entity.Property(e => e.ConfigValue)
- .HasColumnType("nvarchar(max)");
- });
-
// =============================================
// Config 配置
// =============================================
diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminConfigDbContext.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminConfigDbContext.cs
new file mode 100644
index 0000000..6299750
--- /dev/null
+++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminConfigDbContext.cs
@@ -0,0 +1,35 @@
+using Microsoft.EntityFrameworkCore;
+using MiAssessment.Admin.Business.Entities;
+
+namespace MiAssessment.Admin.Business.Data;
+
+///
+/// Admin 配置数据库上下文
+/// 连接后台管理库,专门用于读取 admin_configs 配置表
+///
+public class AdminConfigDbContext : DbContext
+{
+ public AdminConfigDbContext(DbContextOptions options) : base(options)
+ {
+ }
+
+ ///
+ /// 后台配置表
+ ///
+ public DbSet AdminConfigs { get; set; } = null!;
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
+
+ modelBuilder.Entity(entity =>
+ {
+ entity.HasIndex(e => e.ConfigKey)
+ .IsUnique()
+ .HasDatabaseName("IX_admin_configs_config_key");
+
+ entity.Property(e => e.ConfigValue)
+ .HasColumnType("nvarchar(max)");
+ });
+ }
+}
diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Extensions/ServiceCollectionExtensions.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Extensions/ServiceCollectionExtensions.cs
index caaad7f..0618e8b 100644
--- a/server/MiAssessment/src/MiAssessment.Admin.Business/Extensions/ServiceCollectionExtensions.cs
+++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Extensions/ServiceCollectionExtensions.cs
@@ -34,6 +34,12 @@ public static class ServiceCollectionExtensions
{
options.UseSqlServer(configuration.GetConnectionString("BusinessConnection"));
});
+
+ // 注册 AdminConfigDbContext,连接后台管理库读取配置
+ services.AddDbContext(options =>
+ {
+ options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));
+ });
}
// 自动注册业务服务
diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AdminConfigService.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AdminConfigService.cs
index 6536773..96fdb4b 100644
--- a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AdminConfigService.cs
+++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AdminConfigService.cs
@@ -16,7 +16,7 @@ namespace MiAssessment.Admin.Business.Services;
///
public class AdminConfigService : IAdminConfigService
{
- private readonly AdminBusinessDbContext _adminDbContext;
+ private readonly AdminConfigDbContext _adminDbContext;
private readonly IRedisService _redisService;
private readonly ILogger _logger;
@@ -28,7 +28,7 @@ public class AdminConfigService : IAdminConfigService
};
public AdminConfigService(
- AdminBusinessDbContext adminDbContext,
+ AdminConfigDbContext adminDbContext,
IRedisService redisService,
ILogger logger)
{
diff --git a/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs b/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs
index c9f8672..534247d 100644
--- a/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs
+++ b/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs
@@ -26,7 +26,7 @@ public class ConfigController : ControllerBase
///
private static class ConfigKeys
{
- public const string Upload = "upload_setting";
+ public const string Upload = "uploads";
}
public ConfigController(IAdminConfigService configService, ILogger logger)
diff --git a/server/MiAssessment/src/MiAssessment.Api/Program.cs b/server/MiAssessment/src/MiAssessment.Api/Program.cs
index 6d0e35d..a6671eb 100644
--- a/server/MiAssessment/src/MiAssessment.Api/Program.cs
+++ b/server/MiAssessment/src/MiAssessment.Api/Program.cs
@@ -46,12 +46,18 @@ try
containerBuilder.RegisterModule();
});
- // 配置 DbContext
+ // 配置 DbContext(业务库)
builder.Services.AddDbContext(options =>
{
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
});
+ // 配置 AdminConfigReadDbContext(Admin库,只读,用于读取运营配置)
+ builder.Services.AddDbContext(options =>
+ {
+ options.UseSqlServer(builder.Configuration.GetConnectionString("AdminConnection"));
+ });
+
// 配置 JWT 设置
var jwtSettings = new JwtSettings();
builder.Configuration.GetSection("JwtSettings").Bind(jwtSettings);
diff --git a/server/MiAssessment/src/MiAssessment.Api/appsettings.json b/server/MiAssessment/src/MiAssessment.Api/appsettings.json
index e36d8ba..5828c66 100644
--- a/server/MiAssessment/src/MiAssessment.Api/appsettings.json
+++ b/server/MiAssessment/src/MiAssessment.Api/appsettings.json
@@ -1,6 +1,7 @@
{
"ConnectionStrings": {
"DefaultConnection": "Server=192.168.195.15,1433;uid=sa;pwd=Dbt@com@123;Database=MiAssessment_Business;MultipleActiveResultSets=true;pooling=true;min pool size=5;max pool size=32767;connect timeout=20;Encrypt=True;TrustServerCertificate=True;",
+ "AdminConnection": "Server=192.168.195.15,1433;uid=sa;pwd=Dbt@com@123;Database=MiAssessment_Admin;MultipleActiveResultSets=true;pooling=true;min pool size=5;max pool size=32767;connect timeout=20;Encrypt=True;TrustServerCertificate=True;",
"Redis": "192.168.195.15:6379,defaultDatabase=2,abortConnect=false,connectTimeout=5000"
},
"AppSettings": {
diff --git a/server/MiAssessment/src/MiAssessment.Core/Services/ConfigService.cs b/server/MiAssessment/src/MiAssessment.Core/Services/ConfigService.cs
index 8a2b294..ff36e40 100644
--- a/server/MiAssessment/src/MiAssessment.Core/Services/ConfigService.cs
+++ b/server/MiAssessment/src/MiAssessment.Core/Services/ConfigService.cs
@@ -9,22 +9,38 @@ namespace MiAssessment.Core.Services;
///
/// 配置服务实现
+/// 运营配置(app_setting、base等)从 Admin 库读取
+/// 业务配置从业务库读取
///
public class ConfigService : IConfigService
{
private readonly MiAssessmentDbContext _dbContext;
+ private readonly AdminConfigReadDbContext _adminConfigDbContext;
private readonly ILogger _logger;
private readonly IRedisService _redisService;
// 当前版本号
private const string CurrentVersion = "116";
+ ///
+ /// 需要从 Admin 库读取的运营配置键
+ ///
+ private static readonly HashSet AdminConfigKeys = new()
+ {
+ "app_setting", "base", "weixinpay_setting", "miniprogram_setting",
+ "weixinpay", "alipay_setting", "h5_setting", "uploads",
+ "systemconfig", "sign", "user_config", "infinite_multiple",
+ "rank_setting", "system_test", "tencent_sms_config"
+ };
+
public ConfigService(
MiAssessmentDbContext dbContext,
+ AdminConfigReadDbContext adminConfigDbContext,
ILogger logger,
IRedisService redisService)
{
_dbContext = dbContext;
+ _adminConfigDbContext = adminConfigDbContext;
_logger = logger;
_redisService = redisService;
}
@@ -116,11 +132,24 @@ public class ConfigService : IConfigService
_logger.LogWarning(ex, "Failed to get config from cache: {Key}", key);
}
- // 从数据库获取
- var config = await _dbContext.Configs
- .Where(c => c.ConfigKey == key)
- .Select(c => c.ConfigValue)
- .FirstOrDefaultAsync();
+ // 根据配置键决定从哪个库读取
+ string? config;
+ if (AdminConfigKeys.Contains(key))
+ {
+ // 运营配置从 Admin 库读取
+ config = await _adminConfigDbContext.AdminConfigs
+ .Where(c => c.ConfigKey == key)
+ .Select(c => c.ConfigValue)
+ .FirstOrDefaultAsync();
+ }
+ else
+ {
+ // 业务配置从业务库读取
+ config = await _dbContext.Configs
+ .Where(c => c.ConfigKey == key)
+ .Select(c => c.ConfigValue)
+ .FirstOrDefaultAsync();
+ }
// 存入缓存(10分钟过期)
if (!string.IsNullOrEmpty(config))
diff --git a/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayConfigService.cs b/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayConfigService.cs
index dfc6cb9..695f32e 100644
--- a/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayConfigService.cs
+++ b/server/MiAssessment/src/MiAssessment.Core/Services/WechatPayConfigService.cs
@@ -14,7 +14,7 @@ namespace MiAssessment.Core.Services;
///
public class WechatPayConfigService : IWechatPayConfigService
{
- private readonly MiAssessmentDbContext _dbContext;
+ private readonly AdminConfigReadDbContext _adminConfigDbContext;
private readonly IRedisService _redisService;
private readonly ILogger _logger;
private readonly Random _random = new();
@@ -24,11 +24,11 @@ public class WechatPayConfigService : IWechatPayConfigService
private static readonly TimeSpan CACHE_DURATION = TimeSpan.FromMinutes(5);
public WechatPayConfigService(
- MiAssessmentDbContext dbContext,
+ AdminConfigReadDbContext adminConfigDbContext,
IRedisService redisService,
ILogger logger)
{
- _dbContext = dbContext;
+ _adminConfigDbContext = adminConfigDbContext;
_redisService = redisService;
_logger = logger;
}
@@ -49,7 +49,7 @@ public class WechatPayConfigService : IWechatPayConfigService
var merchants = new List();
try
{
- var settingConfig = await _dbContext.Configs
+ var settingConfig = await _adminConfigDbContext.AdminConfigs
.Where(c => c.ConfigKey == "weixinpay_setting")
.Select(c => c.ConfigValue)
.FirstOrDefaultAsync();
@@ -79,7 +79,7 @@ public class WechatPayConfigService : IWechatPayConfigService
}
}
- var weixinpayConfig = await _dbContext.Configs
+ var weixinpayConfig = await _adminConfigDbContext.AdminConfigs
.Where(c => c.ConfigKey == "weixinpay")
.Select(c => c.ConfigValue)
.FirstOrDefaultAsync();
@@ -132,7 +132,7 @@ public class WechatPayConfigService : IWechatPayConfigService
var miniprograms = new List();
try
{
- var settingConfig = await _dbContext.Configs
+ var settingConfig = await _adminConfigDbContext.AdminConfigs
.Where(c => c.ConfigKey == "miniprogram_setting")
.Select(c => c.ConfigValue)
.FirstOrDefaultAsync();
diff --git a/server/MiAssessment/src/MiAssessment.Core/Services/WechatService.cs b/server/MiAssessment/src/MiAssessment.Core/Services/WechatService.cs
index aac6fb6..7cee6e6 100644
--- a/server/MiAssessment/src/MiAssessment.Core/Services/WechatService.cs
+++ b/server/MiAssessment/src/MiAssessment.Core/Services/WechatService.cs
@@ -20,6 +20,7 @@ public class WechatService : IWechatService
private readonly WechatPaySettings _wechatPaySettings;
private readonly IRedisService _redisService;
private readonly MiAssessmentDbContext _dbContext;
+ private readonly AdminConfigReadDbContext _adminConfigDbContext;
// 微信API端点
private const string WechatCodeToSessionUrl = "https://api.weixin.qq.com/sns/jscode2session";
@@ -34,13 +35,15 @@ public class WechatService : IWechatService
ILogger logger,
IOptions wechatPaySettings,
IRedisService redisService,
- MiAssessmentDbContext dbContext)
+ MiAssessmentDbContext dbContext,
+ AdminConfigReadDbContext adminConfigDbContext)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_wechatPaySettings = wechatPaySettings?.Value ?? throw new ArgumentNullException(nameof(wechatPaySettings));
_redisService = redisService ?? throw new ArgumentNullException(nameof(redisService));
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
+ _adminConfigDbContext = adminConfigDbContext ?? throw new ArgumentNullException(nameof(adminConfigDbContext));
}
///
@@ -620,8 +623,8 @@ public class WechatService : IWechatService
{
try
{
- // 从 miniprogram_setting 读取小程序配置
- var miniprogramConfig = await _dbContext.Configs
+ // 从 miniprogram_setting 读取小程序配置(运营配置,从Admin库读取)
+ var miniprogramConfig = await _adminConfigDbContext.AdminConfigs
.Where(c => c.ConfigKey == "miniprogram_setting")
.Select(c => c.ConfigValue)
.FirstOrDefaultAsync();
@@ -700,8 +703,8 @@ public class WechatService : IWechatService
{
try
{
- // 从数据库读取 weixinpay 配置
- var weixinpayConfig = await _dbContext.Configs
+ // 从数据库读取 weixinpay 配置(运营配置,从Admin库读取)
+ var weixinpayConfig = await _adminConfigDbContext.AdminConfigs
.Where(c => c.ConfigKey == "weixinpay")
.Select(c => c.ConfigValue)
.FirstOrDefaultAsync();
diff --git a/server/MiAssessment/src/MiAssessment.Infrastructure/Modules/ServiceModule.cs b/server/MiAssessment/src/MiAssessment.Infrastructure/Modules/ServiceModule.cs
index b55cbd5..b4a9653 100644
--- a/server/MiAssessment/src/MiAssessment.Infrastructure/Modules/ServiceModule.cs
+++ b/server/MiAssessment/src/MiAssessment.Infrastructure/Modules/ServiceModule.cs
@@ -31,7 +31,8 @@ public class ServiceModule : Module
var wechatPaySettings = c.Resolve>();
var redisService = c.Resolve();
var dbContext = c.Resolve();
- return new WechatService(httpClientFactory.CreateClient(), logger, wechatPaySettings, redisService, dbContext);
+ var adminConfigDbContext = c.Resolve();
+ return new WechatService(httpClientFactory.CreateClient(), logger, wechatPaySettings, redisService, dbContext, adminConfigDbContext);
}).As().InstancePerLifetimeScope();
// 注册 IP 地理位置服务
@@ -69,13 +70,13 @@ public class ServiceModule : Module
// ========== 支付系统服务注册 ==========
- // 注册微信支付配置服务(从数据库读取配置)
+ // 注册微信支付配置服务(从Admin库读取配置)
builder.Register(c =>
{
- var dbContext = c.Resolve();
+ var adminConfigDbContext = c.Resolve();
var redisService = c.Resolve();
var logger = c.Resolve>();
- return new WechatPayConfigService(dbContext, redisService, logger);
+ return new WechatPayConfigService(adminConfigDbContext, redisService, logger);
}).As().InstancePerLifetimeScope();
// 注册微信支付 V3 服务
@@ -156,9 +157,10 @@ public class ServiceModule : Module
builder.Register(c =>
{
var dbContext = c.Resolve();
+ var adminConfigDbContext = c.Resolve();
var logger = c.Resolve>();
var redisService = c.Resolve();
- return new ConfigService(dbContext, logger, redisService);
+ return new ConfigService(dbContext, adminConfigDbContext, logger, redisService);
}).As().InstancePerLifetimeScope();
// ========== 小程序首页模块服务注册 ==========
diff --git a/server/MiAssessment/src/MiAssessment.Model/Data/AdminConfigReadDbContext.cs b/server/MiAssessment/src/MiAssessment.Model/Data/AdminConfigReadDbContext.cs
new file mode 100644
index 0000000..23fa9d4
--- /dev/null
+++ b/server/MiAssessment/src/MiAssessment.Model/Data/AdminConfigReadDbContext.cs
@@ -0,0 +1,39 @@
+using Microsoft.EntityFrameworkCore;
+using MiAssessment.Model.Entities;
+
+namespace MiAssessment.Model.Data;
+
+///
+/// Admin 配置只读数据库上下文
+/// 连接 Admin 库,用于读取 admin_configs 表中的运营配置
+/// 如:支付配置、小程序配置、上传配置等
+/// 注意:此上下文仅用于只读操作,写操作通过 Admin 项目进行
+///
+public class AdminConfigReadDbContext : DbContext
+{
+ public AdminConfigReadDbContext(DbContextOptions options) : base(options)
+ {
+ }
+
+ ///
+ /// 后台配置表
+ ///
+ public DbSet AdminConfigs { get; set; } = null!;
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
+
+ modelBuilder.Entity(entity =>
+ {
+ // ConfigKey 唯一索引
+ entity.HasIndex(e => e.ConfigKey)
+ .IsUnique()
+ .HasDatabaseName("IX_admin_configs_config_key");
+
+ // ConfigValue 使用 nvarchar(max)
+ entity.Property(e => e.ConfigValue)
+ .HasColumnType("nvarchar(max)");
+ });
+ }
+}
diff --git a/server/MiAssessment/src/MiAssessment.Model/Entities/AdminConfig.cs b/server/MiAssessment/src/MiAssessment.Model/Entities/AdminConfig.cs
new file mode 100644
index 0000000..83dee10
--- /dev/null
+++ b/server/MiAssessment/src/MiAssessment.Model/Entities/AdminConfig.cs
@@ -0,0 +1,52 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace MiAssessment.Model.Entities;
+
+///
+/// 后台配置表(Admin库),存储运营级配置信息
+/// 如:支付配置、小程序配置、上传配置等
+///
+[Table("admin_configs")]
+public class AdminConfig
+{
+ ///
+ /// 主键ID
+ ///
+ [Key]
+ public int Id { get; set; }
+
+ ///
+ /// 配置键名
+ ///
+ [Required]
+ [MaxLength(100)]
+ [Column("config_key")]
+ public string ConfigKey { get; set; } = null!;
+
+ ///
+ /// 配置值(JSON格式或普通文本)
+ ///
+ [Column("config_value", TypeName = "nvarchar(max)")]
+ public string? ConfigValue { get; set; }
+
+ ///
+ /// 配置描述
+ ///
+ [MaxLength(200)]
+ [Column("description")]
+ public string? Description { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ [Column("created_at")]
+ public DateTime CreatedAt { get; set; } = DateTime.Now;
+
+ ///
+ /// 更新时间
+ ///
+ [Column("updated_at")]
+ public DateTime? UpdatedAt { get; set; }
+}
diff --git a/server/MiAssessment/tests/MiAssessment.Tests/Services/AdminConfigServicePropertyTests.cs b/server/MiAssessment/tests/MiAssessment.Tests/Services/AdminConfigServicePropertyTests.cs
index cbbc8cb..d2344cb 100644
--- a/server/MiAssessment/tests/MiAssessment.Tests/Services/AdminConfigServicePropertyTests.cs
+++ b/server/MiAssessment/tests/MiAssessment.Tests/Services/AdminConfigServicePropertyTests.cs
@@ -46,11 +46,11 @@ public class AdminConfigServicePropertyTests
};
// Create a fresh service instance for each test
- var options = new DbContextOptionsBuilder()
+ var options = new DbContextOptionsBuilder()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
- using var dbContext = new AdminBusinessDbContext(options);
+ using var dbContext = new AdminConfigDbContext(options);
var mockRedis = new Mock();
mockRedis.Setup(x => x.GetStringAsync(It.IsAny())).ReturnsAsync((string?)null);
@@ -313,11 +313,11 @@ public class AdminConfigServicePropertyTests
private AdminConfigService CreateService()
{
- var options = new DbContextOptionsBuilder()
+ var options = new DbContextOptionsBuilder()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
- var dbContext = new AdminBusinessDbContext(options);
+ var dbContext = new AdminConfigDbContext(options);
var mockRedis = new Mock();
mockRedis.Setup(x => x.GetStringAsync(It.IsAny())).ReturnsAsync((string?)null);
diff --git a/server/MiAssessment/tests/MiAssessment.Tests/Services/AdminConfigServiceTests.cs b/server/MiAssessment/tests/MiAssessment.Tests/Services/AdminConfigServiceTests.cs
index 0ba10e8..c4a37e6 100644
--- a/server/MiAssessment/tests/MiAssessment.Tests/Services/AdminConfigServiceTests.cs
+++ b/server/MiAssessment/tests/MiAssessment.Tests/Services/AdminConfigServiceTests.cs
@@ -17,7 +17,7 @@ namespace MiAssessment.Tests.Services;
///
public class AdminConfigServiceTests : IDisposable
{
- private readonly AdminBusinessDbContext _adminDbContext;
+ private readonly AdminConfigDbContext _adminDbContext;
private readonly Mock _mockRedisService;
private readonly Mock> _mockLogger;
private readonly AdminConfigService _configService;
@@ -25,11 +25,11 @@ public class AdminConfigServiceTests : IDisposable
public AdminConfigServiceTests()
{
// 使用 InMemory 数据库
- var options = new DbContextOptionsBuilder()
+ var options = new DbContextOptionsBuilder()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
- _adminDbContext = new AdminBusinessDbContext(options);
+ _adminDbContext = new AdminConfigDbContext(options);
_mockRedisService = new Mock();
_mockLogger = new Mock>();