跳到主要内容

新模块开发指南

本文介绍如何按照 FreeKit Pro Modules 的约定从零创建并注册一个新业务模块。

目录结构约定

src/Services/<ModuleName>/
└── FreeKit.<ModuleName>/ # 模块主项目
├── <ModuleName>ModuleStartup.cs # 模块入口(必须)
├── Domain/ # 实体、领域逻辑
├── Application/ # 应用服务、用例
├── Contracts/ # DTOs、接口(共享给客户端)
├── Controllers/ # REST 控制器
└── Models/ # FreeSql 实体配置(可选)

第 1 步 — 创建项目

cd src/Services
mkdir Notification
cd Notification
dotnet new classlib -n FreeKit.Notification --framework net10.0

.csproj 中添加对 BuildingBlocks 的引用:

<ItemGroup>
<ProjectReference Include="..\..\..\BuildingBlocks\IGeekFan.FreeKit.Infrastructure\IGeekFan.FreeKit.Infrastructure.csproj" />
</ItemGroup>

第 2 步 — 实现 IModuleStartup

每个模块必须实现 IGeekFan.FreeKit.Modularity.IModuleStartup

using IGeekFan.FreeKit.Modularity;

namespace FreeKit.Notification;

public class NotificationModuleStartup : IModuleStartup
{
/// <summary>
/// 注册服务到 DI 容器
/// </summary>
public void ConfigureServices(IServiceCollection services, IConfiguration c)
{
// 注册 AutoMapper
services.AddAutoMapper(typeof(NotificationModuleStartup).Assembly);

// 读取模块配置
services.Configure<NotificationOption>(c.GetSection("Notification"));

// 注册业务服务
services.AddScoped<INotificationService, NotificationService>();
}

/// <summary>
/// 配置中间件 / 同步 FreeSql 表结构
/// </summary>
public void Configure(WebApplication app, IWebHostEnvironment env)
{
IFreeSql fsql = app.Services.GetRequiredService<IFreeSql>();
fsql.CodeFirst.SyncStructure(
typeof(NotificationRecord)
);
}
}

第 3 步 — 定义实体

使用 FreeSql 的 [Table] 特性,无需继承任何基类(但可继承框架提供的 AuditEntity):

using FreeSql.DataAnnotations;

namespace FreeKit.Notification.Domain;

[Table(Name = "notification_record")]
public class NotificationRecord
{
[Column(IsPrimary = true, IsIdentity = true)]
public long Id { get; set; }

public Guid UserId { get; set; }

public string Title { get; set; } = string.Empty;

public string Content { get; set; } = string.Empty;

public bool IsRead { get; set; }

[Column(ServerTime = DateTimeKind.Utc, CanUpdate = false)]
public DateTime CreateTime { get; set; }
}

第 4 步 — 编写 Controller

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;

namespace FreeKit.Notification.Controllers;

[ApiController]
[Route("api/notification")]
[Authorize]
public class NotificationController : ControllerBase
{
private readonly INotificationService _service;

public NotificationController(INotificationService service)
=> _service = service;

[HttpGet]
public async Task<IActionResult> GetList([FromQuery] int page = 1, [FromQuery] int size = 20)
{
var result = await _service.GetListAsync(page, size);
return Ok(result);
}

[HttpPost("{id}/read")]
public async Task<IActionResult> MarkRead(long id)
{
await _service.MarkReadAsync(id);
return Ok();
}
}

第 5 步 — 注册到 E.cs

src/Services/Host/FreeKit.DI/E.csModules 字典中添加一行:

// FreeKit.DI/E.cs
using FreeKit.Notification; // <-- 添加 using

public static readonly Dictionary<string, Type> Modules = new(StringComparer.OrdinalIgnoreCase)
{
{ "identity", typeof(IdentityModuleStartup) },
// ... 其他模块
{ "notification", typeof(NotificationModuleStartup) }, // <-- 添加此行
};

同时在 FreeKit.DI.csproj 中添加项目引用:

<ProjectReference Include="..\..\Notification\FreeKit.Notification\FreeKit.Notification.csproj" />

第 6 步 — 添加配置(可选)

appsettings.Development.json 中添加模块配置段:

{
"Notification": {
"MaxRetryCount": 3,
"DefaultExpireDays": 30
}
}

对应的 Options 类:

public class NotificationOption
{
public int MaxRetryCount { get; set; } = 3;
public int DefaultExpireDays { get; set; } = 30;
}

模块禁用

只需从 E.csModules 字典中删除对应的键即可,无需修改任何其他代码。

约定总结

规则说明
命名<Entity>ServiceI<Entity>Service<Entity>Dto<Entity>Req
启动类必须实现 IModuleStartup,命名为 <Module>ModuleStartup
建表Configure() 中调用 fsql.CodeFirst.SyncStructure(...)
DI 注册ConfigureServices() 中注册,不使用 Program.cs
配置使用 IOptions<T> 模式,Options 类命名为 <Feature>Option