# whl.admin.backend **Repository Path**: whllong/whl.admin.backend ## Basic Information - **Project Name**: whl.admin.backend - **Description**: No description available - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-16 - **Last Updated**: 2026-08-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # WHL.Admin Backend > WHL.Admin 全栈业务平台 — 后端(.NET 10 + SqlSugar + 自研 WHL 框架) > > 前端:`../new.front/` | 插件:`../whl.plugins/` > 远端:gitee.com/whllong/whl.admin.backend ## 简介 WHL 后端是 **Framework + Plugins** 双层架构: - **Framework**(`Framework/WHL./`):跨插件共享的底层 — SqlSugar / Jwt / Cache / Proxy / Swagger / DistributedLock / Plugins 动态加载 - **Plugins**(从 `whl.plugins` 仓库):业务模块 — Sys / App / Platform / File / Log / Codegen / PluginManager / OAuth - **Host**(`WHL.Web/`):ASP.NET 10 Web API 宿主,`Program.cs` + `config/` + Plugins 目录 ## 软件架构 ``` WHL.Admin Backend ├── Framework/ 框架层(自研 + NuGet 包装) │ ├── WHL.Core/ 基础接口 / 基类 / 配置 / SaaSOption │ ├── WHL.Core.Utility/ Helper / Filter / Model / MVC / Middleware(TenantResolution / HttpRequest) │ ├── WHL.Core.Extensions/ DI / AppApplicationBuilderExtensions / Autofac / AutoMap │ ├── WHL.Auth/ ICurrentUser / IAuthToken │ ├── WHL.Jwt/ ICurrentUser.Scoped + JwtPermissionAttribute + AuditMiddleware │ ├── WHL.Cache/ Cache(Process / Sql / Redis / NCache) │ ├── WHL.SqlSugar/ SqlSugarScope + DbContext + UnitOfWork + Repository + BaseTable 体系 │ ├── WHL.Proxy/ ApiInterceptorService(Castle 拦截) │ ├── WHL.Share.Proxy/ IProxy*Service 业务接口约定 │ ├── WHL.Swagger/ Swashbuckle 10 集成 │ ├── WHL.DistributedLock/ 分布式锁服务 │ └── WHL.Plugins/ 插件动态加载机制(ALC 隔离) ├── Proxy/ 跨模块代理 │ ├── WHL.Proxy/ Castle 拦截实现 │ ├── WHL.Share.Proxy/ IProxy*Service 接口 │ ├── WHL.Share.Proxy.Middleware/ HttpRequestMiddleware / LogProxyHelper │ ├── WHL.Share.Proxy.Model/ 共享 DTO │ └── WHL.Share.Proxy.Log/ LogChannelWriter / DbLogWriter ├── Sys/ 系统管理 plugin(用户 / 角色 / 权限 / 菜单 / 字典) ├── WHL.Web/ 宿主(Program.cs + config/ + Plugins 目录) │ ├── Program.cs DI 注册 + 中间件管道 │ ├── config/ appsettings.json / saas.json / jwt.json 等 │ ├── Adapter/ DB adapter DLL(SqlServer/MySql/Oracle/PG/达梦/金仓/...) │ └── Plugins/ 运行时 plugin DLL(从 whl.plugins 部署) └── docs/ 整体迁移脚本 ``` ## 技术栈 | 类别 | 技术 | |------|------| | 运行时 | .NET 10(SDK 10.0.301 / Runtime 10.0.9) | | Web | ASP.NET Core 10 Web API + Swagger 10 | | ORM | SqlSugar 5.x(主库 MySQL 8.x,适配 16 种 DB) | | 鉴权 | JWT + 自研 `JwtPermissionAttribute`(3 字段合并) | | 依赖注入 | Autofac + `AppApplicationBuilderExtensions` | | 多租户 | `/t/{slug}/` URL prefix 改写 + `X-Tenant` header(2026-07-16) | | 插件 | 自研 `WHL.Plugins` + `AssemblyLoadContext` 隔离加载 | | 缓存 | Process / Sql / Redis / NCache(2026-07 加诊断接口) | | 日志 | 17 类 + DbLogWriter + LogChannelWriter(异步 channel 攒批) | | 分布式锁 | IDistributedLockService + 锁 key 命名空间 | ## 快速开始 ### 环境要求 - .NET SDK 10.0.301 - MySQL 8.x(或 16 种适配 DB 任意一种) - 默认端口:`4000`(`WHL.Web/Program.cs` `ListenAnyIP(4000)`) ### 初始化数据库 CodeFirst 已禁用,所有表都靠手写 DDL。按模块顺序执行 SQL: ```bash # 框架级 docs/distributed_lock.sql # 平台 + 业务模块(各 plugin 自己 docs/) Platform/docs/seed_platform_*.sql Platform/docs/ddl_platform_*.sql # 业务 plugin whl.plugins/App/server/docs/ddl_app_*.sql whl.plugins/Sys/server/docs/ddl_sys_*.sql whl.plugins/OAuth/server/docs/ddl_oauth_*.sql whl.plugins/File/server/docs/seed_file_category_applogo.sql # FC_0005 ... ``` ### 启动 ```bash dotnet build F:\Git\WHL.AI\backend\WHL.sln dotnet run --project WHL.Web ``` Swagger UI:`http://localhost:4000/api/doc`(2026-07-16 改 RoutePrefix) ## 关键设计 ### 多租户(2026-07-16 升级) `TenantResolutionMiddleware` 在 `UseRouting` **之前**运行: ```json // config/saas.json "SaaS": { "Mode": "Both", // None | Path | Both "PathPrefix": "/t/", // URL 改写前缀 "DefaultTenant": "master", // 无 prefix fallback "PlatformPathPrefix": "/platform/", // bypass 路径 "RewritePath": true // 改写 Request.Path } ``` 3 种 URL 模式(都路由到同一 controller): - `Mode=None`:`/platform/...` → master tenant(单租户兼容) - `Mode=Path`:`/t/{slug}/platform/...` → 显式 tenant(URL 改写后匹配) - `Mode=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" ↓ UseRouting: 匹配 /platform/stats/overview → StatsController ↓ JwtPermissionAttribute: 读 Request.Path(已改写)→ 跟 DB RouteUrl 精确匹配 ``` 跨 plugin proxy 时 `ApiInterceptorService` 透传 `X-Tenant` header 给被调模块。 ### 鉴权(2026-07-06 收口) ```csharp [JwtPermission(PageCode = 20001, IsAuthCheck = true)] public async Task Save(XxxModel entity) { ... } ``` 3 字段合并(替代老 `AllowAnonymous` / `IsIgnore` / `IsParent`): - `IsAuthCheck=true`(默认):走 token 校验 + URL RBAC 匹配 - `IsAuthCheck=false`:完全公开 `SysUrlPermissionChecker.IsUrlAllowed` 走 `IsExactMatch(p.RouteUrl, requestPath)`,**DB 存无 prefix URL,跟改写后 path 1:1 匹配**。 ### 插件加载 ``` WHL.Web/Plugins/{PluginCode}/ ├── WHL..Domain.dll ├── WHL..IService.dll ├── WHL..Service.dll ├── WHL..Web.dll └── manifest.json ``` - `IPlugin` 入口 + `IPluginMetadataSource` 反射 - `AssemblyLoadContext` 隔离(`isCollectible: true`) - `AddDynamicPlugins` 必须在 `AddSwagger` 之前(plugin ALC 先存在) - 部署走 `whl.plugins/publish.ps1` 一键打包 ### DB Adapter(2026-07-14) WHL **不写** adapter dll,只写接口 + 反射加载机制: ``` WHL.Web/Adapter/ ├── SqlServer/ # 真实 driver dll ├── MySql/ ├── Oracle/ ├── PostgreSql/ ├── DM/ # 达梦 ├── Kingbase/ # 金仓 ├── GaussDB/ ├── openGauss/ ├── Oscar/ # 神通 ├── OceanBase/ ├── PolarDB/ ├── TDSQL/ ├── GBase/ ├── HighGo/ # 瀚高 ├── MariaDB/ ├── ClickHouse/ ├── DB2/ ├── DuckDB/ └── SQLite/ # SqlSugar 内置 ``` `DbConnectionFactory.Create(config)` 反射加载(driver 不存在返回 null)。 `SysDbType` enum 21 种,每种带 `[SysDbTypeMeta(adapterName, sqlSugarDbType, description)]`,**枚举名 1:1 对应 Adapter 物理目录名**。 新增 DB = 加 enum 字段 + 在 Adapter/{enum.ToString()}/ 放 driver dll,零代码改动。 ### 数据字典 1 级 + 自动 seed(2026-07-14) - 框架级 enum(`SysDbType` 等)走 attribute 反射,**不写表** - 业务字典走 `IHostedService` 反射读 plugin Enum 命名空间,upsert 父+子 row - 前端按 `parentNo` 拿子项 - 字典树前端 1 级展开(`WTree :expand-depth="1"`) ### 日志 17 类(`Info/Success/Warn/Error/Debug/Tip/Step/Header/...`)+ DbLogWriter + LogChannelWriter(异步 channel + 攒批): - HTTP 日志:`HttpRequestMiddleware` framework 层收口 - 业务日志:`LogProxyHelper.Http` / `.Sql` 跨 plugin - DB 落 `log_http_20260701` / `log_sql` 表 - SQL 业务线程零阻塞 ### 分布式锁 `IDistributedLockService` 提供 SqlServer / MySQL / Redis / NCache 后端: ```csharp using (_lockService.Lock($"my_lock_key_{tenant}")) { // 临界区 } ``` ## 开发约定 - **Controller action 4-line**:`return Success(await _service.Xxx(args));` - **类必须 `/// `** 注释 - **async 不带 `Async` 后缀**:`Task GetById` 而非 `GetByIdAsync` - **查询方法 `Find` 开头**:`FindById` / `FindAll` / `FindByFileNo` - **`enabled` 参数 `bool?`**:null=全部 / true=启用 / false=禁用 - **DDL 规范**: - 列名 PascalCase 无下划线 - 4 件套审计:`CreateDate / CreateByUserId / LastModifyDate / LastModifyUserId` - `IsDeleted` 永远最末尾 - 索引 `idx__` snake_case - **DDL 路径**:`/server/docs/ddl_*.sql` + 配套 `seed_*.sql` - 字符串默认 `varchar(50)`(DPAPI 加密列 1000) ## Windows 服务部署(运维命令) `WHL.Web.exe` 可注册成 Windows 服务 `whl.service`(StartType=Auto,WHL.Web 死后自动拉起)。 **所有命令必须以管理员身份运行 CMD**(PowerShell 也可,sc.exe 是同一工具)。 部署目录固定 `D:\wwwroot\whl.service\`,含 `WHL.Web.exe` + 各 `WHL..Web.dll`。 ```cmd :: 创建服务(只跑一次,已存在会报错) sc create whl.service binPath= "D:\wwwroot\whl.service\WHL.Web.exe" start= auto :: 启动 sc start whl.service :: 停止 sc stop whl.service :: 改启动模式(覆盖 dll 前先改 demand,防止服务在 WHL.Web 死后自动拉起导致文件锁) sc config whl.service start= demand sc config whl.service start= auto :: 查状态 sc query whl.service :: 删除服务 sc delete whl.service ``` > `sc create` / `sc config` 语法 `binPath= "..."` / `start= auto` 的等号后必须有空格,值用双引号包路径。 ## 构建 ```bash dotnet build F:\Git\WHL.AI\backend\WHL.sln dotnet format WHL.sln --verify-no-changes ``` ## License Private — WHL.Admin 内部项目