12. 系统参数配置
后端手册配置缓存大约 6 分钟约 1709 字
系统参数存在 SysConfig 表(主库、[SysTable]),SysConfigService 提供带缓存的读写;业务代码一律用 GetConfigValueByCode<T>(code) 取值,不要自己查表。
1. 表结构与缓存
SysConfig(Admin.NET.Core/Entity/SysConfig.cs)
├─ Id 雪花 Id
├─ Name 名称 [SugarIndex i_{table}_n]
├─ Code 编码 [SugarIndex i_{table}_c, IsUnique = true] ★ 取值靠它
├─ Value 参数值(string,最大 512)
├─ SysFlag 是否内置(YesNoEnum,Y = 禁止删除)
├─ GroupCode 分组编码(默认 "Default" = ConfigConst.SysDefaultGroup)
├─ OrderNo 排序
├─ Remark 备注
└─ 审计字段(EntityBase)
缓存 Key:CacheConst.KeyConfig = "sys_config:"
├─ sys_config:{code} 参数值(GetConfigValueByCode 用)
├─ sys_config:Value:{code} (失效时清理)
├─ sys_config:Remark:{code} (失效时清理)
└─ sys_config:{groupCode}:GroupWithCache (失效时清理)2. SysConfigService 方法全表
| 方法 | 特性 | 路由 | 说明 |
|---|---|---|---|
Page(PageConfigInput) | [DisplayName] | POST /api/sysconfig/page | 分页,按 Name/Code 模糊、GroupCode 精确 |
List(PageConfigInput) | [DisplayName] | POST /api/sysconfig/list | 不分页列表(按分组) |
AddConfig(AddConfigInput) | Add + HttpPost | POST /api/sysconfig/add | 重名/重 Code 报 D9000 |
UpdateConfig(UpdateConfigInput) | Update + HttpPost + [UnitOfWork] | POST /api/sysconfig/update | 改完自动清缓存 |
DeleteConfig(DeleteConfigInput) | Delete + HttpPost | POST /api/sysconfig/delete | SysFlag=Y 禁止删除(D9001) |
BatchDeleteConfig(List<long>) | BatchDelete + HttpPost | POST /api/sysconfig/batchDelete | 跳过内置参数 |
GetDetail([FromQuery] ConfigInput) | [DisplayName] | POST /api/sysconfig/getDetail | 详情 |
GetConfig(string code) | [NonAction] | — | 内部用,取整条记录 |
GetConfigValueByCode(string code) | [DisplayName] | POST /api/sysconfig/getConfigValueByCode | 取字符串值 |
GetConfigValueByCode<T>(string code) | [NonAction] | — | ★ 泛型取值,业务代码用这个 |
UpdateConfigValue(code, value) | [NonAction] | — | 内部用,改值并清缓存 |
GetGroupList() | [DisplayName] | POST /api/sysconfig/getGroupList | 所有分组编码 |
GetTokenExpire() | [NonAction] | — | Token 过期分钟数(默认 20) |
GetRefreshTokenExpire() | [NonAction] | — | RefreshToken 过期分钟数(默认 40) |
BatchUpdateConfig(List<BatchConfigInput>) | BatchUpdate + HttpPost | POST /api/sysconfig/batchUpdate | 按 Code 批量改 Value |
GetSmPublicKey() | [DisplayName] + [AllowAnonymous] | POST /api/sysconfig/getSmPublicKey | 密码加密公钥(登录页用) |
3. 后台怎么读
public class LeaveService : IDynamicApiController, ITransient
{
private readonly SysConfigService _sysConfigService;
public LeaveService(SysConfigService sysConfigService) => _sysConfigService = sysConfigService;
// 字符串
var pwd = await _sysConfigService.GetConfigValueByCode<string>(ConfigConst.SysPassword);
// 强类型(内部 Convert.ChangeType)
var days = await _sysConfigService.GetConfigValueByCode<int>(ConfigConst.SysLogRetentionDays);
var enabled = await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysOpLog);
// 带默认值的写法(框架自己的做法,见 GetTokenExpire)
var tokenExpire = await _sysConfigService.GetConfigValueByCode<string>(ConfigConst.SysTokenExpire);
_ = int.TryParse(tokenExpire, out var expire);
expire = expire == 0 ? 20 : expire;
}GetConfigValueByCode<T> 的真实实现(带缓存、防缓存穿透):
[NonAction]
public async Task<T> GetConfigValueByCode<T>(string code)
{
if (string.IsNullOrWhiteSpace(code)) return default;
var value = _sysCacheService.Get<string>($"{CacheConst.KeyConfig}{code}");
if (string.IsNullOrEmpty(value))
{
value = (await _sysConfigRep.CopyNew().GetFirstAsync(u => u.Code == code))?.Value;
_sysCacheService.Set($"{CacheConst.KeyConfig}{code}", value);
}
if (string.IsNullOrWhiteSpace(value)) return default;
return (T)Convert.ChangeType(value, typeof(T));
}
CopyNew()很关键:绕过当前请求的租户/机构过滤器,保证任何上下文都能取到配置。
4. 后台怎么写
// 方式一:走服务(推荐,自动清缓存)
await _sysConfigService.UpdateConfigValue(ConfigConst.SysLogRetentionDays, "365");
// 方式二:直接改表(必须手动清缓存,否则读的是旧值)
var config = await _sysConfigRep.GetFirstAsync(u => u.Code == code);
config.Value = "365";
await _sysConfigRep.AsUpdateable(config).ExecuteCommandAsync();
_sysCacheService.Remove($"{CacheConst.KeyConfig}{code}");清缓存的四个 Key(SysConfigService.RemoveConfigCache):
private void RemoveConfigCache(SysConfig config)
{
_sysCacheService.Remove($"{CacheConst.KeyConfig}Value:{config.Code}");
_sysCacheService.Remove($"{CacheConst.KeyConfig}Remark:{config.Code}");
_sysCacheService.Remove($"{CacheConst.KeyConfig}{config.GroupCode}:GroupWithCache");
_sysCacheService.Remove($"{CacheConst.KeyConfig}{config.Code}");
}5. ConfigConst 关键常量
// Admin.NET.Core/Const/ConfigConst.cs
public const string SysVersion = "sys_version"; // 系统版本
public const string SysDemoEnv = "sys_demo"; // 演示环境
public const string SysPassword = "sys_password"; // 默认密码
public const string SysPasswordMaxErrorTimes = "sys_password_max_error_times"; // 密码最大错误次数
public const string SysLogRetentionDays = "sys_log_retention_days"; // 日志保留天数
public const string SysOpLog = "sys_oplog"; // 记录操作日志
public const string SysSingleLogin = "sys_single_login"; // 单设备登录
public const string SysTokenExpire = "sys_token_expire"; // Token 过期(分钟)
public const string SysRefreshTokenExpire = "sys_refresh_token_expire"; // RefreshToken 过期
public const string SysErrorMail = "sys_error_mail"; // 异常日志邮件
public const string SysErrorWeCom = "sys_error_weCom"; // 异常日志企微
public const string SysDomainLogin = "sys_domain_login"; // 域登录验证
public const string SysForceChangePassword = "sys_force_change_password"; // 强制改密码
public const string SysSM2Key = "sys_sm2_key"; // 国密 SM2 密匙
public const string SysPasswordStrength = "sys_password_strength"; // 密码强度校验
public const string SysPasswordStrengthExpression = "sys_password_strength_expression";
public const string SysPasswordExpirationTime = "sys_password_expiration_time"; // 密码有效期
public const string SysPasswordRecord = "sys_password_record"; // 历史密码校验
public const string SysUpgrade = "sys_upgrade"; // 显示更新日志
public const string SysI18NSwitch = "sys_i18n_switch"; // 多语言切换
public const string SysIdleTimeout = "sys_idle_timeout"; // 闲置超时(秒)
public const string SysOnlineNotice = "sys_online_notice"; // 上线通知
public const string SysLogHttp = "sys_http_log"; // 请求日志
public const string SysMobileLogin = "sys_mobile_login"; // 手机号登录
public const string SysScanLogin = "sys_scan_login"; // 扫一扫登录
public const string SysSignUp = "sys_sign_up"; // 用户注册
public const string SysMobileTokenVersion = "sys_mobile_token_version"; // 移动端 Token 版本
public const string SysTablePageSequence = "sys_table_page_sequence"; // 表格分页序号
public const string SysDefaultGroup = "Default"; // 默认分组
public const string SysWebLogo = "sys_web_logo"; // 系统图标
public const string SysWebTitle = "sys_web_title"; // 主标题
public const string SysWebViceTitle = "sys_web_viceTitle"; // 副标题
public const string SysWebViceDesc = "sys_web_viceDesc"; // 描述
public const string SysWebWatermark = "sys_web_watermark"; // 水印
public const string SysWebCopyright = "sys_web_copyright"; // 版权
public const string SysWebIcp = "sys_web_icp"; // ICP 备案号
public const string SysWebIcpUrl = "sys_web_icpUrl"; // ICP 地址6. 新增一个自定义参数
// Admin.NET.Application/SeedData/SysConfigSeedData.cs(应用层种子,最后执行)
[IncreSeed] // 生产环境配合 EnableIncreSeed 增量生效
public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
{
public IEnumerable<SysConfig> HasData()
{
return
[
new SysConfig
{
Id = SqlSugarConst.DefaultTenantId + 9001, // Id 必须显式赋值,避免重复插入
Name = "请假天数上限",
Code = "biz_leave_max_days",
Value = "30",
SysFlag = YesNoEnum.N, // N = 允许后台删除;Y = 内置禁止删除
Remark = "单次请假最多天数",
OrderNo = 900,
GroupCode = ConfigConst.SysDefaultGroup,
CreateTime = DateTime.Parse("2026-01-01 00:00:00")
},
];
}
}配套常量(写在 Admin.NET.Application/Const/BusinessModuleConst.cs):
namespace Admin.NET.Application;
public class BusinessModuleConst
{
public const string BizLeaveMaxDays = "biz_leave_max_days";
}使用:
var maxDays = await _sysConfigService.GetConfigValueByCode<int>(BusinessModuleConst.BizLeaveMaxDays);
if (input.Days > maxDays) throw Oops.Oh($"单次请假最多 {maxDays} 天");7. 前端如何消费
管理端:系统管理 → 参数配置 分页 /api/sysconfig/page、改值 /api/sysconfig/update
业务端:登录后拉取配置
├─ 单条:POST /api/sysconfig/getConfigValueByCode?code=sys_web_title
├─ 分组:POST /api/sysconfig/list { groupCode: "Default" }
└─ 批量改:POST /api/sysconfig/batchUpdate [{ code, value }]
登录页:POST /api/sysconfig/getSmPublicKey([AllowAnonymous],取密码加密公钥)前端约定:
- 站点类配置(
sys_web_logo/sys_web_title/sys_web_watermark/sys_web_icp…)建议登录后一次性拉list存进全局 store,不要每个组件各请求一次。 - 开关类配置值统一是字符串
"True"/"False",前端要自己转布尔。 - 修改配置后后台已自动清缓存,前端刷新即可拿到新值。
8. 步骤清单
- 在
Const类里加常量(不要到处硬编码字符串) - 写
SeedData/SysConfigSeedData.cs灌初始值(显式Id、GroupCode、SysFlag) - 后台读用
_sysConfigService.GetConfigValueByCode<T>(常量) - 后台写用
UpdateConfigValue或BatchUpdateConfig(自动清缓存) - 直接改表时必须手动
Remove($"{CacheConst.KeyConfig}{code}") - 前端按分组拉取,开关类值做布尔转换
⚠️ 注意事项 / 常见坑
Code是唯一索引,重名会触发D9000;新增前先确认不冲突(建议加biz_前缀)。SysFlag = YesNoEnum.Y的参数后台删不掉(D9001),业务参数用N。- 值是
string,GetConfigValueByCode<int>走Convert.ChangeType,值不是数字会抛异常——先TryParse。 - 改配置后必须清缓存,否则要等缓存自然过期。走
UpdateConfig/DeleteConfig/BatchUpdateConfig/UpdateConfigValue会自动清。 GetSmPublicKey是[AllowAnonymous],别把敏感密钥塞进去。- 修改
SysSM2Key会导致已有密码无法解密(源码注释明确提示"修改密匙要重新生成数据库/账号表")。 - 种子数据里
Id不显式赋值,每次启动Storageable会按主键比对——但不同环境建议固定 Id 保证一致。 - 配置是全局共享的(
SysConfig : EntityBase,不带租户隔离),租户级配置请自己加TenantId字段另建表。
小结
配置 = SysConfig 表 + ConfigConst 常量 + SysConfigService.GetConfigValueByCode<T>()(带 sys_config:{code} 缓存)。读走泛型方法,写走服务方法(自动清缓存),新增走种子数据 + [IncreSeed],前端按分组一次拉全。