核心概念
Program.cs 里调用 AddSmartAdmin 和 MapSmartAdmin,换来一整套企业级后台:认证、RBAC、多组织数据权限、字典与配置、日志、上传、通知公告、只读演示模式、密码过期策略。后端以 NuGet 包、前端以 npm 包 smart-admin-web 交付,是内核不是应用,业务代码住在你自己的仓库里。这个形态逼出一条约束:里面任何一环,你都得能换掉。
为什么不是又一个后台模板
复制一套后台模板,上手很快。但业务代码一多,项目就和模板焊在一起了:以后想换掉其中一块,比如把密码哈希从 PBKDF2 换成 argon2,得先把模板原来的写法从自己的代码里一处处摘出来。
SmartAdmin 把这些通用功能从业务里拆了出来。你可以直接用默认实现,也可以把它接进已有项目,还可以不 fork 就替换掉任意一环。
可替换性模型
它落成三条约束,由可替换性契约(ReplaceabilityTests)锁定:
- 接口注册 +
TryAdd:内置服务都用TryAdd*注册。消费方只要在AddSmartAdmin()之前注册同一个接口,就能胜出,覆盖掉默认实现。 - 模板方法拆分:长方法被拆成若干
virtual小步骤。消费方继承之后只重写其中一步,不用整段复制。 - 业务程序集挂载:消费方的实体经
options.ApplicationAssemblies加入 CodeFirst 建表,控制器也自动AddApplicationPart。不改内核就能扩展。
这三条落到代码里具体怎么写,见替换内置服务。
前端换一环,机制和后端相反,结果相同:createSmartAdmin 按内核、插件、应用的顺序登记页面和文案,同一个 key 后登记的胜出,所以应用里和内置页同 key 的页面直接顶替内置页。接法归前端模板那页。
包分层
依赖只能自上而下,这个次序在整套设计里承重:
text
SmartAdmin.Core 纯契约:接口、Options、Result<T>、ErrorCode。无 SqlSugar、无 ASP.NET。
↑
SmartAdmin.SqlSugar 数据层:ISqlSugarClient 单例、IRepository<>、实体基类、CodeFirst、种子。
↑
SmartAdmin.Services 领域层:实体(Sys*)、服务实现、RBAC / 数据范围。
↑
SmartAdmin.AspNetCore 宿主集成:AddSmartAdmin / MapSmartAdmin、JWT、权限/会话过滤器、内置控制器。
SmartAdmin 元包:只引用 AspNetCore,消费方装它一个即可拉起整条栈。请求流水线
一个已认证请求依次流经:
- 认证:Microsoft JWT Bearer,框架 401 被重塑成标准信封(code 40006)。
[RolePermission]:权限码就是规范化路由({METHOD}:/{route},比如你在上一页调过的GET:/api/v1/ping)。代码里没有权限字符串,授权靠在角色菜单 UI 里勾路由。超管(sadm)直接放行。它还会校验会话是否仍有效,所以强制下线立即生效。- 数据范围:授权阶段解析出当前用户的有效机构数据范围,注入
IDataScopeContext。 - 结果信封:控制器可以直接
return dto,过滤器统一包成Result<T>。业务错误则抛AdminException或返回ErrorCode,同样转成信封。错误是数字ErrorCode,从不下发本地化文案,i18n 交给前端按码翻译。
数据层约定
- 一个
SqlSugarScope单例。全局查询过滤器自动做软删除(ISoftDelete)和数据范围(IOrgScoped/DataEntity按当前请求解析的机构集过滤)。 - AOP 在插入/更新时自动填审计字段:雪花
Id、CreateTime、CreateUserId、CreateOrgId、UpdateTime、UpdateUserId。其中CreateOrgId是数据范围的锚点。业务代码只管业务字段。 - 雪花
WorkerId不配就由文件锁在本机自动抢号,同机多进程不会同号。跨机器、跨容器水平扩展时每实例必须显式配不同值,否则同毫秒发号会撞主键。
更完整的架构与设计背景见仓库的 架构与设计文档。