【ABP】项目示例(4)——领域服务
领域服务
在上一章节中,已经完成了仓储的设计,在这一章节中,实现领域服务,即业务的核心逻辑
领域服务主要处理特定领域的业务逻辑,对内协调和整合聚合根与各个实体的业务关系,对外作为业务的边界,供应用服务组合来提供完整复杂的功能
规约
在名称为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);
}
解决方案的目录结构现如下

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

浙公网安备 33010602011771号