组织介绍

组织介绍

WHL —— 企业级 SaaS 平台底座 + 垂直业务系统,全栈自研。

不是又一套 CRUD 脚手架。这是一套能直接拿去交付企业项目的底座:后端框架、插件机制、多租户、数据库适配层、前端 Admin,五件事都自己做完了,业务模块拿来即用。


一、整体架构

四层结构,每层职责单一,边界清晰:

┌─────────────────────────────────────────────────────────────┐
│  WHL.Web  宿主 — ASP.NET Core 10 Web API                      │
│  DI 注册 / 中间件管道 / config/*.json / Adapter/ / Plugins/   │
├─────────────────────────────────────────────────────────────┤
│  Sys  系统管理(框架级,编译进主程序集,不是 plugin)             │
│  用户 / 角色 / 权限 / 页面 / 数据字典 / 编码规则状态 / 在线会话  │
├─────────────────────────────────────────────────────────────┤
│  Plugins  业务插件(11 个,ALC 隔离,运行时动态加载)              │
│  Platform / OAuth / Org / App / File / Codegen /              │
│  Event / Scheduler / Workflow / Log / PluginManager          │
├─────────────────────────────────────────────────────────────┤
│  Framework  跨插件共享底座(13 个 WHL.* 项目)                  │
│  Core / Core.Utility / Core.Extensions / Auth / Jwt /          │
│  Cache / SqlSugar / EventBus / Share.Proxy / Plugins /        │
│  DistributedLock / Swagger / Analyzers                        │
└─────────────────────────────────────────────────────────────┘

关键设计:Framework 与 Plugins 之间没有反向依赖。框架不知道有哪些业务插件,插件也不知道框架里除了公共抽象还有什么。新增业务模块 = 建一个 plugin 目录 + 一个 manifest.json,主程序一行不改。


二、Framework 层:13 个自研项目

项目 职责
WHL.Core 基础抽象:ICurrentUser / ICurrentTenant / ICurrentCompany / IApplicationService / SysException / SaaSOption / BaseService
WHL.Core.Utility Helper + MVC Filter + 中间件:TenantResolutionMiddleware / HttpRequestMiddleware / StringTrimFilter / DataValidationFilter / DbConnectionFactory / AliyunSmsSender
WHL.Core.Extensions DI 与映射:AppApplicationBuilderExtensions / Autofac 容器 / AutoMappingAttribute 自动映射
WHL.Auth 认证契约:IAuthToken / ITokenResult / CurrentUserExtensions
WHL.Jwt JWT 签发与鉴权:JwtAuthToken / JwtPermissionAttribute / AuditMiddleware / CurrentUser / CurrentTenant / JwtTokenSessionStore
WHL.Cache 缓存抽象与实现:ICacheService + 工厂模式(Process / Sql / Redis / NCache)+ 诊断接口(枚举 entries / 统计 / TTL)
WHL.SqlSugar ORM 底座:SqlSugarScope / SqlSugarDbContext / UnitOfWork / Repository / BaseTable / CustomDbType 21 种 DB / 租户分库路由
WHL.EventBus 事件总线:Contracts 契约 + InMemory / 外部总线后端 + EventPublisher / EventSubscriber + AOP 拦截
WHL.Share.Proxy 跨插件调用契约:IProxy*Service 接口 + 共享 DTO 模型
WHL.Plugins 插件动态加载:PluginLoadContext(可回收 ALC)/ PluginManager / PluginLoadPlan / 健康检查
WHL.DistributedLock 分布式锁:IDistributedLockService,支持 SqlServer / MySQL / Redis / NCache 后端,锁 key 带租户命名空间
WHL.Swagger Swashbuckle 10 集成 + SwaggerSummaryOperationFilter 摘要过滤
WHL.Analyzers Roslyn 源码分析器(HttpVerbParameterAnalyzer 等),把规范约束到编译期

三、插件机制:运行时可插拔

目录结构

WHL.Web/Plugins/{PluginCode}/
├── manifest.json          code / name / version / order / enabled / loadOrder
├── WHL.<Code>.Domain.dll  实体
├── WHL.<Code>.IService.dll 接口 + DTO
├── WHL.<Code>.Service.dll 实现
└── WHL.<Code>.Web.dll     Controller

manifest.json 样例

{
  "code": "Org",
  "name": "组织架构",
  "version": "0.1.0",
  "order": 200,
  "enabled": true,
  "loadOrder": [
    "WHL.Org.Domain.dll",
    "WHL.Org.IService.dll",
    "WHL.Org.Service.dll",
    "WHL.Org.Web.dll"
  ]
}

加载流程

AddDynamicPlugins()
  → 扫 Plugins/*/manifest.json
  → 每个 plugin 一个 AssemblyLoadContext(isCollectible: true,可热卸载)
  → 解析顺序:默认 ALC 已加载 → 其他 plugin ALC 已加载(跨 plugin 共享,MD5 校验)→ plugin 目录 → 主目录
  → 找不到 → fail-fast 抛 FileNotFoundException(启动期暴露,不静默)
  → AddApplicationPart 注入 MVC 路由 + ConfigureServices 注册 DI
  → 预加载阶段保留 ALC 引用,防 GC 提前回收

跨插件调用 — 插件之间不直接引用 DLL,统一走 IProxy*Service:

Plugin A ──HttpClient──→ Proxy 拦截器(自动透传 X-Tenant)──→ Plugin B

ApiInterceptorService 在拦截器里自动把当前请求的 X-Tenant 头带过去,租户上下文跨插件调用不丢。

热插拔 — IPluginManager.LoadAsync 支持运行时加载 / 卸载插件,isCollectible: true 让 DLL 文件在卸载后可以被替换或删除。


四、11 个业务插件

Platform — 平台底座

租户 / 行政区域 / 通知配置(邮件 + 短信)/ 编码规则 / 数据库类型管理 / 缓存诊断 / 分布式锁管理 / 运行统计。

内置管理诊断面板:缓存条目枚举 / 缓存统计 / 分布式锁状态 / 编码规则生成,是线上排障的入口。

OAuth — 统一认证中心

认证客户端 / 认证提供者 / 提供者租户 / 三方用户 / 授权码 / 访问令牌 / 刷新令牌。

多登录方式收敛到一处:OAuth2、TokenGuid、JWT、Soap 协议都走同一套客户端与 Provider 模型,前端只对接一个登录入口。

Org — 组织架构

组织 / 岗位 / 部门 / 员工 / 员工 N:M 部门与岗位联动 / 组织图 / 变更日志 / 数据字典。

App — 应用管理

应用注册 / 应用插件更新 / 应用运行 / 数据库管理 / 数据字典 / 脚本执行 / 服务器与部署 / 租户绑定与审计。

面向多应用交付:一个平台承载多个业务应用,应用级数据库绑定、服务器部署、租户授权都在这里闭环。

File — 文件中心

物理文件存储 / 分类 / 内容类型 / 磁盘用量统计 / 公开访问路径签发。

Codegen — 代码生成

库表结构 / 外部库接入 / 数据字典 / SQL 执行 / 模板管理 / 一键生成实体与页面。

从库里一张表到「实体 + IService + Service + Controller + 页面 + 菜单」一条龙,配合后端路由自动同步菜单,新模块落地不用手抄 CRUD。

Event — 事件中心

事件主题 / 事件规则 / 订阅 / 手动投递 / 失败重试 / 消息队列 / 通知。

规则引擎 + 失败重试 + 消息队列三件套,业务侧发事件、订阅端按规则消费,失败自动重投,不再手写回调。

Scheduler — 任务调度

定时任务 / 任务包 / 运行记录 / Worker 池 / 实例画像 / 表单 Agent / 死信队列 / 统计。

多调度模式 + Worker 实例画像 + DLQ 死信队列,任务失败可追可重投。

Workflow — 流程引擎

流程定义 / 版本 / 节点审批配置(组织 / 机器人 / 外部 / 动态)/ 子流程 / 实例 / 队列 / 我的任务 / 通知。

版本化流程定义 + 节点级审批策略,流程变更不影响在跑的实例。

Log — 日志中心

HTTP 访问日志 / SQL 审计日志 / 登录日志 / 系统日志,按天分表落库(log_http_{yyyyMMdd} / log_sql_{yyyyMMdd}),文件 + 数据库双写,异步 channel 攒批,业务线程零阻塞。

PluginManager — 插件治理

插件列表 / 版本管理 / 挂载 / 数据库迁移。配合 WHL.Plugins 的 ALC 机制,支撑插件的安装、启停、升级与热加载。


五、21 种数据库适配

WHL 不写 adapter 代码,只写接口 + 反射加载机制。

类别 数据库
商业 / 主流 MySQL、SQL Server、Oracle、PostgreSQL、SQLite、Access
国产化 达梦 DM、金仓 KingbaseES、神通 Oscar、openGauss、GaussDB、瀚高 HighGo、GBase、OceanBase、PolarDB、TDSQL、MariaDB
分析型 / 冷门 IBM Db2、ClickHouse、DuckDB
WHL.Web/Adapter/
├── SqlServer/   MySql/      Oracle/      PostgreSql/  Sqlite/
├── DM/          Kingbase/   Oscar/       openGauss/   GaussDB/
├── OceanBase/   PolarDB/   TDSQL/       GBase/       HighGo/
├── MariaDB/     ClickHouse/ DB2/         DuckDB/

扩展机制(零代码):

// 1) Framework/WHL.SqlSugar/Custom/CustomDbType.cs 加一个字段
[CustomDbTypeMeta("MySQL", DbType.MySql, "MySQL — 最流行的开源关系型数据库")]
MySql = 0,

// 2) WHL.Web/Adapter/{枚举名}/ 放 driver dll

枚举名与 Adapter 物理目录名 1:1 对应,DbConnectionFactory 反射装载。前端下拉、连接测试、预热驱动全部自动出现,不用改前后端任何代码。


六、多租户

TenantResolutionMiddleware 在 UseRouting 之前运行,三种 URL 模式都路由到同一 controller:

// config/saas.json — 字段默认值取自 Framework/WHL.Core/Configuration/SaaSOption.cs
"SaaS": {
  "Mode": "Both",                 // None | Path | Both
  "PathPrefix": "/t/",            // URL 改写前缀
  "DefaultTenant": "master",      // 无 prefix 时的 fallback(源码默认空串,按部署配)
  "PlatformPathPrefix": "/api/platform/",  // bypass 路径(源码默认)
  "RewritePath": true             // 是否改写 Request.Path 去掉 /t/{slug}
}
模式 请求 落到
None /platform/... master 租户(单租户兼容)
Path /t/{slug}/platform/... 显式租户(URL 改写后匹配)
Both 两种都支持 对应租户

Both 模式下的完整链路:

请求 /t/master/platform/stats/overview
  ↓ TryRewritePath:Request.Path → /platform/stats/overview,slug="master" 写 Items
  ↓ IsPlatformPath? 否
  ↓ Mode=Both → ResolveSlugAsync("master") → 拿 TenantIdentity
  ↓ context.Items[ItemsKey] = "master"
  ↓ Request.Headers["X-Tenant"] = tenantNo
UseRouting:匹配 /platform/stats/overview → StatsController
  ↓
JwtPermissionAttribute:读改写后的 Request.Path → 跟 DB RouteUrl 精确匹配

租户隔离贯穿:缓存 namespace 前缀、租户级配置表(每租户 1 条 + 系统租户 fallback)、SqlSugar 全局过滤、跨插件 Proxy 头透传。


七、权限与鉴权

[JwtPermission(PageCode = 20001, IsAuthCheck = true)]
public async Task<ApiResult> Save(XxxModel entity) { ... }
  • 用户 / 角色 / 页面三级 RBAC,权限表支持 Role 与 User 双主体,User 可做 allow / deny 覆盖
  • 菜单按后端路由自动同步 —— 反射 controller 的 AreaGroupAttribute + JwtPermissionAttribute 生成 sys_page,前端不用手写菜单表
  • PageCode 全解决方案唯一,冲突在启动期直接抛异常
  • URL 权限检查走 IsExactMatch(DB RouteUrl, 改写后 Request.Path),DB 存无 prefix URL,1:1 匹配
  • IsAuthCheck = false 表示完全公开端点

八、可观测

能力 说明
事件日志 4 类事件(System / Sql / Http / Login)× 5 级日志级别(Ignore / Default / OverTime / Error / Exception),按天分表落库,文件 + DB 双写
异步写入 LogChannelWriter channel 攒批(默认队列 10000 / 批量 100),SQL 日志在业务线程零阻塞
缓存诊断 后端名 / entries 枚举 / 命中统计 / TTL 倒计时 / 单条 value 查看,内置管理面板
分布式锁 可视化查看锁状态,锁 key 带租户命名空间
死信队列 Scheduler + Event 失败任务进 DLQ,可追可重投
审计 AuditMiddleware + 4 件套审计列(CreateDate / CreateByUserId / LastModifyDate / LastModifyUserId)+ IsDeleted 软删

九、数据字典与编码规则

  • 框架级枚举(如 CustomDbType)走 attribute 反射,不写表
  • 业务字典由 IHostedService 反射读取插件 Enum 命名空间,自动 upsert 父 + 子 row,前端按 parentNo 取子项
  • 编码规则(platform_coding_rule):规则编码 / 名称 / 前缀 / 后缀 / 流水步长 / 起始值 / 日期前缀类型,按应用维度配置,支持按应用编码(如 C_)生成业务单号

十、三大核心仓库

仓库 定位
whl.admin.backend 后端框架 + 宿主(Framework 13 项目 / Sys 系统管理 / WHL.Web 宿主 / DB Adapter)
whl.admin.plugins 11 个业务插件(Platform / OAuth / Org / App / File / Codegen / Event / Scheduler / Workflow / Log / PluginManager)
whl.admin.front 前端(Vue 3 + Element Plus + vue-pure-admin + Vite + TypeScript)

十一、快速开始

环境要求

  • .NET SDK 10.0.301(Runtime 10.0.9)
  • MySQL 8.x,或 CustomDbType 覆盖的任意一种适配数据库
  • 默认端口 4000

初始化数据库

CodeFirst 已禁用,所有表手写 DDL。框架级 + 核心模块一条龙:

mysql -u root -p < backend/docs/init/init-all.sql

init-all.sql 按依赖顺序执行:sys → pluginmanager → file → platform → plugin/codegen。其余插件的 schema / seed 在各自仓库目录(见 backend/README.md 表格)。

启动

dotnet build backend/WHL.sln
dotnet run --project backend/WHL.Web

Swagger UI:http://localhost:4000/api/doc

Windows 服务部署

WHL.Web.exe 可注册为 Windows 服务 whl.service(StartType=Auto,进程退出自动拉起),部署目录 D:\wwwroot\whl.service\:

sc create whl.service binPath= "D:\wwwroot\whl.service\WHL.Web.exe" start= auto
sc start whl.service

十二、开发约定

  • Controller action 四行:return Success(await _service.Xxx(args));
  • 类必须有 /// <summary> 注释
  • async 方法不带 Async 后缀:Task<X> GetById
  • 查询方法 Find 开头:FindById / FindAll
  • enabled 参数统一 bool?(null=全部 / true=启用 / false=禁用)
  • DDL 规范:列名 PascalCase 无下划线、4 件套审计列、IsDeleted 永远最末尾、索引 idx_<table>_<col>、DPAPI 加密列宽度 1000
  • 后端路由与前端请求路径全小写,[controller] = 类名去 Controller 后缀
  • 跨模块 Proxy 调用必须透传 X-Tenant,否则租户隔离失效

十三、适用场景

  • 多租户 SaaS 平台底座
  • 企业内部管理平台(组织 / 权限 / 审批 / 任务 / 日志)
  • 国产化替代工程(从 Oracle / SQL Server 平移到达梦 / 金仓 / openGauss)
  • 多应用统一交付与运维(应用编排 / 服务器部署 / 定时任务)
  • 垂直业务系统快速落地

十四、如何参与

WHL 目前是单人维护组织,接受以下形式的参与:

  • Issue 反馈:在具体仓库提 issue,bug / 改进建议均可,会逐条看
  • PR 提交:重大变更前先开 issue 讨论;小修小补直接提 PR
  • 合作开发:长期合作请邮件联系,加组织 / 提权限

项目采用 MulanPSL-2.0 开源协议,商业使用前请阅读协议条款。


捐助

如果你觉得 WHL 对你的项目有帮助,欢迎支持一杯咖啡 ☕

联系


License: MulanPSL-2.0

成就
0
Star
0
Fork
成员(1)
371744 whltian 1578921825
幻影

搜索帮助