模块开发
新建模块检查清单
新建业务模块时,至少确认:
- 在
Yi.Abp/module/{kebab-module-name}/创建 5 层项目结构。 - 项目命名使用
Yi.Module.{Module}.*。 - 每层创建对应 ABP Module 类并声明
[DependsOn]。 - 在
src组装层添加项目引用和模块依赖。 - 在
YiAbpWebModule.cs中注册动态 API。 - DTO 遵循输入
Vo、输出Dto的命名规范。
推荐生成方式
2.0 起推荐使用 Skills 生成模块和 CRUD:
历史工具
Yi.Abp.Tool 曾用于生成模块结构。
2.0 起该工具已移除,模块生成改为使用 Skill 实现。历史说明保留用于理解迁移背景,不建议继续使用旧 Tool 流程。
模块依赖关系
每层都是 ABP 模块,通过 [DependsOn] 声明依赖:
csharp
[DependsOn(
typeof(YiModuleRbacApplicationContractsModule),
typeof(YiModuleRbacDomainModule),
typeof(YiFrameworkDddApplicationModule))]
public class YiModuleRbacApplicationModule : AbpModule
{
}依赖方向应保持单向:
text
Application
-> Application.Contracts
-> Domain
-> Domain.Shared
SqlSugarCore
-> Domain应用服务开发
完整 CRUD 推荐继承 YiCrudAppService:
csharp
public class UserService : YiCrudAppService<
UserAggregateRoot,
UserGetOutputDto,
UserGetListOutputDto,
Guid,
UserGetListInputVo,
UserCreateInputVo,
UserUpdateInputVo>,
IUserService
{
}列表查询 DTO
列表查询 DTO 通常继承 PagedAllResultRequestDto,用于分页、时间范围和动态排序。
依赖注入
构造函数可以使用元组解构赋值:
csharp
public UserService(
ISqlSugarRepository<UserAggregateRoot, Guid> repository,
UserManager userManager,
ICurrentUser currentUser) : base(repository) =>
(_repository, _userManager, _currentUser) =
(repository, userManager, currentUser);对象映射
项目使用 Mapster:
csharp
var dto = entity.Adapt<UserGetOutputDto>();动态 API
项目使用 ABP Conventional Controllers。应用服务会自动映射为 REST 接口,不需要手写 Controller。
当前 Web 启动模块中统一配置:
csharp
options.ConventionalControllers.Create(
typeof(YiModuleRbacApplicationModule).Assembly,
options => options.RemoteServiceName = "rbac");
options.ConventionalControllers.ConventionalControllerSettings
.ForEach(x => x.RootPath = "api");因此接口根路径统一是 /api,不是 /api/{module}。
示例:
| 服务方法 | HTTP 接口 |
|---|---|
UserService.GetListAsync() | GET /api/user |
UserService.CreateAsync() | POST /api/user |
UserService.UpdateAsync() | PUT /api/user/{id} |
UserService.DeleteAsync() | DELETE /api/user/{id} |
查询模式
常见查询使用 SqlSugar 的 WhereIF:
csharp
var total = 0;
var output = await _repository._DbQueryable
.WhereIF(!string.IsNullOrEmpty(input.UserName), x => x.UserName.Contains(input.UserName!))
.WhereIF(input.State is not null, x => x.State == input.State)
.WhereIF(input.StartTime is not null && input.EndTime is not null,
x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime)
.LeftJoin<DeptAggregateRoot>((user, dept) => user.DeptId == dept.Id)
.OrderByDescending(user => user.CreationTime)
.Select((user, dept) => new UserGetListOutputDto(), true)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);