【ABP】项目示例(4)——领域服务

news/2025/2/25 21:41:12/文章来源:https://www.cnblogs.com/rainseason/p/18642728

领域服务

在上一章节中,已经完成了仓储的设计,在这一章节中,实现领域服务,即业务的核心逻辑
领域服务主要处理特定领域的业务逻辑,对内协调和整合聚合根与各个实体的业务关系,对外作为业务的边界,供应用服务组合来提供完整复杂的功能

规约

在名称为General.Backend.Domain的类库中的Specifications文件夹下,新建名称为SameUserSpecification和SameRoleSpecification的规约类,用于定义和复用相同用户和相同角色的逻辑判断

  • SameUserSpecification
public class SameUserSpecification : Specification<User>
{/// <summary>/// 账号/// </summary>public string Account { get; } = string.Empty;/// <summary>/// 用户名称/// </summary>public string UserName { get; } = string.Empty;public SameUserSpecification(string userName, string account = ""){Account = account;UserName = userName;}public override Expression<Func<User, bool>> ToExpression(){return user => user.Account == Account || user.Name == UserName;}
}
  • SameRoleSpecification
public class SameRoleSpecification : Specification<Role>
{/// <summary>/// 角色编码/// </summary>public string Code { get; } = string.Empty;/// <summary>/// 角色名称/// </summary>public string Name { get; } = string.Empty;public SameRoleSpecification(string name, string code = ""){Code = code;Name = name;}public override Expression<Func<Role, bool>> ToExpression(){return user => user.Code == Code || user.Name == Name;}
}

服务

在Services文件夹下分别新建名称为UserService、RoleService和MenuService的领域服务类

  • UserService
public class UserService : DomainService
{private readonly IUserRepository _userRepository;public UserService(IUserRepository userRepository){_userRepository = userRepository;}public async Task<User> LoginCheckAsync(string account,string password){var user = await _userRepository.FindUserByAccountAsync(account);if (user == null){throw new UserFriendlyException("用户不存在");}if (user.IsFrozen == FrozenStatus.Frozen){throw new UserFriendlyException("用户已被冻结");}var checkResult = user.CheckPassword(password);await _userRepository.UpdateAsync(user);if (!checkResult){throw new UserFriendlyException("用户密码错误");}return user;}public async Task<User> AddAsync(string account,string password,string name,string? contact = null,string? address = null){var sameUsers = await _userRepository.GetSameUserAsync(new SameUserSpecification(name, account));if (sameUsers.Count > 0){if (sameUsers.Any(user => user.Account == account)){throw new UserFriendlyException("已存在相同账号的用户");}if (sameUsers.Any(user => user.Name == name)){throw new UserFriendlyException("已存在相同名称的用户");}}var user = new User(GuidGenerator.Create(),account,password,name,contact,address);return user;}public async Task<User> ModifyAsync(Guid id,string password,string name,string? contact = null,string? address = null){var user = await _userRepository.FindAsync(id);if (user == null){throw new UserFriendlyException("用户不存在");}if (user.Account == UserConsts.AdminAccount){throw new UserFriendlyException("不可操作管理员用户");}var sameUsers = await _userRepository.GetSameUserAsync(new SameUserSpecification(name));if (sameUsers.Any(item => item.Name == name && item.Id != id)){throw new UserFriendlyException("已存在相同名称的用户");}user.SetName(name);user.SetPassword(password);user.Contact = contact;user.Address = address;return user;}public async Task<User> UnfreezeAsync(Guid id){var user = await _userRepository.FindAsync(id);if (user == null){throw new UserFriendlyException("用户不存在");}user.UnFrozen();return user;}public void BindRoles(User user, List<Guid> roleIds){var userRoles = new List<UserRole>();roleIds.ForEach(roleId =>{var userRole = new UserRole(GuidGenerator.Create(),user.Id,roleId);userRoles.Add(userRole);});user.SetRoles(userRoles);}
}
  • RoleService
public class RoleService : DomainService
{private readonly IRoleRepository _roleRepository;public RoleService(IRoleRepository roleRepository){_roleRepository = roleRepository;}public async Task<Role> AddAsync(string code,string name,string? remark){var sameRoles = await _roleRepository.GetSameRoleAsync(new SameRoleSpecification(name, code));if (sameRoles.Count > 0){if (sameRoles.Any(role => role.Code == code)){throw new UserFriendlyException("已存在相同编码的角色");}if (sameRoles.Any(role => role.Name == name)){throw new UserFriendlyException("已存在相同名称的角色");}}var role = new Role(GuidGenerator.Create(),code,name,remark);return role;}public async Task<Role> ModifyAsync(Guid id,string name,string? remark){var role = await _roleRepository.FindAsync(id);if (role == null){throw new UserFriendlyException("角色不存在");}var sameRoles = await _roleRepository.GetSameRoleAsync(new SameRoleSpecification(name));if (sameRoles.Any(item => item.Name == name && item.Id != id)){throw new UserFriendlyException("已存在相同名称的角色");}role.SetName(name);role.Remark = remark;return role;}public void BindMenus(Role role, List<string> menuCodes){var roleMenus = new List<RoleMenu>();menuCodes.ForEach(menuCode =>{var roleMenu = new RoleMenu(GuidGenerator.Create(),role.Id,menuCode);roleMenus.Add(roleMenu);});role.SetMenus(roleMenus);}
}
  • MenuService
public class MenuService : DomainService
{private readonly IMenuRepository _menuRepository;private readonly IRoleRepository _roleRepository;private readonly IRepository<RoleMenu> _roleMenuRepository;public MenuService(IMenuRepository menuRepository,IRoleRepository roleRepository,IRepository<RoleMenu> roleMenuRepository){_menuRepository = menuRepository;_roleRepository = roleRepository;_roleMenuRepository = roleMenuRepository;}/// <summary>/// 获取用户权限缓存/// </summary>/// <param name="user"></param>/// <returns></returns>public async Task<UserPermissionCacheItem> GetPermissionAsync(User user){var userPermissionCacheItem = new UserPermissionCacheItem{UserId = user.Id,UserAccount = user.Account,UserName = user.Name};var menuModules = new List<Menu>();var account = user.Account);if (account == UserConsts.AdminAccount){menuModules = await _menuRepository.GetListAsync();userPermissionCacheItem.Role = RoleConsts.AdminRoleCode;userPermissionCacheItem.RoleName = RoleConsts.AdminRoleName;}else{var roleIds = user.UserRoles.Select(rel => rel.RoleId);var roles = await _roleRepository.GetListAsync(role => roleIds.Contains(role.Id));var roleMenus = await _roleMenuRepository.GetListAsync(rel => roleIds.Contains(rel.RoleId));var menuCodes = roleMenus.Select(rel => rel.MenuCode).Distinct();menuModules = await _menuRepository.GetListAsync(menu => menuCodes.Contains(menu.Code));userPermissionCacheItem.Role = string.Join(",", roles.Select(x => x.Code).ToList());userPermissionCacheItem.RoleName = string.Join(",", roles.Select(x => x.Name).ToList());}userPermissionCacheItem.MenuRouters = await GetMenuRouter(menuModules);foreach (var menuModule in menuModules){userPermissionCacheItem.Modules.Add(menuModule.Code);}return userPermissionCacheItem;}/// <summary>/// 获取路由菜单/// </summary>/// <param name="menus"></param>/// <returns></returns>public async Task<List<MenuRouterCacheItem>> GetMenuRouter(List<Menu> menus){var menuRouterCacheItems = new List<MenuRouterCacheItem>();menus = menus.Where(x => x.Type == MenuConsts.CatalogTypeName || x.Type == MenuConsts.MenuTypeName).OrderBy(y => y.Sort).ToList();//获取部分勾选的菜单的目录列表var parentCodes = menus.Where(x => x.Type == MenuConsts.MenuTypeName).Select(y => y.ParentCode);var exceptCodes = parentCodes.Except(menus.Select(x => x.Code));if (exceptCodes.Any()){var partMenus = await _menuRepository.GetListAsync(x => exceptCodes.Contains(x.Code));menus.AddRange(partMenus);menus = menus.OrderBy(x => x.Sort).ToList();}var menuTree = BuildTreeMenus(menus);menuRouterCacheItems = BuildRouter(menuTree);return menuRouterCacheItems;}/// <summary>/// 构建前端菜单树/// </summary>/// <param name="menus"></param>/// <returns></returns>private static List<Menu> BuildTreeMenus(List<Menu> menus){var result = new List<Menu>();var queue = new Queue<Menu>();var rootMenu = Menu.BuildRoot();queue.Enqueue(rootMenu);while (queue.Count > 0){var parentMenu = queue.Dequeue();var childMenus = menus.Where(x => x.ParentCode == parentMenu.Code).ToList();parentMenu.SetSubMenu(childMenus);foreach (var childMenu in childMenus){queue.Enqueue(childMenu);}}result = rootMenu.SubMenu;return result;}/// <summary>/// 构建路由/// </summary>/// <param name="menus"></param>/// <returns></returns>private static List<MenuRouterCacheItem> BuildRouter(List<Menu> menus){var result = new List<MenuRouterCacheItem>();foreach (var menu in menus){var menuRouterCacheItem = new MenuRouterCacheItem{Name = menu.Name,Hidden = false,Path = menu.UrlAddress,Component = !string.IsNullOrEmpty(menu.ComponentAddress) && menu.Type == MenuConsts.MenuTypeName ? menu.ComponentAddress : "Layout"};var meta = new Meta(menu.Name, menu.Icon);menuRouterCacheItem.Meta = meta;var subMenu = menu.SubMenu;if (subMenu != null && menu.Type == MenuConsts.CatalogTypeName){menuRouterCacheItem.AlwaysShow = true;menuRouterCacheItem.Redirect = "noRedirect";menuRouterCacheItem.Children = BuildRouter(subMenu);}result.Add(menuRouterCacheItem);}return result;}
}

在General.Backend.Domain中新建名称为CacheItems的文件夹,存放缓存对象类

/// <summary>
/// 菜单路由
/// </summary>
public class MenuRouterCacheItem
{/// <summary>/// 路由名称/// </summary>public string Name { get; set; } = string.Empty;/// <summary>/// 路由地址/// </summary>public string? Path { get; set; }/// <summary>/// 是否隐藏路由,当设置true的时候该路由不会在侧边栏出现/// </summary>public bool Hidden { get; set; }/// <summary>/// 组件地址/// </summary>public string Component { get; set; } = string.Empty;/// <summary>/// 重定向地址,当设置noRedirect的时候该路由在面包屑导航中不可被点击/// </summary>public string Redirect { get; set; } = string.Empty;/// <summary>/// 当你一个路由下面的children声明的路由大于1个时,自动会变成嵌套的模式--如组件页面/// </summary>public bool AlwaysShow { get; set; }/// <summary>/// 菜单内容/// </summary>public Meta Meta { get; set; }/// <summary>/// 子菜单路由/// </summary>public List<MenuRouterCacheItem> Children { get; set; } = [];
}/// <summary>
/// 菜单内容
/// </summary>
public class Meta
{public Meta(string title, string? icon){Title = title;Icon = icon;}/// <summary>/// 路由在侧边栏和面包屑中展示的名称/// </summary>public string Title { get; set; } = string.Empty;/// <summary>/// 路由的图标/// </summary>public string? Icon { get; set; }
}

对IRepositories文件夹下的IUserRepository和IRoleRepository仓储接口增加业务方法定义

  • IUserRepository
/// <summary>
/// 用户仓储
/// </summary>
public interface IUserRepository : IBaseRepository<User, Guid>
{public Task<User?> FindUserByAccountAsync(string account, bool include = true, CancellationToken cancellationToken = default);public Task<List<User>> GetSameUserAsync(SameUserSpecification specification, CancellationToken cancellationToken = default);
}
  • IRoleRepository
/// <summary>
/// 角色仓储
/// </summary>
public interface IRoleRepository : IBaseRepository<Role, Guid>
{public Task<List<Role>> GetSameRoleAsync(SameRoleSpecification specification, CancellationToken cancellationToken = default);
}

解决方案的目录结构现如下

在下一章节中,实现应用服务

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

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

相关文章

YOLOv10 解析与地平线 征程 6模型量化

一,YOLOv10 解析 1.简介 近些年来,研究人员对 YOLO 的架构设计、优化目标、数据增强策略等进行了探索,取得了显著进展。然而,后处理对非极大值抑制(NMS)的依赖阻碍了 YOLO 的端到端部署,并对推理延迟产生不利影响。此外,YOLO 中各个组件的设计缺乏全面彻底的检查,导致…

SemanticKernel之Chat

去年写过几过几篇关于SemanticKernel的文章,由于正式发布的版本与之前的版本变化较大,加上前的东京《生成式AI应用开发》活动,想把演示的Demo逐一分享出来,的以再次开启SemanticKernel系统。下面是一个Chat的例子,用户提问,如果本地有固定数据能对应,直接返回,如果没有…

7、添加特效

去除画面logo 复制一份 拖动模糊特效到复制的片段中 右键分离音频或者快捷键【ctrl+shift+s】 删除音频 添加原创特效,特效随机 有音乐的去除音乐 适当拖动放大 调整透明度 开幕特效 三秒 调整参数后,复制双份 画面特效和人物特效二选一 画面特效需要修改参数

Prompt升级

前两篇关于Prompt的文章分别从提示词规则建议和具体框架角度说明了一下Prompt的使用技巧,接下来可以说是对框架式提示词的的进一步升级——结构化提示词。 结构化定义: 对信息进行组织,使其遵循特定的模式和规则,从而方便有效理解信息。 结构化提示词语法:这个结构支持 Mark…

Spring复习-事务

事务概述 Spring事务编程概述 事务是开发中必不可少的东西,使用JDBC开发时,我们使用connnection对事务进行控制,使用MvBatis时,我们使用SqlSession对事务进行控制,缺点显而易见,当我们切换数据库访问技术时,事务控制的方式总会变化,Spring 就将这些技术基础上,提供了统…

Prompt进阶

在Prompt入门里,我们分享了OpenAI官方给出的提示词建议,但这些建议基本上是指导性的,方向性的,概念性的,虽然对我们编写提示词有很大帮助,但究竟我们的提示词好还是不好,效率怎么性,很大程度上要看每个人的理解,和提示词输出。那么有没有一个可操作性的,方法论的,谁…

P1174 打砖块

链接 https://www.luogu.com.cn/problem/P1174 思路刚开始的思路:设置dp[i][j]:前i列使用了j颗子弹,那么递推dpi,j=max(dpi,j,dpi-1,k+maxj-k),然后统计在第i列使用了j-k颗子弹会多出来多少颗,把这些遍历加到前面,见代码。喜提70pts。但是搞不懂哪里错了。 看了评论区的dp:70…

redis - [06] redis-benchmark性能测试

题记部分 001 || 参数含义 002 || 测试100个并发,100000个请求 启动redis-server redis-server /etc/redis.conf 进行性能测试 redis-benchmark -h localhost -p 6379 -c 100 -n 100000

百万架构师第四十三课:Nginx:Nginx 应用实战|JavaGuide

百万架构师系列文章阅读体验感更佳 原文链接:https://javaguide.net 公众号:不止极客 课程目标:Nginx 反向代理功能配置 Nginx 负载均衡实战 Nginx 动静分离配置 Nginx 配置文件分析 Nginx 多进程模型原理 Nginx 高可用集群实战反向代理​ 我们把请求发送到 proxy (代理服务…

大三下每日打卡003

今天配置了python的虚拟环境anaconda想尝试一下yolov8来实现识别

需求评审

需求评审是产品经理日常会议的形式之一,也是一个“公开处刑”的时刻。这篇文章,我们看看作者分享的如何做好一次需求评审的经验,供大家参考。前段时间有小伙伴留言,想聊一下关于需求评审面向不同角色如何处理,以及产品不同生命周期产品工作上有什么区别。我结合自己工作经…

牛客题解 | 对称的二叉树_1

牛客题库题解题目 题目链接 题目的主要信息:判断一棵二叉树是否是镜像,即判断二叉树是否是轴对称图形轴对称:非轴对称:举一反三: 学习完本题的思路你可以解决如下题目: BM28. 二叉树的最大深度 BM29. 二叉树中和为某一值的路径(一) BM32. 合并二叉树 BM33. 二叉树的镜像…