Files
DocprintService/.NET5底层框架搭建方案.md
T
2026-09-02 10:23:24 +08:00

325 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` 的思路,定义:
```csharp
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)
```csharp
[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 装配顺序:**
```csharp
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 字段不变),保证前端/移动端零改动切换。