Article directory
Create business users
Different from the authentication user IdentityUser of the identity management module (Identity module), the business user (BusinessUser) is a domain model around the definition of "user" in the business system. For example: in a hospital system, business users can be doctors, nurses, and patients; in an OA system, business users can be employees, administrators, customers, etc.
Business users and authentication users are associated by a synchronization mechanism, and business users are associated and synchronized with authentication users through the Synchronizer of DistributedEvent.
In the Health business module, two types of business users are defined:
Client: client;
Employee: Employee.
These business users inherit from HealthUser, which is the base class of business users and contains basic information of business users, such as name, gender, date of birth, ID number, etc. And it is necessary to implement the IUpdateUserData interface, so as to update the basic information of the business user when synchronizing the authentication user information.
Employee contains job number, job title, profile and other information. Its domain model is defined as follows:
public class Employee : HealthUser<Guid>, IUser, IUpdateUserData
{
[StringLength(12)]
public string EmployeeNumber { get; set; }
[StringLength(64)]
public string EmployeeTitle { get; set; }
public string Introduction { get; set; }
...
}
Client contains customer number, height, weight, marital status and other information. Its domain model is defined as follows:
public class Client : HealthUser<Guid>, IUser, IUpdateUserData
{
//unique
[StringLength(12)]
public string ClientNumber { get; set; }
public string ClientNumberType { get; set; }
[Range(0.0, 250.0)]
public double? Height { get; set; }
[Range(0.0, 1000.0)]
public double? Weight { get; set; }
public string Marriage { get; set; }
public string Status { get; set; }
}
Create a business user synchronizer
Taking Client as an example, ClientLookupService is a query service for business users. Its base class UserLookupService defines the query interface for associated users, including query by ID, query by user name, query by organizational structure, and query by user relationship.
Create ClientLookupService, the code is as follows
public class ClientLookupService : UserLookupService<Client, IClientRepository>, IClientLookupService
{
public ClientLookupService(
IClientRepository userRepository,
IUnitOfWorkManager unitOfWorkManager)
: base(
userRepository,
unitOfWorkManager)
{
}
protected override Client CreateUser(IUserData externalUser)
{
return new Client(externalUser);
}
}
The synchronizer subscribes to the distributed event EntityUpdatedEto. When the authenticated user is updated, the synchronizer will update the basic information of the business user.
Create ClientSynchronizer, the code is as follows
public class ClientSynchronizer :
IDistributedEventHandler<EntityUpdatedEto<UserEto>>,
ITransientDependency
{
protected IClientRepository UserRepository { get; }
protected IClientLookupService UserLookupService { get; }
public ClientSynchronizer(
IClientRepository userRepository,
IClientLookupService userLookupService)
{
UserRepository = userRepository;
UserLookupService = userLookupService;
}
public async Task HandleEventAsync(EntityUpdatedEto<UserEto> eventData)
{
var user = await UserRepository.FindAsync(eventData.Entity.Id);
if (user != null)
{
if (user.Update(eventData.Entity))
{
await UserRepository.UpdateAsync(user);
}
}
}
}
Create a business user application service
Take Employee as an example
Create EmployeeAppService in the application layer, where we implement the operation of adding, deleting, modifying and checking business users.
EmployeeAppService inherits from CrudAppService, which is the base class for CRUD provided by the ABP framework. Its base class defines the interfaces for CRUD, including GetAsync, GetListAsync, CreateAsync, UpdateAsync, DeleteAsync, etc.
OrganizationUnit provides the query basis for the query interface of business users to query by organizational structure. OrganizationUnitAppService is injected into EmployeeAppService.
public class EmployeeAppService : CrudAppService<Employee, EmployeeDto, Guid, GetAllEmployeeInput, CreateEmployeeInput>, IEmployeeAppService
{
private readonly IOrganizationUnitAppService organizationUnitAppService;
}
increase
Create the CreateWithUserAsync method for creating business users.
public async Task<EmployeeDto> CreateWithUserAsync(CreateEmployeeWithUserInput input)
{
var createdUser = await identityUserAppService.CreateAsync(input);
await CurrentUnitOfWork.SaveChangesAsync();
var currentEmployee = await userLookupService.FindByIdAsync(createdUser.Id);
ObjectMapper.Map(input, currentEmployee);
var updatedEmployee = await Repository.UpdateAsync(currentEmployee);
var result = ObjectMapper.Map<Employee, EmployeeDto>(updatedEmployee);
if (input.OrganizationUnitId.HasValue)
{
await organizationUnitAppService.AddToOrganizationUnitAsync(
new UserToOrganizationUnitInput()
{ UserId = createdUser.Id, OrganizationUnitId = input.OrganizationUnitId.Value });
}
return result;
}
delete
The default implementation of the delete interface is provided by CrudAppService without rewriting.
change
Create an UpdateWithUserAsync method for updating business users.
public async Task<EmployeeDto> UpdateWithUserAsync(CreateEmployeeInput input)
{
var currentEmployee = await userLookupService.FindByIdAsync(input.Id);
if (currentEmployee == null)
{
throw new UserFriendlyException("没有找到对应的用户");
}
ObjectMapper.Map(input, currentEmployee);
var updatedEmployee = await Repository.UpdateAsync(currentEmployee);
var result = ObjectMapper.Map<Employee, EmployeeDto>(updatedEmployee);
return result;
}
check
The interface for querying a single entity is provided by CrudAppService by default and does not need to be rewritten.
Query collection:
Taking Employee as an example, the input parameters required by the query interface are:
OrganizationUnitId: Query users by organizational structure
IsWithoutOrganization: Query users who do not belong to any organizational structure
EmployeeTitle: Query users by professional title
Create GetAllEmployeeInput, the code is as follows
public class GetAllEmployeeInput : PagedAndSortedResultRequestDto
{
public string EmployeeTitle { get; set; }
public Guid? OrganizationUnitId { get; set; }
public bool IsWithoutOrganization { get; set; }
}
Override CreateFilteredQueryAsync
protected override async Task<IQueryable<Employee>> CreateFilteredQueryAsync(GetAllEmployeeInput input)
{
var query = await ReadOnlyRepository.GetQueryableAsync().ConfigureAwait(continueOnCapturedContext: false);
if (input.OrganizationUnitId.HasValue && !input.IsWithoutOrganization)
{
var organizationUnitUsers = await organizationUnitAppService.GetOrganizationUnitUsersAsync(new GetOrganizationUnitUsersInput()
{
Id = input.OrganizationUnitId.Value
});
if (organizationUnitUsers.Count() > 0)
{
var ids = organizationUnitUsers.Select(c => c.Id);
query = query.Where(t => ids.Contains(t.Id));
}
else
{
query = query.Where(c => false);
}
}
else if (input.IsWithoutOrganization)
{
var organizationUnitUsers = await organizationUnitAppService.GetUsersWithoutOrganizationAsync(new GetUserWithoutOrganizationInput());
if (organizationUnitUsers.Count() > 0)
{
var ids = organizationUnitUsers.Select(c => c.Id);
query = query.Where(t => ids.Contains(t.Id));
}
else
{
query = query.Where(c => false);
}
}
query = query.WhereIf(!string.IsNullOrEmpty(input.EmployeeTitle), c => c.EmployeeTitle == input.EmployeeTitle);
return query;
}
So far, we have completed the implementation of adding, deleting, modifying and checking functions for business users.
create controller
Create EmployeeController in the HttpApi project, the code is as follows:
[Area(HealthRemoteServiceConsts.ModuleName)]
[RemoteService(Name = HealthRemoteServiceConsts.RemoteServiceName)]
[Route("api/Health/employee")]
public class EmployeeController : AbpControllerBase, IEmployeeAppService
{
private readonly IEmployeeAppService _employeeAppService;
public EmployeeController(IEmployeeAppService employeeAppService)
{
_employeeAppService = employeeAppService;
}
[HttpPost]
[Route("CreateWithUser")]
public Task<EmployeeDto> CreateWithUserAsync(CreateEmployeeWithUserInput input)
{
return _employeeAppService.CreateWithUserAsync(input);
}
[HttpDelete]
[Route("Delete")]
public Task DeleteAsync(Guid id)
{
return _employeeAppService.DeleteAsync(id);
}
[HttpPut]
[Route("UpdateWithUser")]
public Task<EmployeeDto> UpdateWithUserAsync(CreateEmployeeInput input)
{
return _employeeAppService.UpdateWithUserAsync(input);
}
[HttpGet]
[Route("Get")]
public Task<EmployeeDto> GetAsync(Guid id)
{
return _employeeAppService.GetAsync(id);
}
[HttpGet]
[Route("GetAll")]
public Task<PagedResultDto<EmployeeDto>> GetAllAsync(GetAllEmployeeInput input)
{
return _employeeAppService.GetAllAsync(input);
}
}
test
run project
On the web side, enter the organization
We create several departments at will, as shown in the following figure:
Create several employee users and assign them to "R&D Group A" and "R&D Group B",
Query by organizational structure
By clicking on different organizational structures, you can view different users:
Users in "R&D Group A":
Users in "R&D Group B":
Users in "unassigned":
Query by job title
Click "Filter" in a certain group, select job title-intermediate, and click Query
All employees whose job title is mid-level will be queried
The message Payload of the combined query is as follows:
In the next chapter, we will implement the general query application layer base class, so that query by organization can be applied to all business entities.