【ABP】项目示例(2)——聚合根和实体

news/2025/2/15 15:39:08/文章来源:https://www.cnblogs.com/rainseason/p/18621570

聚合根和实体

在上一章节中,已经完成了项目搭建的前置准备,在这一章节中,实现领域层的聚合根和实体
创建名称为General.Backend.Domain的标准类库,分别新建名称为Entities、Services、IRepositories和Specifications的文件夹,用于存放实体和聚合根、领域服务、仓储接口和规约。
本项目使用ABP相关的Nuget包的版本为8.3.0,为保持版本一致,后续其他ABP相关的Nuget包都使用该版本。
在程序包管理控制台选中General.Backend.Domain,执行以下命令安装ABP领域相关的Nuget包。

Install-Package Volo.Abp.Ddd.Domain -v 8.3.0

新建名称为GeneralDomainModule的领域模块类

public class GeneralDomainModule : AbpModule
{public override void ConfigureServices(ServiceConfigurationContext context){}
}

创建名称为General.Backend.Domain.Shared的标准类库,分别新建名称为Enums和Consts的文件夹,用于存放枚举和常量。
在程序包管理控制台选中General.Backend.Domain.Shared,执行以下命令安装ABP领域共享相关的Nuget包。

Install-Package Volo.Abp.Ddd.Domain.Shared -v 8.3.0

新建名称为GeneralDomainSharedModule的领域共享模块类

public class GeneralDomainSharedModule : AbpModule
{}

General.Backend.Domain添加项目引用General.Backend.Domain.Shared

用户聚合

在Enums文件夹下新建名称为FrozenStatus的用户冻结状态枚举

/// <summary>
/// 冻结状态
/// </summary>
public enum FrozenStatus
{/// <summary>/// 未冻结/// </summary>UnFrozen = 1,/// <summary>/// 已冻结/// </summary>Frozen = 2
}

在Consts文件夹下新建名称为UserConsts的用户常量类,定义用户领域使用到的常量,用于数据库表配置和常规校验

public static class UserConsts
{public const string UserTableName = "user";public const string UserTableComment = "用户表";public const string UserRoleTableName = "user_role";public const string UserRoleTableComment = "用户角色表";public const string AdminAccount = "admin";public const string AdminName = "Admin";public const int MaxLoginErrorCount = 5;public const int MinAccountLength = 2;public const int MaxAccountLength = 32;public const int MinPasswordLength = 6;public const int MaxPasswordLength = 32;public const int MinNameLength = 1;public const int MaxNameLength = 32;public const int MaxContactLength = 64;public const int MaxAddressLength = 64;
}

在Entities文件夹下新建名称为User的用户聚合根类和UserRole的用户角色实体类,业务逻辑为一个用户拥有一个或者多个角色,用户的角色需要通过用户聚合来管理

/// <summary>
/// 用户
/// </summary>
public class User : FullAuditedAggregateRoot<Guid>
{/// <summary>/// 账号/// </summary>public virtual string Account { get; private set; } = string.Empty;/// <summary>/// 密码/// </summary>public virtual string Password { get; private set; } = string.Empty;/// <summary>/// 用户名称/// </summary>public virtual string Name { get; private set; } = string.Empty;/// <summary>/// 联系方式/// </summary>public virtual string? Contact { get; set; }/// <summary>/// 地址/// </summary>public virtual string? Address { get; set; }/// <summary>/// 登录错误次数/// </summary>public virtual byte LoginErrorCount { get; private set; }/// <summary>/// 是否冻结(1:未冻结,2:已冻结)/// </summary>public virtual FrozenStatus IsFrozen { get; private set; }/// <summary>/// 用户关联角色列表/// </summary>public virtual ICollection<UserRole> UserRoles { get; private set; } = [];protected User(){}public User(Guid id,[NotNull] string account,[NotNull] string password,[NotNull] string name,[CanBeNull] string? contact = null,[CanBeNull] string? address = null){Id = id;SetAccount(account);SetPassword(password);SetName(name);IsFrozen = FrozenStatus.UnFrozen;Contact = Check.Length(contact, nameof(contact), UserConsts.MaxContactLength);Address = Check.Length(address, nameof(address), UserConsts.MaxAddressLength);}private void SetAccount([NotNull] string account){Account = Check.Length(account, nameof(account), UserConsts.MaxAccountLength, UserConsts.MinAccountLength)!;}internal virtual void SetPassword([NotNull] string password){Password = Check.Length(password, nameof(password), UserConsts.MaxPasswordLength, UserConsts.MinPasswordLength)!;}public virtual void SetName([NotNull] string name){Name = Check.Length(name, nameof(name), UserConsts.MaxNameLength, UserConsts.MinNameLength)!;}internal virtual bool CheckPassword([NotNull] string password){Check.NotNullOrEmpty(password, nameof(password));if (Password != password) {TryFrozen();return false;}UnFrozen();return true;}public virtual void UnFrozen(){LoginErrorCount = 0;IsFrozen = FrozenStatus.UnFrozen;}private bool TryFrozen(){var isSuccess = false;if (Account != UserConsts.AdminAccount){LoginErrorCount += 1;if (LoginErrorCount >= UserConsts.MaxLoginErrorCount){IsFrozen = FrozenStatus.Frozen;isSuccess = true;}}return isSuccess;}public virtual void SetRoles(ICollection<UserRole> userRoles){UserRoles = userRoles;}
}
/// <summary>
/// 用户角色
/// </summary>
public class UserRole : Entity<Guid>, IHasCreationTime
{/// <summary>/// 用户Id/// </summary>public virtual Guid UserId { get; private set; }/// <summary>/// 角色Id/// </summary>public virtual Guid RoleId { get; private set; }/// <summary>/// 创建时间/// </summary>public virtual DateTime CreationTime { get; private set; }protected UserRole(){}internal UserRole(Guid id,Guid userId,Guid roleId){Id = id;UserId = userId;RoleId = roleId;}
}

角色聚合

在Entities文件夹下新建名称为Role的角色聚合根类和RoleMenus的角色菜单实体类,业务逻辑为一个角色拥有一个或者多个菜单,用户拥有的菜单需要通过角色聚合来管理

/// <summary>
/// 角色
/// </summary>
public class Role : FullAuditedAggregateRoot<Guid>
{/// <summary>/// 编码/// </summary>public virtual string Code { get; private set; } = string.Empty;/// <summary>/// 名称/// </summary>public virtual string Name { get; private set; } = string.Empty;/// <summary>/// 描述/// </summary>public virtual string? Remark { get; set; }/// <summary>/// 角色关联菜单列表/// </summary>public virtual ICollection<RoleMenu> RoleMenus { get; private set; } = [];protected Role(){}public Role(Guid id,[NotNull] string code,[NotNull] string name,[CanBeNull] string? remark = null){Id = id;Code = Check.Length(code, nameof(code), RoleConsts.MaxCodeLength, RoleConsts.MinCodeLength)!;SetName(name);Remark = Check.Length(remark, nameof(remark), RoleConsts.MaxRemarkLength);}internal virtual void SetName([NotNull] string name){Name = Check.Length(name, nameof(name), UserConsts.MaxNameLength, UserConsts.MinNameLength)!;}public virtual void SetMenus(ICollection<RoleMenu> roleMenus){RoleMenus = roleMenus;}
}
/// <summary>
/// 角色菜单
/// </summary>
public class RoleMenu : Entity<Guid>, IHasCreationTime
{/// <summary>/// 角色Id/// </summary>public virtual Guid RoleId { get; private set; }/// <summary>/// 菜单编码/// </summary>public virtual string MenuCode { get; private set; } = string.Empty;/// <summary>/// 创建时间/// </summary>public virtual DateTime CreationTime { get; private set; }protected RoleMenu(){}internal RoleMenu(Guid id,Guid roleId,[NotNull] string menuCode){Id = id;RoleId = roleId;MenuCode = Check.Length(menuCode, nameof(menuCode), RoleConsts.MaxMenuCodeLength, RoleConsts.MinMenuCodeLength)!;}
}

同样地在Consts文件夹下新建名称为RoleConsts的角色常量类,定义角色领域使用到的常量,用于数据库表配置和常规校验

public class RoleConsts
{public const string RoleTableName = "role";public const string RoleTableComment = "角色表";public const string RoleMenuTableName = "role_menu";public const string RoleMenuTableComment = "角色菜单表";public const string AdminRoleCode = "Admin";public const string AdminRoleName = "Admin";public const int MaxCodeLength = 32;public const int MinCodeLength = 1;public const int MaxNameLength = 32;public const int MinNameLength = 1;public const int MaxMenuCodeLength = 64;public const int MinMenuCodeLength = 1;public const int MaxRemarkLength = 255;
}

菜单聚合

在Entities文件夹下新建名称为Menu的菜单聚合根类

/// <summary>
/// 菜单
/// </summary>
public class Menu : BasicAggregateRoot<Guid>, IHasCreationTime
{/// <summary>/// 编码/// </summary>public virtual string Code { get; private set; } = string.Empty;/// <summary>/// 父编码/// </summary>public virtual string ParentCode { get; private set; } = string.Empty;/// <summary>/// 名称/// </summary>public virtual string Name { get; private set; } = string.Empty;/// <summary>/// 类型/// </summary>public virtual string Type { get; private set; } = string.Empty;/// <summary>/// 层级/// </summary>public virtual int Level { get; private set; }/// <summary>/// 图标/// </summary>public virtual string? Icon { get; private set; }/// <summary>/// 路由地址/// </summary>public virtual string? UrlAddress { get; private set; }/// <summary>/// 组件地址/// </summary>public virtual string? ComponentAddress { get; private set; }/// <summary>/// 排序/// </summary>public virtual int Sort { get; private set; }/// <summary>/// 创建时间/// </summary>public virtual DateTime CreationTime { get; private set; }/// <summary>/// 子菜单/// </summary>public List<Menu> SubMenu { get; private set; } = [];protected Menu(){}public Menu([NotNull] string code,[NotNull] string parentCode,[NotNull] string name,[NotNull] string type,int level,int sort,[CanBeNull] string? icon = null,[CanBeNull] string? urlAddress = null,[CanBeNull] string? componentAddress = null){Code = Check.Length(code, nameof(code), MenuConsts.MaxCodeLength, MenuConsts.MinCodeLength)!;ParentCode = Check.Length(parentCode, nameof(parentCode), MenuConsts.MaxParentCodeLength, MenuConsts.MinParentCodeLength)!;Name = Check.Length(name, nameof(name), MenuConsts.MaxNameLength, MenuConsts.MinNameLength)!;Type = Check.Length(type, nameof(type), MenuConsts.MaxTypeLength, MenuConsts.MinTypeLength)!;Level = level;Sort = sort;Icon = Check.Length(icon, nameof(icon), MenuConsts.MaxIconLength);UrlAddress = Check.Length(urlAddress, nameof(urlAddress), MenuConsts.MaxUrlAddressLength);ComponentAddress = Check.Length(componentAddress, nameof(componentAddress), MenuConsts.MaxComponentAddressLength);}private Menu([NotNull] string code){Code = Check.Length(code, nameof(code), MenuConsts.MaxCodeLength, MenuConsts.MinCodeLength)!;}internal static Menu BuildRoot(){return new Menu(MenuConsts.MenuRootCode);}internal static Menu BuildCatalog([NotNull] string code,[NotNull] string name,[NotNull] string icon,[NotNull] string urlAddress,int sort){return new Menu(code,MenuConsts.MenuRootCode,name,MenuConsts.CatalogTypeName,1,sort,icon,urlAddress);}internal static Menu BuildMenu([NotNull] string code,[NotNull] string parentCode,[NotNull] string name,[NotNull] string icon,[NotNull] string urlAddress,[NotNull] string componentAddress,int sort){return new Menu(code,parentCode,name,MenuConsts.MenuTypeName,2,sort,icon,urlAddress,componentAddress);}internal static Menu BuildFunc([NotNull] string code,[NotNull] string parentCode,[NotNull] string name,int sort){return new Menu(code,parentCode,name,MenuConsts.FuncTypeName,3,sort);}public void SetSubMenu([NotNull] List<Menu> subMenus){Check.NotNull(subMenus, nameof(subMenus));foreach (var parentCode in subMenus.Select(menu => menu.ParentCode)){Check.Equals(Code, parentCode);}SubMenu = subMenus;}
}

同样地在Consts文件夹下新建名称为MenuConsts的菜单常量类

public class MenuConsts
{public const string MenuTableName = "menu";public const string MenuTableComment = "菜单表";public const string MenuRootCode = "root";public const string CatalogTypeName = "C";public const string MenuTypeName = "M";public const string FuncTypeName = "F";public const int MaxCodeLength = 64;public const int MinCodeLength = 1;public const int MaxParentCodeLength = 64;public const int MinParentCodeLength = 1;public const int MaxNameLength = 32;public const int MinNameLength = 1;public const int MaxTypeLength = 8;public const int MinTypeLength = 1;public const int MaxIconLength = 32;public const int MaxUrlAddressLength = 64;public const int MaxComponentAddressLength = 64;
}

上面已经对用户、角色和菜单领域创建了相应的聚合根,相关的业务逻辑以聚合根和实体中方法来实现
解决方案的目录结构现如下

在下一章节中,使用仓储作为领域模型和数据模型的桥梁,将领域模型持久化到数据库中的数据模型中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/884279.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P1896 [SCOI2005] 互不侵犯(状态压缩)

位运算符好麻烦,没打括号被卡了半天 #include<iostream> #define int long long using namespace std; int f[12][100][1<<11]; int s[1<<11]; int num[1<<11]; signed main(){int n,k;cin>>n>>k;int cnt=0;for(int i=0;i<(1<<…

200N03-ASEMI豆浆机专用MOS管200N03

200N03-ASEMI豆浆机专用MOS管200N03编辑:ll 200N03-ASEMI豆浆机专用MOS管200N03 型号:200N03 品牌:ASEMI 封装:TO-252 批号:最新 最大漏源电流:200A 漏源击穿电压:30V RDS(ON)Max:1.8mΩ 引脚数量:3 芯片个数: 沟道类型:N沟道MOS管、中低压MOS管 漏电流:ua 特性:…

第七章-收益归因:Brinson模型

例子 现有一个投资组合,其基准组合为:70%中证800,20%债券,10%现金,如下:基金经理主动投资,对资产权重进行调整。假设基准组合收益率为\(r\)。若是看好股票,那就多配置一些股票,调整中证800权重为\(w_1^{\prime}\)。则收益调整为(\(w_1^{\prime}-w)(r_1-r)\)。会有两种情…

我用GPT干什么

有几天没有更新文章了,一方面因为感觉GPT出来了,写什么都不香了,非得写点关于GPT的才有资格出来和大家见面;另一方面,确实最近一段时间也在全面拥抱GPT:学习,总结,思考。所以今天就想随心所欲地说说GPT使用感受。其实GPT出来没多长时间,就注册账号,并体验了网页版,更…

金融期权

金融期权品种一览上海证券交易所品种 行权方式 上市日期华夏上证50ETF期权 欧式 2015-02-09华泰沪深300ETF期权 欧式 2019-12-23南方中证500ETF期权 欧式 2022-09-19华夏上证科创板50ETF期权 欧式 2023-06-05易方达上证科创板50ETF期权 欧式 2023-06-05深圳证券交易所品种 行权…

从ClickHouse中流式查询大数据

提示:本篇不适合手机阅读,主要为了提供代码实现。为了减速少大的Excel文件在内存中驻留,使用流的方式,边查询边组装,边下载文件相对来说是一个好的方式,下面是基于ClickHouse数据源的方式,下载100万条记录的处理方式,本地测试,内存只有100多M的使用,下载完后就会释放…

Axure9下载完美汉化及破解

一)Axure9下载 复制链接到浏览器,复制后需删除作者后面的部份才能打开!Windows系统: https://pan.baidu.com/s/1CF-lpmzzaevUX2lCZUmmiAMac新系统(M芯片) https://pan.baidu.com/s/1k73IJBAdlwUL1YPPRWXchA 提取码: 1323 注:M1版本自带破解,仅需汉化即可Mac旧系统(int…

单视图度量(Single View Metrology)总结

前面我们介绍了标定相机,并且我们知道,如果我们能够确定三维点的坐标以及标定相机的参数,就能够确定它在图像平面的位置。那我们现在思考一个问题,如果我们知道图像平面的坐标以及标定相机的参数,我们能够确定这个三维点的位置吗? 答案是否定的。因为一般情况下,三维点可…

简说VS中的.http

在VS17.5中,加入了一个.http文件,应该和你想的一样,就是一个存放请求的文件,关键是他能执行请求,很像curl。不过市面上相似的工具有很多,比如Postman就做的很好了,那为什么VS还要引入呢?我的简单理解,虽然Postman好用,但毕竟和VS中的源码是分离的,即使Postman等工具…

P1119 灾后重建

链接 https://www.luogu.com.cn/problem/P1119 题目知识点floyd算法思路看题解,讲的差不多,本篇就是记录下写过的题。 唯一要注意的就是当遍历k(本代码用cnt代替)时,ij都要从0取到n-1。代码 #define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<algorithm&g…

库卡机器人维修伺服电机刹车故障维修

库卡机器人作为工业自动化领域的佼佼者,其伺服电机系统的稳定性和可靠性对于生产线的顺畅运行至关重要。然而,在实际应用中,KUKA工业机械手伺服电机刹车故障时有发生,这不仅会影响机器人的正常运行,还可能对生产安全造成威胁。 ,kuka机器人电机刹车故障通常表现为以下几种…

市场上的各种指数

上证指数系列 由上海证券交易所编制,重点指数如下(数据截至2024-07-06):指数名称 指数代码 收盘 成交额(亿元) 平均股本(亿股) 静态市盈率上证指数 000001 2976.3 2793.62 21.37 12.74科创50 000688 732.06 223.94 16.28 28.55上证180 000010 7754.99 1184.8 145.84 11.24上…