MoneyTree.Core 1.0.3
MoneyTree.Core 核心抽象层
📋 概述
MoneyTree.Core 是 MoneyTree 框架的基石模块,提供纯粹的领域抽象,零持久化依赖。所有其他模块均基于此层构建。它定义了实体、值对象、领域事件、工作单元以及统一 API 响应等核心类型。
| 属性 | 说明 |
|---|---|
| NuGet 包 | MoneyTree.Core |
| 外部依赖 | MediatR.Contracts |
| 定位 | 框架基石,纯领域抽象 |
| 设计原则 | 零持久化依赖、ID 简单直接、无外键关联、自动事务 |
🏗️ 项目文件结构
MoneyTree.Core/
├── MoneyTree.Core.csproj
├── GlobalUsings.cs
├── Abstractions/ # 抽象接口层
│ ├── IEntity.cs # 实体接口
│ ├── IAggregateRoot.cs # 聚合根接口
│ ├── IValueObject.cs # 值对象接口
│ ├── IUnitOfWork.cs # 工作单元接口(泛型 + 非泛型)
│ ├── IModule.cs # 模块化注册接口
│ └── IDomainEvent.cs # 领域事件接口
├── Primitives/ # 基类实现
│ ├── EntityBase.cs # 实体抽象基类(领域事件管理)
│ ├── SnowflakeEntity.cs # 实体基类(雪花ID,默认推荐)
│ ├── UlidEntity.cs # 实体基类(ULID string ID,可选)
│ ├── AggregateRoot.cs # 聚合根基类
│ ├── ValueObject.cs # 值对象基类(自动值相等)
│ ├── Enumeration.cs # 枚举类基类
│ ├── DomainEvent.cs # 领域事件基类
│ ├── DomainException.cs # 领域异常
│ ├── PageQuery.cs # 统一分页查询参数
│ ├── PageResult.cs # 分页结果(无汇总)
│ ├── PageResultTSummary.cs # 分页结果(带汇总)
│ ├── ApiResult.cs # 统一 API 响应(无数据)
│ ├── ApiResultT.cs # 统一 API 响应(带数据)
│ └── SecretEncryptionService.cs # AES-256-GCM 共享加密服务
├── Guard/
│ └── Ensure.cs # 守卫断言工具类
├── Attributes/
│ ├── IoCAttribute.cs # [IoC] 类级 IoC 注册标记(替代 IInjectable)
│ ├── InjectAttribute.cs # [Inject] 属性注入标记(含 KeyName)
│ └── TimeZoneAwareAttribute.cs # [TimeZoneAware] 时区感知标记
├── CurrentUser/
│ ├── ICurrentUser.cs # 当前用户接口
│ └── CurrentUser.cs # 当前用户基类
└── Extensions/
├── UlidExtension.cs # Ulid 扩展方法
├── BaseNConverter.cs # 任意进制(2~64)转换工具
├── DateTimeExtension.cs # DateTime 扩展(如解析 yyyy-MM 格式)
├── EnumHelper.cs # 枚举描述、下拉框选项、字典转换
├── ListExtension.cs # IList/IEnumerable 安全遍历扩展
├── SnowflakeIdGenerator.cs # 雪花算法 ID 生成器(64 位有序唯一 ID)
├── StringExtension.cs # 字符串扩展(snake_case 转换等)
└── UserIdHelper.cs # 用户/会员 ID 与邀请码生成
🧱 领域模型基类
MoneyTree.Core 提供了一套完整的 DDD 领域模型基类,帮助开发者快速构建符合领域驱动设计规范的业务模型。
EntityBase — 实体抽象基类
所有实体基类的根基,统一提供领域事件管理能力(AddDomainEvent/RemoveDomainEvent/ClearDomainEvents)。
Entity(ULID)和 SnowflakeEntity(雪花ID)均继承此类,消除领域事件管理的代码重复。
ID 的类型和生成策略由子类自行声明(因 init 属性默认值需在声明时设置)。
UlidEntity — 实体基类(string ID,Ulid)
继承 EntityBase,ID 为 string 类型(存储 Ulid 的 26 字符 Base32 字符串)。
适用于需要字符串主键的场景。框架预置能力,业务项目按需选用。
主键映射通过 EF Core Fluent API 配置,不使用 [Key] 数据注解。
public abstract class UlidEntity : EntityBase
{
public string Id { get; init; } = Ulid.NewUlid().ToString();
}
SnowflakeEntity — 实体基类(long ID,雪花算法)
继承 EntityBase,默认推荐使用的实体基类。ID 为 long 类型(64 位有序唯一雪花 ID),对应数据库 bigint 列。
线程安全的雪花 ID 生成器在类初始化时自动分配主键。
主键映射通过 EF Core Fluent API 配置,不使用 [Key] 数据注解。
public abstract class SnowflakeEntity : EntityBase
{
public long Id { get; init; } = _snowflake.Value.NextId();
}
public class Order : SnowflakeEntity // 或继承 AggregateRoot(已基于 SnowflakeEntity)
{
public long CustomerId { get; set; } // 关联 User 的 ID(雪花 ID)
}
AggregateRoot — 聚合根
继承自 SnowflakeEntity,标记为聚合根。聚合根是领域模型中唯一允许外部访问的入口。
所有聚合根的 ID 均为 long(雪花 ID)。
ValueObject — 值对象
基于属性值的相等比较,而非 ID 引用。通过 GetEqualityComponents() 定义参与比较的属性。
public class Address : ValueObject
{
public string Street { get; }
public string City { get; }
protected override IEnumerable<object> GetEqualityComponents()
{
yield return Street;
yield return City;
}
}
Enumeration — 枚举类
替代原始 enum 类型,支持更多的行为和属性:
public class OrderStatus : Enumeration
{
public static readonly OrderStatus Pending = new(1, "待处理");
public static readonly OrderStatus Shipped = new(2, "已发货");
public static readonly OrderStatus Delivered = new(3, "已签收");
}
DomainEvent — 领域事件
领域事件基类,结合 MediatR.Contracts 实现事件驱动:
public class OrderCreatedDomainEvent : DomainEvent
{
public string OrderId { get; }
public OrderCreatedDomainEvent(string orderId)
{
OrderId = orderId;
}
}
🔄 工作单元与依赖注入
| 类型 | 用途 |
|---|---|
IUnitOfWork |
工作单元接口,SaveChangesAsync 自动事务提交 |
IUnitOfWork<TDbContext> |
泛型工作单元,绑定特定 DbContext |
IoCAttribute |
类级特性,自动注册并启用属性注入 |
IModule |
模块化批量注册接口 |
[Inject] |
属性注入标记特性 |
👤 当前用户抽象
| 类型 | 用途 |
|---|---|
ICurrentUser |
当前用户接口,提供用户 ID、用户名、租户 ID、角色 |
CurrentUser |
当前用户基类,框架自动从 HttpContext 填充 |
| 属性 | 类型 | 说明 |
|---|---|---|
| Id | long |
当前用户 ID(雪花 ID) |
| UserName | string |
用户名 |
| TenantId | long? |
当前租户 ID(雪花 ID) |
| Roles | IReadOnlyList<string> |
角色列表 |
📦 ApiResult 设计
ApiResult 使用数字状态码 Code 替代 HTTP 状态码,提供丰富的静态工厂方法:
| 状态码 | 静态工厂方法 | 说明 |
|---|---|---|
| 200 | ApiResult.Success() |
请求成功 |
| 400 | ApiResult.BadRequest(error) |
请求参数错误 |
| 401 | ApiResult.Unauthorized(message) |
未认证 |
| 403 | ApiResult.Forbidden(message) |
无权限 |
| 404 | ApiResult.NotFound(entityName, id) |
资源未找到 |
| 409 | ApiResult.Conflict(error) |
资源冲突 |
| 500 | ApiResult.Error(error) |
服务器内部错误 |
ApiResult 属性
| 属性 | 类型 | 说明 |
|---|---|---|
| Code | int |
状态码 |
| Message | string? |
错误或成功的提示信息 |
| Errors | IReadOnlyList<string> |
多条错误信息列表 |
ApiResult<T> 额外属性
| 属性 | 类型 | 说明 |
|---|---|---|
| Data | T? |
成功时的返回数据 |
实例方法
| 方法 | 说明 |
|---|---|
Map(mapper) |
映射成功的值 |
Bind(binder) |
绑定操作(Monad 模式) |
Match(onSuccess, onFailure) |
模式匹配 |
响应格式
{
"code": 200,
"message": null,
"data": { "id": "01H2X5J4K3M7Q8R9S0T1V2W3X4", "name": "订单001" }
}
📄 PageQuery 分页设计
基于毫秒时间戳锚点的统一分页设计,避免传统偏移分页的数据漂移问题。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| Timestamp | double |
当前时间偏移 | 毫秒时间戳锚点(相对 2026-01-01 UTC 的偏移),首页自动生成,翻页回传 |
| PageTime | DateTimeOffset |
— | 基于 Timestamp 计算的分页时间(只读),PageIndex=1 时返回当前 UTC 时间并刷新 Timestamp |
| PageIndex | int |
1 |
目标页码(从 1 开始) |
| PageSize | int |
20 |
每页大小(1-100) |
| SortField | string? |
null |
排序字段名 |
| SortDirection | SortDirection |
Desc |
排序方向 |
分页流程
客户端首次请求(PageIndex=1)
│
▼
PageTime 返回当前 UTC 时间,并刷新 Timestamp
查询:WHERE CreatedAt <= PageTime OFFSET (PageIndex-1)*PageSize LIMIT PageSize
│
▼
返回 { items, summary, totalCount, timestamp }
│
▼
客户端翻页(PageIndex=2,回传 Timestamp)
│
▼
PageTime 基于 Timestamp 还原分页时间,数据视图保持一致
PageResult<T>(无 Summary)
| 属性 | 类型 | 说明 |
|---|---|---|
| Items | IReadOnlyList<T> |
当前页数据 |
| TotalCount | int |
总记录数 |
| PageIndex | int |
当前页码 |
| PageSize | int |
每页大小 |
| Timestamp | double |
时间戳锚点(与 PageQuery.Timestamp 语义一致) |
PageResult<T, TSummary>(带 Summary)
| 属性 | 类型 | 说明 |
|---|---|---|
| Items | IReadOnlyList<T> |
当前页数据 |
| Summary | TSummary |
汇总数据 |
| TotalCount | int |
总记录数 |
| PageIndex | int |
当前页码 |
| PageSize | int |
每页大小 |
| Timestamp | double |
时间戳锚点(与 PageQuery.Timestamp 语义一致) |
🛡️ Ensure 守卫工具
提供一组参数校验断言方法:
| 方法 | 说明 |
|---|---|
Ensure.NotNull(value, paramName) |
断言非空 |
Ensure.NotNullOrEmpty(value, paramName) |
断言非空且非空白字符串 |
Ensure.NotNullOrWhiteSpace(value, paramName) |
断言非空、非空白字符串 |
Ensure.NotEmpty(collection, paramName) |
断言集合非空 |
Ensure.InRange(value, min, max, paramName) |
断言数字在范围内 |
🧰 Extensions 工具类
SnowflakeIdGenerator — 雪花 ID 生成器
基于 Twitter Snowflake 算法的 64 位有序唯一 ID 生成器,线程安全。
| 参数 | 位数 | 说明 |
|---|---|---|
| 时间戳 | 41 位 | 毫秒级,默认纪元 2026-01-01 |
| 数据中心 ID | 5 位 | 支持 0~31 个数据中心 |
| 机器 ID | 5 位 | 支持 0~31 个节点 |
| 序列号 | 12 位 | 每毫秒 4096 个 ID |
// 使用全局单例
var generator = SnowflakeIdGenerator.Instance;
// 或手动指定 datacenterId 和 workerId 创建独立实例
var customGen = new SnowflakeIdGenerator(datacenterId: 1, workerId: 2);
// 生成 ID
long id = generator.NextId(); // 686542123456789012
string idStr = generator.NextIdString(); // "686542123456789012"
// 从 ID 反向提取生成时间
var timestamp = SnowflakeIdGenerator.ExtractTimestampFromId(id);
UserIdHelper — 用户 ID 与邀请码生成
提供多种适用于用户与会员场景的 ID 生成策略:
| 方法 | 说明 | 示例 |
|---|---|---|
GenerateUserId(prefix) |
雪花算法 + 前缀 | "USR686542123456789012" |
GenerateUserIdShort(length, prefix) |
Base62 短码,适合 URL | "USRaB3xK9mQ" |
GenerateMemberNumber(prefix) |
日期 + 6 位随机数 | "M20260701123456" |
GenerateReferralCode(length) |
加密强随机邀请码,默认 8 位 | "A3K9MXPQ" |
BaseNConverter — 任意进制转换
支持 2~64 进制字符串与整数(ulong/long/BigInteger)互转:
// 编码
string base62 = BaseNConverter.ToBase(12345UL, 62); // "3D7"
string hex = BaseNConverter.ToBase(255UL, 16); // "FF"
// 解码
ulong value = BaseNConverter.FromBaseToUInt64("3D7", 62); // 12345
BigInteger big = BaseNConverter.FromBaseToBigInteger("FF", 16); // 255
EnumHelper — 枚举帮助类
| 方法 | 说明 |
|---|---|
GetEnumDescription(value) |
获取枚举 [Description] 特性值 |
ToDropdownItems<T>() |
转换为下拉框选项(Code/Value/Label) |
ToDictionary<T>() |
转换为 Dictionary<int, string> |
FromCode<T>(code) |
根据数值获取枚举实例 |
FromName<T>(name) |
根据名称获取枚举实例 |
ListExtension — 集合安全遍历
| 方法 | 说明 |
|---|---|
ForEach(action) |
对 IList/IEnumerable 执行操作,索引遍历避免分配 |
ForEachSafe(action, throwOnModification) |
安全遍历,检测集合修改时可抛异常或提前终止 |
ForEachWithCount(action) |
遍历并返回已处理元素数量 |
StringExtension — 字符串扩展
| 方法 | 说明 |
|---|---|
ToSnakeCase() |
将 PascalCase/camelCase 转换为 snake_case |
IsNullOrWhiteSpace() |
扩展方法形式的空字符串判断 |
DateTimeExtension — 日期扩展
| 方法 | 说明 |
|---|---|
ParseYearMonthString(value) |
解析 "yyyy-MM" 格式字符串为当月第一天 |
⚠️ 领域异常
DomainException 用于在领域层抛出业务异常,包含错误码:
throw new DomainException("已发货的订单无法取消", 409, "ORDER_SHIPPED");
🎯 设计原则
- 零持久化依赖:Core 层不引用 EF Core 或任何数据库包
- ID 简单直接:默认使用
long(雪花 ID),对应数据库bigint列 - 无外键关联:通过
long类型的 CustomerId 存储关联 ID - 自动事务:
IUnitOfWork.SaveChangesAsync()内部自动处理事务 - 时区时间:配合
[TimeZoneAware]特性或UseTimeZoneAware()Fluent API 保障时区正确(按需配置,非全量应用)。MySQL 使用CrossTimeZoneConverter,PG/SQL Server 使用UtcDateTimeConverter。
Showing the top 20 packages that depend on MoneyTree.Core.
| Packages | Downloads |
|---|---|
|
MoneyTree.AI
MoneyTree Framework AI 集成模块。基于 Microsoft.Extensions.AI 抽象层,提供嵌入生成、RAG 检索等功能。
|
2 |
|
MoneyTree.ApiVersioning
MoneyTree Framework API版本控制模块。支持URL路径、请求头、查询字符串等多种版本策略,内置弃用机制和Swagger分组。
|
2 |
|
MoneyTree.Audit.Shared
MoneyTree Audit 共享层。提供审计实体模型、EF Core 配置和 EfAuditLogger 直写实现。业务服务引用此包后,可通过独立 DbContext 直写审计表,审计日志不受业务事务回滚影响。
|
2 |
|
MoneyTree.Caching
MoneyTree Framework 缓存模块。基于 Microsoft.Extensions.Caching.Hybrid 和 StackExchange.Redis 封装,提供多级缓存能力。
|
2 |
|
MoneyTree.Configuration
MoneyTree Framework 配置中心客户端,提供远程配置拉取、热更新、多源适配。
|
2 |
|
MoneyTree.DataPermission
MoneyTree Framework 数据权限模块。提供行级数据权限筛选,支持部门、区域等多维度数据隔离。
|
2 |
|
MoneyTree.DependencyInjection
MoneyTree Framework 依赖注入模块。提供自动属性注入、模块化批量注册、服务扫描等功能。
|
2 |
|
MoneyTree.EFCore
MoneyTree Framework EF Core 核心包。提供 DbContext 基类、工作单元、审计拦截器、领域事件拦截器。
|
2 |
|
MoneyTree.GeoLocation
MoneyTree Framework 地理位置服务模块。提供统一的地理位置服务抽象,支持高德、百度、腾讯等地图服务商。
|
2 |
|
MoneyTree.AI
MoneyTree Framework AI 集成模块。基于 Microsoft.Extensions.AI 抽象层,提供嵌入生成、RAG 检索等功能。
|
1 |
|
MoneyTree.ApiVersioning
MoneyTree Framework API版本控制模块。支持URL路径、请求头、查询字符串等多种版本策略,内置弃用机制和Swagger分组。
|
1 |
|
MoneyTree.Audit.Shared
MoneyTree Audit 共享层。提供审计实体模型、EF Core 配置和 EfAuditLogger 直写实现。业务服务引用此包后,可通过 DbContext 在同事务中直接写入审计表,零网络调用、强一致、实时。
|
1 |
|
MoneyTree.Caching
MoneyTree Framework 缓存模块。基于 Microsoft.Extensions.Caching.Hybrid 和 StackExchange.Redis 封装,提供多级缓存能力。
|
1 |
|
MoneyTree.CanaryRelease
MoneyTree Framework 灰度发布模块。提供渐进式流量切换,支持权重、用户哈希、租户、Header等多种分流策略。
|
1 |
|
MoneyTree.Configuration
MoneyTree Framework 配置中心客户端,提供远程配置拉取、热更新、多源适配。
|
1 |
|
MoneyTree.DataExport
MoneyTree Framework 数据导出模块。提供声明式数据导出,支持 Excel、CSV 格式,大数据量流式写入。
|
1 |
|
MoneyTree.DataPermission
MoneyTree Framework 数据权限模块。提供行级数据权限筛选,支持部门、区域等多维度数据隔离。
|
1 |
|
MoneyTree.DependencyInjection
MoneyTree Framework 依赖注入模块。提供自动属性注入、模块化批量注册、服务扫描等功能。
|
1 |
|
MoneyTree.EFCore
MoneyTree Framework EF Core 核心包。提供 DbContext 基类、工作单元、审计拦截器、领域事件拦截器。
|
1 |
|
MoneyTree.GeoLocation
MoneyTree Framework 地理位置服务模块。提供统一的地理位置服务抽象,支持高德、百度、腾讯等地图服务商。
|
1 |
.NET 10.0
- MediatR.Contracts (>= 2.0.1)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.9)
- Ulid (>= 1.4.1)