Files
yunhai.netcore.tool/.NET5底层框架搭建方案.md
T
wanghu 8c840aa933
build / build-push (push) Failing after 40s
git
2026-09-02 10:44:33 +08:00

27 KiB
Raw Blame History

YhCore — .NET 5.0 底层框架搭建方案

参考项目:E:\YHXT\java\bschclassmanage(Spring Boot 2.7 + JPA + MySQL + Redis + Nacos)
本地类库支撑:E:\YHXT\net\Library
范围:仅底层框架(分层结构、基础设施、横切组件),不含任何业务逻辑


1. 背景与目标

将现有 Java(Spring Boot 2.7)项目 bschclassmanage 的底层框架能力平移到 .NET 5.0,形成一套可复用的后端基础框架,供 YhCore 及后续 .NET 项目使用。

目标:

  1. 保持与 Java 版一致的分层模型与开发习惯(Controller → Service → Repository → Entity),降低团队切换成本。
  2. 复用 E:\YHXT\net\Library 下已有的 DLL(日志、调度、文档处理、JSON 等),减少重复采购/引入。
  3. 提供统一的基础设施:配置管理、日志、异常处理、统一响应、用户上下文、操作日志 AOP、数据访问、缓存、任务调度、接口文档、鉴权。
  4. 只搭骨架,不写业务——框架落地后业务模块以"填空"方式接入。

2. Java 参考项目结构分析

bschclassmanage 的包结构即框架的职责划分,逐项映射如下:

Java 包 / 组件 职责 关键实现
controller (api / mobiapi / oapi) 接口入口,按渠道分三套 Spring MVC
service / service.impl 业务服务层 接口 + 实现
Repository / specification 数据访问层 + 动态查询 Spring Data JPA + Specification
entity 数据库实体 Hibernate 注解实体
model (dto / vo / excel / constant / tool) 传输与视图模型 POJO + ModelMapper 转换
config 框架配置(Async / Redis / Swagger / WebMvc / File) @Configuration
interceptor 用户上下文拦截器 UserContextInterceptor
annotation + aspect 操作日志、同步日志 AOP @OperationLog + AspectJ
scheduler 定时任务(升级、日志清理) Spring @Scheduled
validation 自定义校验(密码等) Hibernate Validator
utils 30+ 工具类(日期/分页/签名/加解压/Redis/HTTP…) 静态工具类
配置体系 bootstrap.yml(Nacos) + application-{env}.yml Nacos 配置中心,多 Profile
日志 logback-spring.xml Logback + Slf4j

中间件清单(来自 pom.xml / application.yml):MySQL 8、Redis、RabbitMQ(已预留配置)、Nacos、CAS SSO(yh-auth)、应用间签名鉴权(yh-app-auth)、Swagger(SpringDoc)。


3. 技术选型对照表(Java → .NET 5.0)

能力 Java 实现 .NET 5.0 方案 来源
Web 框架 Spring MVC ASP.NET Core 5.0 Web API 内置
依赖注入 Spring IoC 内置 DI(Autofac 可选,AOP 需要时引入) 内置 / NuGet
ORM Spring Data JPA (Hibernate) EF Core 5.0 为主;高频 SQL 用 Dapper NuGet Microsoft.EntityFrameworkCore.SqlServer 5.x + Library Dapper.Common.dll
数据库 mysql-connector-java 8.0.33(MySQL 8) 主库 SQL Server(Microsoft.EntityFrameworkCore.SqlServer 5.x);MySQL 可切换(Pomelo.EntityFrameworkCore.MySql 5.x,按 DatabaseProvider 配置切换) NuGet
动态条件查询 JPA Specification EF Core IQueryable 链式拼装 + 自定义 Specification 模式 框架自研(轻量)
缓存 spring-data-redis StackExchange.Redis(封装 RedisUtil 对应 Java 版) NuGet
配置中心 Nacos + 多 Profile appsettings.{env}.json 为基础;预留 nacos-sdk-csharp 接入点 NuGet(可选)
日志 Logback + Slf4j接口日志 log4net(Library 已有),仅用于接口访问日志:单一中间件记录接口调用详情,其余日志(SQL/框架/业务 Debug)全部关闭 Library
JSON fastjson / Jackson Newtonsoft.Json(Library 已有,兼容旧接口格式) Library
对象映射 ModelMapper AutoMapper NuGet
参数校验 Hibernate Validator FluentValidation(或 DataAnnotations) NuGet
接口文档 SpringDoc OpenAPI Swashbuckle.AspNetCore 5.x NuGet
定时任务 @Scheduled / scheduler 包 Quartz.NET 3.x(Library 已有 Quartz.dll,需核版本)或 BackgroundService Library
AOP AspectJ(aspect + annotation) ActionFilter + 自定义 Attribute(操作日志);全局逻辑用 Middleware 内置
拦截器 HandlerInterceptor Middleware / ActionFilter 内置
HTTP 客户端 HttpClient / OkHttp IHttpClientFactory NuGet Microsoft.Extensions.Http
Excel/文档 easypoi (POI) Aspose.Cells / Aspose.Words(Library 已有,打印场景核心) Library
压缩 GzipUtil ICSharpCode.SharpZipLib(Library 已有)或 BCL System.IO.Compression Library
HTML 解析 CommonMark / jsoup 类 HtmlAgilityPack(Library 已有) Library
SSO / 鉴权 CAS(yh-auth)+ yh-app-auth 签名 自定义 AuthenticationHandler + 签名中间件(对接 BschOpenApi.dll) Library + 自研
短信/消息 — HyCloud.Sms.dll / BestschSendMsg.dll(按需接入) Library
Lombok @Getter/@Setter C# 9 record / 自动属性,无需替代 语言内置
异步线程池 AsyncConfig async/await + Task.Run,无需独立配置 语言内置

4. Library DLL 适配性评估(.NET 5.0 兼容性)

.NET 5.0 可引用面向 .NET Standard 2.0/2.1 或 .NET Core/.NET 5+ 的程序集;纯 .NET Framework 4.x 程序集多数可运行但需逐一验证。

DLL 用途 .NET 5 兼容策略
Aspose.Cells/Words/Slides.dll Excel/Word/PPT 文档生成(打印核心) 需确认版本 ≥ 20.x(支持 .NET Standard 2.0)。旧 .NET Framework 版建议升级 NuGet 官方包
EntityFramework.dll + EntityFramework.SqlServer.dll EF6(SQL Server) ⚠️ 不建议使用:EF6 仅 6.4+ 支持 .NET Core;本方案选 EF Core 5(SqlServer 提供方)
Dapper.Common.dll Dapper 扩展 验证目标框架;不兼容则改用 NuGet Dapper + Dapper.Contrib
log4net.dll + Common.Logging*.dll 日志 log4net 2.0.8+ 支持 .NET Standard 2.0,可直接用;建议只保留 log4net,弃用 Common.Logging(.NET 5 有官方 ILogger 抽象)
Quartz.dll 任务调度 需 ≥ 3.1(.NET Standard 2.0);旧 2.x 不兼容,则 NuGet 升级 Quartz 3.x
Newtonsoft.Json.dll JSON 直接用(或 NuGet 对齐版本)
ICSharpCode.SharpZipLib.dll 压缩/解压 1.2+ 支持 .NET Standard 2.0,可用
HtmlAgilityPack.dll HTML 解析 1.11+ 支持 .NET Standard 2.0,可用
C5.dll 扩展集合 2.5+ 支持 .NET Standard,可用(优先级低)
BschOpenApi.dll 开放平台签名 ⚠️ 需反编译确认目标框架;若为 .NET Framework 4.x 且无 Core 版,封装为独立 Facade 隔离风险
BestschSendMsg.dll / HyCloud.Sms.dll / BestSchool.Unread.dll 消息/短信 同上,逐个验证;不兼容走 HTTP API 调用替代
HTTPSSSL.dll / DecompressFile.dll HTTPS / 解压 .NET 5 原生支持,建议弃用,用 BCL 替代

结论:核心三件(Aspose、log4net、Quartz)+ Newtonsoft.Json + SharpZipLib + HtmlAgilityPack 构成 Library 复用主力;EntityFramework、HTTPSSSL、DecompressFile 弃用;业务 DLL(BschOpenApi/Sms 等)做兼容性验证后封装接入。


5. 解决方案结构设计

E:\YHXT\net\YhCore\
├─ YhCore.sln
├─ docs\                                # 方案与设计文档
├─ lib\                                 # 本地 DLL 引用(软链或复制自 E:\YHXT\net\Library)
├─ sqlscript\                           # 数据库脚本(沿用 Java 项目惯例)
├─ src\
│  ├─ YhCore.WebApi\            # ★ 启动项目(对应 Java 的 controller + config)
│  │   ├─ Controllers\
│  │   │   ├─ Api\                      # 对应 controller/api
│  │   │   ├─ MobiApi\                  # 对应 controller/mobiapi
│  │   │   └─ OApi\                     # 对应 controller/oapi
│  │   ├─ Middleware\                   # 对应 interceptor:接口访问日志、用户上下文、异常、签名
│  │   ├─ Filters\                      # 对应 aspect+annotation:操作日志、校验
│  │   ├─ Attributes\                   # 对应 annotation:[OperationLog] 等
│  │   ├─ Extensions\                   # ServiceCollection / ApplicationBuilder 扩展
│  │   ├─ appsettings.json
│  │   ├─ appsettings.Development.json
│  │   ├─ appsettings.Production.json
│  │   ├─ log4net.config
│  │   ├─ Program.cs
│  │   └─ Startup.cs
│  ├─ YhCore.Service\           # 业务服务层(对应 service + service.impl)
│  │   ├─ Interfaces\
│  │   └─ Impl\
│  ├─ YhCore.Repository\        # 数据访问层(对应 Repository + specification)
│  │   ├─ Base\                         # Repository 基类、IRepository<T>、UnitOfWork
│  │   ├─ Specification\                # 动态查询条件构造
│  │   └─ YHDbContext.cs                # EF Core 上下文
│  ├─ YhCore.Entity\            # 数据库实体(对应 entity,DB First 生成)
│  ├─ YhCore.Model\             # 对应 model:DTO / VO / Excel / 常量
│  │   ├─ Dto\
│  │   ├─ Vo\
│  │   ├─ Excel\
│  │   └─ Constant\
│  ├─ YhCore.Common\            # 对应 utils:30+ 工具类的 .NET 版
│  │   ├─ Result\                       # ServiceResult / ApiResult 统一响应
│  │   ├─ UserContext\                  # 当前用户上下文(AsyncLocal 实现)
│  │   ├─ Cache\                        # RedisUtil 封装
│  │   ├─ Http\                         # HttpClient 封装
│  │   ├─ Security\                     # Signature 签名、加密
│  │   └─ Utils\                        # 日期/分页/枚举/校验/Gzip…
│  └─ YhCore.Task\              # 对应 scheduler:Quartz 任务宿主
│      ├─ Jobs\
│      └─ QuartzHostedService.cs
├─ tests\
│  └─ YhCore.Tests\             # xUnit 单元测试
└─ docker\                              # Dockerfile / compose(沿用 Java 项目惯例)

分层依赖方向(单向,禁止反向引用):

WebApi → Service → Repository → Entity
  └──────→ Model ←──────┘
  └──────────→ Common ←──────────┘(Common 不依赖任何上层)

6. 底层框架核心组件设计

6.1 配置体系(对应 bootstrap.yml + application-{env}.yml)

  • 本地开发:appsettings.json + appsettings.Development.json(默认不连 Nacos,对应 Java 的 profile=local)。
  • 生产:环境变量注入(对应 CI 注入 SPRING_PROFILES_ACTIVE=prod)+ 可选 Nacos(nacos-sdk-csharp),加载顺序沿用"共享配置 → 应用配置 → 环境变量覆盖"的三级优先级约定。
  • 强类型配置:services.Configure<YhSsoOptions>(configuration.GetSection("Yh:Sso")),Options 类按 Java yml 的 key 结构一一对应(yh.sso.*、yh.app-auth.*、datasource、redis 等)。

6.2 日志(对应 logback-spring.xml)

只保留接口访问日志,其余日志一律去除。

  • 通过 ApiAccessLogMiddleware(请求管道最外层)统一记录每次接口调用详情,框架不再输出任何其他日志(SQL 日志、框架运行日志、业务 Debug/Info 日志全部关闭)。
  • 记录字段:请求时间、渠道(api / mobiapi / oapi)、请求路径与方法、Query 与 Body(敏感字段脱敏,对应 SensitiveDataUtil)、客户端 IP、当前用户(取 UserContext)、响应状态码、响应耗时、异常摘要(若本次请求出错)。
  • 落地:log4net(Library 复用)配置单个 RollingFileAppender,按天滚动一个接口日志文件,不做级别分文件。
  • 屏蔽其他输出:appsettings.json 中将 Microsoft.*、System.*、Pomelo.*(EF Core SQL 日志)等内置日志类别全部设为 None;代码规范上禁止在业务代码中调用 ILogger 写日志。

6.3 统一响应(对应 yh-api-std / BestSchool.Common 的 ServiceResult)

复用 Library\新建文件夹\BestSchool.Common\ServiceResult.cs 的思路,定义:

public class ApiResult<T>
{
    public int Code { get; set; }        // 0=成功
    public string Message { get; set; }
    public T Data { get; set; }
    public static ApiResult<T> Ok(T data) => new() { Code = 0, Data = data };
    public static ApiResult<T> Fail(string msg, int code = -1) => new() { Code = code, Message = msg };
}

配合 ActionFilter 自动包装 Controller 返回值,业务代码只返回 T。

6.4 全局异常处理中间件

  • 捕获未处理异常 → 返回统一 ApiResult.Fail(对应 Java 的 @ControllerAdvice);异常详情由 6.2 的接口访问日志统一记录,不单独输出错误日志。
  • 自定义 BizException:Service 层抛出业务异常,中间件翻译为友好错误码。

6.5 用户上下文(对应 UserContextInterceptor + UserContext + ControllerUserContextAspect)

  • UserContextMiddleware:从 CAS Ticket / Header 解析当前用户,写入 AsyncLocal<UserContext>。
  • 全链路任意位置通过 UserContextHolder.Current 获取(对应 Java 的 ThreadLocal 方案,AsyncLocal 是 .NET 的等价物且对 async 安全)。
  • Java DWebMvcConfig 不迁移、直接移除:它仅负责把 UserContextInterceptor 注册到 /**,中间件方式天然作用于全管道,无需等价的注册配置类。

6.6 操作日志 AOP(对应 @OperationLog + OperationLogAspect)

[AttributeUsage(AttributeTargets.Method)]
public class OperationLogAttribute : Attribute
{
    public string Module { get; }
    public string Action { get; }
}
  • [OperationLog("班级", "升级")] 标注在 Controller 方法上;
  • OperationLogFilter : IAsyncActionFilter 在方法执行后解析 SpEL 等价表达式(用路由参数/参数名占位符),异步落库到操作日志表(对应 OperationLogResolver + OperationLogRecordRepository)。

6.7 数据访问(对应 JPA Repository + Specification)

  • 数据库方案:主库 SQL Server(Server=localhost;Database=EduCloudDocPrint),同时支持切换 MySQL——appsettings.json 中 DatabaseProvider 取 SqlServer(默认)或 MySql,AddDataLayer 扩展按此选择 EF Core 提供方(Microsoft.EntityFrameworkCore.SqlServer 5.x / Pomelo.EntityFrameworkCore.MySql 5.x)。连接串生产环境用环境变量覆盖(ConnectionStrings__SqlServer),不入库密码。
  • EF Core 5:YHDbContext 集中管理实体;旧 EF6 实体无 [Key] 标注,统一按约定推断主键(优先 Id,其次 SerID,皆无则 Keyless);IRepository<T> / RepositoryBase<T> 提供 CRUD、分页(对应 PageUtil)、批量操作。
  • Specification 轻量实现:Expression<Func<T, bool>> 组合器(And/Or/Not),对应 Java specification 包的动态查询。
  • Dapper 通道:复杂报表/批量快照插入(对应 Java 用 JdbcTemplate 的 backupAllSnapshot 场景)走 Dapper,与 EF 共存。
  • 事务:IDbContextTransaction 显式事务 + UnitOfWork 模式;生产 ddl-auto=none 对应 EF 禁用自动迁移,结构变更走 sqlscript 脚本。

6.8 Redis 缓存(对应 RedisConfig + RedisUtil)

  • StackExchange.Redis 单例 ConnectionMultiplexer 注入;
  • RedisUtil 封装 String/Hash/Set/分布式锁,Key 规范常量集中在 RedisConstants(对应 Java 同名类)。

6.9 任务调度(对应 scheduler 包)

  • Quartz.NET 3.x + QuartzHostedService 承载;Job 通过 DI 构造(可访问 Service 层)。
  • 框架内置两个示例 Job 骨架:定时任务模板、日志清理任务模板(对应 SysOperationLogCleanupTask),业务 Job 后续按模板添加。
  • 轻量场景可直接用 BackgroundService + PeriodicTimer。

6.10 接口文档(对应 SwaggerConfig)

  • Swashbuckle:按渠道分组(api / mobiapi / oapi 三个 Swagger Doc),Header 鉴权参数预置,对应 Java 的三套 controller 分组。

6.11 鉴权(对应 yh-auth CAS + yh-app-auth 签名)

  • CasAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>:复刻 CAS ticket 校验与 SSO 重定向逻辑(配置项对齐 yh.sso.*)。
  • AppAuthMiddleware:应用间调用签名校验(app-key / timestamp / sign),对应 yh.app-auth.*;优先尝试复用 BschOpenApi.dll 的签名算法,不兼容则按 Signature.java 逻辑重写。

6.12 参数校验(对应 validation 包)

  • FluentValidation:每个 DTO 一个 AbstractValidator<T>,自动注册;
  • 自定义校验规则(如密码强度,对应 PasswordValidation)实现为可复用扩展方法。

6.13 文件上传(对应 FileConfig + multipart 50MB)

  • FormOptions 配置 MultipartBodyLengthLimit = 50MB;
  • 上传路径走强类型配置 FileOptions,对应 Java FileConfig。

6.14 路由常量(对应 Java Config 枚举的 API_PATH / MOBILEAPI_PATH / OAPI)

  • Java Config 枚举中的三个渠道路由前缀,集中在 Common 层 ApiRoutes 常量类: ApiPath = "api"、MobiApiPath = "mobiapi"、OApiPath = "oapi"。
  • Controller 一律用 [Route(ApiRoutes.ApiPath + "/log")] 引用常量,禁止硬编码 "api/..." 字符串,与 Java 版 Config.API_PATH 常量化做法保持一致。
  • Config 枚举的 FILE_PATH / FILE_WEB_PATH 已由 6.13 FileOptions 覆盖;getString/getInt(ResourceBundle 读 config.properties)无对应需求,不迁移。

7. 关键骨架代码示例

Program.cs / Startup.cs 装配顺序:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
            .AddNewtonsoftJson(o => {                   // 对齐 Java Jackson 配置
                o.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
            });
    services.AddDataLayer(Configuration);   // SQL Server 主库;DatabaseProvider=MySql 时切 Pomelo
    services.AddRedisCache(Configuration); // RedisUtil 单例(StackExchange.Redis,连接串取 Redis:ConnectionString)
    services.AddRepositories();        // 扩展方法:批量注册 IRepository<T>
    services.AddServices();            // 扩展方法:批量注册 Service
    services.AddAutoMapper(typeof(Startup));
    services.AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
    services.AddSwaggerGen(/* 三组文档 + 鉴权 Header */);
    services.AddQuartzHosted();        // 调度
    services.AddLog4Net();             // 日志
}

public void Configure(IApplicationBuilder app)
{
    app.UseMiddleware<ApiAccessLogMiddleware>();      // 0. 接口访问日志(唯一日志出口)
    app.UseMiddleware<GlobalExceptionMiddleware>();   // 1. 异常兜底(异常摘要并入接口日志)
    app.UseMiddleware<UserContextMiddleware>();       // 2. 用户上下文
    app.UseMiddleware<AppAuthMiddleware>();           // 3. 应用签名(oapi 渠道)
    app.UseAuthentication();                          // 4. CAS
    app.UseAuthorization();
    app.UseEndpoints(e => e.MapControllers());
}

8. 实施步骤

阶段 内容 产出
1. 环境准备 安装 .NET 5 SDK、创建解决方案与 7 个类库项目、配置 lib 本地引用 sln 骨架可编译
2. 基础件接入 log4net、Newtonsoft、Swagger、AutoMapper、FluentValidation 启动即出 Swagger 页
3. 数据层 建 YHDbContext + Repository 基类 + Specification + Dapper 通道(暂不生成业务实体) 连通 SQL Server 冒烟通过
4. 横切组件 统一响应、异常中间件、接口访问日志中间件(唯一日志出口)、用户上下文、操作日志 Filter Postman 验证全链路
5. 缓存与调度 RedisUtil + Quartz 宿主 + Job 模板 Redis 读写、Job 触发验证
6. 鉴权 CAS Handler + 签名中间件(对接 yh 平台配置) SSO 联调通过
7. 收尾 Dockerfile、CI 配置、开发规范文档 框架交付

9. 风险与注意事项

  1. .NET 5 已 EOS(2022-05 停止支持):按用户要求采用 5.0,但建议方案预留升级路径(代码只使用与 .NET 6/8 兼容的 API,将来改 TargetFramework 即可)。
  2. Library DLL 多为 .NET Framework 时代产物:开工第一件事是对 lib 全部 DLL 做目标框架核验(可用 dotnet 反编译或 Assembly.LoadFrom 试加载),不兼容的立即走 NuGet 替代,避免后期返工。
  3. EF6 → EF Core 语义差异:Java JPA 的 ddl-auto: update 在 EF Core 中对应 Migrate,生产环境一律禁用自动迁移,结构变更走 SQL 脚本(与 Java 侧生产约定一致)。
  4. AsyncLocal vs ThreadLocal:用户上下文必须用 AsyncLocal,否则 async 方法链中上下文丢失。
  5. Nacos 为可选项:本地/单机部署先用 appsettings.{env}.json + 环境变量;需接入配置中心时再启用 nacos-sdk-csharp,配置 key 结构保持与 Java yml 一致,降低运维心智成本。
  6. 命名习惯迁移:Java 的 getClassHistoryTree 等接口路径与返回结构保持原样(URL 与 JSON 字段不变),保证前端/移动端零改动切换。