namespace Admin.NET.Plugin.HwPortal; // [AllowAnonymous] 是 ASP.NET Core/Furion 里的匿名放行特性。 // 你可以把它理解成 Spring Security 里“这个接口不要求登录”的声明式配置。 // 和 Spring 常见的配置类放行不同,这里直接写在控制器上,阅读时更集中。 [AllowAnonymous] [Route("portal/search")] public class HwSearchController : HwPortalControllerBase { // readonly 字段表示“构造完成后不允许再被重新赋值”。 // 这和 Java 里把依赖字段声明成 final 的意图一致:依赖一旦注入完成,就不要在运行期乱改。 private readonly HwSearchService _service; public HwSearchController(HwSearchService service) { // 这就是 C#/ASP.NET Core 最常见的“构造函数注入”。 // 对应 Java Spring 你可以理解成: // 1. 以前常见的 @Autowired 字段注入 // 2. 更推荐的 @RequiredArgsConstructor / 构造器注入 // 这里只是 C# 没有 Lombok,直接手写构造函数而已。 _service = service; } // [HttpGet] 表示这个方法处理 GET 请求。 // Java Spring 里对应 @GetMapping。 // 这里没有写路径,表示沿用类上的基础路由,也就是 /portal/search。 [HttpGet] // 这是我们自定义的限流特性。 // 注意它不是“只做标记”,而是自己实现了 IAsyncActionFilter,框架执行到这里会先跑限流逻辑,再决定要不要进入方法体。 [HwPortalIpRateLimit("portal_search", 60, 120)] public async Task Search([FromQuery] string keyword, [FromQuery] int? pageNum, [FromQuery] int? pageSize) { // async + await 是 C# 异步编程的核心写法。 // 你可以先把它理解成“这个方法里要等待数据库/IO,但等待时不想卡死线程”。 // Java 里它不等于 new Thread,也不等于 CompletableFuture 全套写法,更像框架层帮你把异步 IO 写法简化了。 // [FromQuery] 表示参数从 URL 查询串里绑定,例如 ?keyword=轮胎&pageNum=1。 // Spring Boot 里通常你会写 @RequestParam;ASP.NET Core 则更常用 [FromQuery] 明确绑定来源。 return Success(await _service.Search(keyword, pageNum, pageSize)); } // 这里写了 "edit",所以完整路由会变成 /portal/search/edit。 // 这和 Spring 的 @GetMapping("/edit") 完全是同一类路由声明思路。 [HttpGet("edit")] [HwPortalIpRateLimit("portal_search_edit", 60, 120)] public async Task EditSearch([FromQuery] string keyword, [FromQuery] int? pageNum, [FromQuery] int? pageSize) { // 展示端和编辑端共用同一套搜索主逻辑,只是编辑端需要多返回 editRoute。 // 这种“控制器只做参数接线,真正差异交给 Service”的写法,是为了让 API 层保持薄,不把业务判断写散。 return Success(await _service.SearchForEdit(keyword, pageNum, pageSize)); } }