系统设置
「系统设置」这一组页面管的是运行期可调、不需要改代码重新部署的东西:菜单、字典、参数、业务编号、缓存、服务监控、版本。本页按页面逐项说明数据模型与使用要点。
对应菜单目录 /setting(部分页面在 /file 目录下)。
菜单管理(/setting/menu)
页面能看到并微调 SysMenu 表,但它不是事实源。
菜单的事实源在后端代码里
菜单、路由、组件路径、权限码、国际化键统一由 Application/Pages/PageRegistry.cs 登记,SaasMenuSeeder 据此播种到 SysMenu。
新增/修改菜单要改 PageRegistry 并重新播种,在页面上手改会在下次播种时被覆盖或产生漂移。菜单管理页的定位是查看结构、调整排序与显隐,不是维护入口。
关键机制见 后端架构 · 菜单。
数据字典(/setting/dict)
两级结构:
| 实体 | 关键字段 |
|---|---|
SysDict | DictCode(编码)、DictName、DictType、IsBuiltIn(内置不可删)、Status、Sort |
SysDictItem | DictId、ParentId(支持树形字典项)、ItemCode、ItemName、ItemValue、Metadata、IsDefault、Status、Sort |
用途是给表单、搜索下拉提供可运营维护的选项集(如客户等级、合同类型)。
字典 vs 枚举:怎么选
- 枚举(C#
enum):值集由代码决定,改动要发版。标签的事实源是后端枚举元数据(Enums.{culture}.json),天然多语言。适合状态机、类型判别这类参与业务逻辑的值。 - 字典(
SysDict):值集由运营维护,随时增删。适合业务分类、标签这类纯数据的值。
前端 Schema 字段用 dictionaryCode 声明,两者的取值路径是统一的——但别把参与 switch 判断的值放进字典,运营删一条就会打穿代码分支。
字典项树走 Redis 缓存(basicapp:saas:configuration:dict-tree),写侧改完必须调 InvalidateDictionaryAsync()。
参数配置(/setting/config)
SysConfig 是运行期系统参数,与 appsettings.json 是两套东西:
| 字段 | 说明 |
|---|---|
ConfigKey / ConfigValue / DefaultValue | 键、当前值、默认值 |
ConfigGroup | 分组,用于页面归类 |
ConfigType | 配置类型(如 Feature 功能开关) |
DataType | 值的数据类型(String / 数值 / 布尔 等),前端据此渲染控件 |
IsBuiltIn | 内置参数不可删 |
IsEncrypted | 加密存储——密钥类参数落库前加密,读侧不回显明文 |
Status / Sort / Remark | 常规 |
什么该放 appsettings、什么该放 SysConfig
appsettings:基础设施连接与启动期就要用的东西(数据库连接串、Redis、JWT 密钥、监听端口)。改了要重启。SysConfig:业务开关与运营参数(是否开放注册、单页默认条数、某功能灰度开关)。改了立即生效(走缓存失效),且可按租户覆盖。
判断标准:这个值需要在运行期由管理员改吗?需要 → SysConfig。
配置值走缓存(basicapp:saas:config:value),写侧调 InvalidateConfigurationAsync(configKey?)。
内置参数
同一功能的设置合成一条 JSON 参数,结构只在对应的设置类型里定义一处;键为小写点分的 模块.领域.名称。参数缺失时按设置类型的默认值运行,值写错(JSON 解析失败、字段类型不对)直接报错,不静默回退。
| 键 | 类型 | 内容 |
|---|---|---|
saas.auth.login | JSON | methods 开放的登录方式:password、phone、email、qrcode,缺省只开 password,登录页只列开放的方式,没开放 phone 时手机号登录接口也拒绝;oauthProviders 展示的第三方登录(name 须与已注册的认证方案一致) |
saas.auth.password | JSON | forceChange 强制改密,默认关闭:开启后,密码由他人设置(管理员创建或重置、平台开通、种子写入)的账号登录后先锁定到改密为止 |
saas.auth.impersonation | JSON | sessionMinutes 模仿会话分钟数(按 1~480 归一);notifyTarget 是否通知被模仿者 |
saas.bot.telegram | JSON | Telegram 机器人平台设置:总开关、Webhook 地址与路由前缀、刷新与缓存周期、兜底回复、网络 |
saas.bot.telegram.webhook-secret-token | 加密文本 | Webhook 模式必填的密钥令牌,单列是为了加密存储 |
saas.log.retention-days | 数字 | 8 类分表日志(含条件命中记录)的保留天数,缺省 180 |
saas.security.login-auto-ban | JSON | 登录失败自动封禁:isEnabled 开关(缺省关);同一 IP 在 windowMinutes 分钟内登录失败(跨账号累计)达到 maxFailures 次即封禁 banMinutes 分钟,24 小时内再犯时长翻倍、最长 maxBanMinutes 分钟;访问策略里放行的 IP 不计数;只认平台这一条,租户不能建同键覆盖。见部署 · 访问策略与真实 IP |
chat.policy | JSON | retentionDays 聊天消息保留天数(缺省 365);sensitiveWords 敏感词数组 |
种子只补缺的参数、对齐名称与说明等元数据,配置值只在为空时写入初始值,运营改过的值与启停状态不动。
业务编号(/setting/numbering)
给业务单据发号(订单号、合同号、工单号),保证同一周期内单调递增且不重复。
SysNumberingRule 的构成:
| 字段 | 说明 | 默认 |
|---|---|---|
RuleCode / RuleName | 规则编码与名称 | — |
Prefix | 前缀,如 SO | — |
Separator | 分隔符 | - |
DateFormat | 日期段格式 | yyyyMMdd |
SerialLength | 序号位数(左补零) | 4 |
ResetCycle | 重置周期:按日/月/年/不重置 | Daily |
TimeZoneId | 按哪个时区判定周期边界 | UTC |
AllowTenantUse | 是否允许租户使用该规则 | false |
CurrentValue / CurrentPeriod / CurrentPeriodOrdinal | 当前值与周期游标(运行时状态) | — |
HasAllocated | 是否已发过号 | — |
生成结果形如 SO-20260804-0001。
三个容易出事的点
TimeZoneId决定周期边界。设成UTC而业务在东八区,那么每天 08:00 才换号段——上线前对齐清楚。ResetCycle不能往回退(如从「按日」改成「按年」),会导致已发出的号段与新周期语义冲突,服务层会拒绝。- 改规则会影响历史号的可读性,但不会回溯改已发的号。
SysNumberingAllocation记录每次分配,可用于追溯。
发号在事务内以「单调翻转 → 递增 → 读回」完成,不加进程内锁;并发下靠数据库保证。
缓存管理(/setting/cache)
查看与清理 Redis 缓存。日常用途有两个:
- 排障:怀疑「改完不生效」时,先看缓存里是不是旧值。
- 应急:确认是失效逻辑漏了,可以手工清一次先恢复业务,再回头修代码。
手工清缓存是应急手段
手工清完问题消失,说明写侧漏调了失效方法——这是需要修的 bug,不是一次性故障。正确做法见 缓存与异步。
缓存键的命名规则、条目清单与失效器方法同样在那一页。
服务监控(/setting/server)
ServerAppService 提供运行时信息可视化:CPU / 内存 / 磁盘 / GPU / 网络 / 主板 / .NET 运行时。
它读的是当前进程所在机器的信息。多实例部署时看到的是「你这次请求恰好打到的那个实例」,不是集群汇总——要做集群视图请接 OpenTelemetry 指标导出。配置与真实健康探测见健康检查与可观测性。
版本管理(/setting/version)
SysVersion 记录应用版本与数据库版本,配合框架的升级引擎:
| 字段 | 说明 |
|---|---|
AppVersion / DbVersion | 应用版本 / 数据库版本 |
MinSupportVersion | 允许升级的最低来源版本 |
IsUpgrading / UpgradeNode / UpgradeStartTime | 升级中标记、执行节点、开始时间(多节点协调用) |
升级能力由配置节 XiHan:Upgrade 控制(分布式锁、主节点、维护模式等),见升级与迁移。
版本页只读,脚本才是迁移入口
SysVersion 由升级引擎独占写入,管理端不提供手工改版本。结构和数据迁移放在 WebHost/UpdateScripts/<版本>/<版本>.sql;DbInitializer 只负责全新数据库建表,不会给已有表补列。EnableAutoCheckOnStartup=true(缺省值)时启动会自动执行待执行脚本、失败即中断启动,详见当前结论。
通道配置(邮件 / 短信 / 机器人)
这几个页面在同一菜单目录下,配置落库而非写 appsettings,因此可以按租户隔离、运行期热切换:
| 页面 | 实体 | 说明 |
|---|---|---|
邮件配置 /setting/email-config | SysEmailConfig | SMTP 网关 |
短信配置 /setting/sms-config | SysSmsConfig | 短信网关 |
机器人配置 /setting/bot-config | SysBotConfig | 钉钉 / 飞书 / 企业微信 Webhook 机器人 |
Telegram 机器人 /setting/telegram-bot | SysTelegramBot | 多实例 Telegram Bot |
它们通过 services.Replace(...) 覆盖框架各 Bot 包的默认 *ConfigStore——框架侧读的是接口,实现被换成了「从数据库读」。密钥类字段加密存储。
发送链路与模板见 消息中心。
