65.9K
CodeProject 正在变化。 阅读更多。
Home

[TinyERP:企业应用程序的 SPA]添加新员工

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2018年11月20日

Apache

5分钟阅读

viewsIcon

3546

概述。如“管理员工 - 第二部分”中所述,查询员工列表很容易,因为它们已经提前准备好了。所有必要的准备步骤都发生在“添加/更新员工信息”中。让我们先来看“添加员工”。在 staffs 中添加以下行。

概述

“管理员工 - 第二部分”中所述,查询员工列表很容易,因为它们已经提前准备好了。

所有必要的准备步骤都发生在“添加/更新员工信息”中。

让我们先来看“添加员工”。

将以下行添加到 staffs.html 中,作为 page-header 的子元素(放在 page-header 结束标签之前)。

<page-actions class="pull-right" [actions]=model.actions></page-actions>

这是定义每个页面操作列表的组件。我们也有模型属性的操作。所以,让我们将它添加到 StaffsModel 类中。

 public actions:Array<PageAction>=[];

同时添加 addAction 方法。

public addAction(action: PageAction):void{
        this.actions.push(action);
    }

此函数从 Staffs.ts 文件调用,用于添加“创建员工”按钮,将此行添加到创建 StaffsModel 的新实例之后。

self.model.addAction(new PageAction("",()=>{self.onAddNewStaffClicked();}).setText("Add new Staff"));

这段代码的意思是,我们将为当前页面添加一个新的页面操作项,当点击该项时,它将触发 onAddNewStaffClicked 方法。

然后我们导航到 addNewStaff 页面。

private onAddNewStaffClicked():void{
        this.navigate(routes.addNewStaff.name);
    }

不用太担心路由。这是一个预定义的从“<modules>/hrm/_share/config/routes.ts”中获取的对象。在此文件中,我们定义了路由列表和相应的名称。所以我们稍后使用它,而不是使用魔术数字。我始终同意使用“routes.staffs.addNew.path”比使用“/hrm/addNewStaff”字符串更好。

随着新添加的 PageAction,"Staffs" 的 UI 在 "Manage Staffs" 的右侧出现了一个新按钮。

注册路由并将 "AddStaff" 页面添加到 HRMRoute 模块。

import {AddNewStaff} from "./pages/addNewStaff";
let routeConfigs = [
    { path: "", redirectTo: routes.staffs.path, pathMatch: "full" },
    { path: routes.staffs.path, component: Staffs},
    { path: routes.addNewStaff.path, component: AddNewStaff}
];
@NgModule({
    imports: [CommonModule, FormsModule, RouterModule.forChild(routeConfigs), AppCommon],
    exports: [RouterModule, Staffs],
    declarations: [Staffs, AddNewStaff],
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class HrmRoute { }

此代码用作参考。运行它会出错。请改用 GitHub 上的源代码。

并定义 addStaff 页面的 UI。

<page>
    <page-header>Add new Staff</page-header>
    <page-content>
        <horizontal-form>
            <form-text-input [labelText]="'First Name'" [(model)]=model.firstName></form-text-input>
            <form-text-input [labelText]="'Last Name'" [(model)]=model.lastName></form-text-input>
            <form-primary-button [label]="'Save'" (onClick)="onSaveClicked($event)"></form-primary-button>
            <form-default-button [label]="'Cancel'" (onClick)="onCancelClicked($event)"></form-default-button>
        </horizontal-form>
    </page-content>
</page>

这很直接,有一个包含 2 个文本输入字段的表单。还有 2 个按钮,一个用于取消“AddStaff”过程,一个用于创建新员工(保存按钮)。

此页面的 ts 文件如下:

import { Component } from "@angular/core";
import { BasePage } from "@app/common";
import { IStaffService } from "../_share/services/istaffService";
import {AddNewStaffModel} from "./addNewStaffModel";
import {LocalIoCNames} from "../_share/enum";
import routes from "../_share/config/routes";
@Component({
    templateUrl:"src/modules/hrm/pages/addNewStaff.html"
})
export class AddNewStaff extends BasePage<AddNewStaffModel>{
    public model: AddNewStaffModel= new AddNewStaffModel();
    public onSaveClicked():void{
        let service: IStaffService = window.ioc.resolve(LocalIoCNames.IStaffService);
        let self=this;
        service.create(self.model).then(()=>{
            self.navigate(routes.staffs.name);
        });
    }
    public onCancelClicked():void{
        this.navigate(routes.staffs.name);
    }
}

此页面的 TypeScript 文件很简单。它只是创建一个新员工并导航回“管理员工”页面。

在 StaffService 的 create 函数内部,我们进行 REST 调用到远程 API,并将必要的信息发送到 API。

public create(staff: any):Promise{
        let uri="https://:86/api/hrm/staffs";
        let iconnector: IConnector = window.ioc.resolve(IoCNames.IConnector);
        return iconnector.post(uri, staff);
    }

注意:请在运行时将“localhost:86”替换为您的 API 主机。

查看“添加员工”的 API。

一切都始于 TinyERP.HRM 项目中的 StaffHandler 类。

[Route("")]
[HttpPost()]
[ResponseWrapper()]
public void CreateStaff(CreateStaffRequest request) {
	this.Execute<CreateStaffRequest, CreateStaffResponse>(request);
}

在 TinyERP 中,所有操作(例如:创建员工、删除......)都应转换为适当的请求。每个请求都需要包含处理该请求的必要信息(例如身份验证,......)。这在 RESTful Web 服务的无状态规则中有所规定。

我们需要做的是调用“Execute”方法。该方法将 CreateStaffRequest 请求重定向到 IBaseCommandHandler<CreateStaffRequest> 处理程序,由该处理程序处理该请求。目前,我们在“TinyERP.HRM/Command/Bootstrap.cs”类中为该请求注册了处理程序。

container.RegisterTransient<IBaseCommandHandler<CreateStaffRequest, CreateStaffResponse>, StaffCommandHandler>();

这意味着 StaffCommandHandler 类订阅入站的 CreateStaffRequest 并返回 CreateStaffRespone。

默认情况下,普通 API 控制器没有 Execute 方法,我们需要让 StaffHandler 继承自 CommandHandlerController<AggregateType>,AggregateType 在此情况下是“Staff”类。

 public class StaffHandler: CommandHandlerController<TinyERP.HRM.Aggregate.Staff>

在 StaffCommandHandler 内部,我们有一个 Handle 方法,它接收 CreateStaffRequest 作为参数。这就是 CreateStaffRequest 被实际处理的地方。

public CreateStaffResponse Handle(CreateStaffRequest command)
{
	this.Validate(command);
	using (IUnitOfWork uow = this.CreateUnitOfWork<TinyERP.HRM.Aggregate.Staff>()) {
		TinyERP.HRM.Aggregate.Staff staff = new Aggregate.Staff();
		staff.UpdateBasicInfo(command);
		IStaffRepository repository = IoC.Container.Resolve<IStaffRepository>(uow);
		repository.Add(staff);
		uow.Commit();
		staff.PublishEvents();
		return ObjectHelper.Cast<CreateStaffResponse>(staff);
	}
}

看看代码,有些地方你可能不理解或感到困惑,例如:验证,......

好的,不用担心,我无意在一篇文章中讲述所有内容。暂时忽略它们,我主要想向您展示创建新员工的基本流程。在后续文章中,我们将详细介绍每个方面,例如:错误处理,......

在这种情况下,我们只需创建一个 Staff 聚合的新实例,并调用相应的方法来更新基本信息并将其保存到 MSSQL 数据库。

为什么我们不直接设置 Staff 实例的属性,我是说不调用 UpdateBasicInfo。

这与 DDD(领域驱动设计)有关。我不会超出本文的范围。请在我的博客(http://www.tranthanhtu.vn)上搜索其他文章以获取更多信息。

所以,Staff 可以被视为一个领域,除了基本员工信息,我们还可以有其他信息(例如:工作历史、薪资、部门、工作状态,......)。这取决于您如何将应用程序分解成更小的领域。

当我们想更改 Staff 领域的信息时,请准备好适当的请求(或 CQRS 中所说的命令),并将该请求发送到系统。Staff 领域聚合(在此情况下是 Staff 类)将处理该请求并更新相应的信息并发出必要的事件。

staff.PublishEvents();

为什么我们需要这些事件以及它们是什么?

在 CQRS 中,我们有分离的写端和读端。写端是我们存储数据以供未来验证的地方,而读端,我们将反规范化的数据以必要的格式存储,这将减少将来读取这些数据所需的时间。请在我的博客上搜索有关 CQRS 的文章(http://www.tranthanhtu.vn),因为我不会超出本文的范围。

那么,这意味着当我们修改写端的数据时,我们也需要将这些更改更新到读端。

看看 Staff 类,我们可以看到,每次更改 Staff 类的属性时,都会添加一个新事件。

public Staff()
{
	this.AddEvent(new OnStaffCreated(this.Id));
}

internal void UpdateBasicInfo(CreateStaffRequest command)
{
	//....
	this.AddEvent(new OnStaffBasicInforChanged(this.Id, this.FirstName, this.LastName, this.Email));
}

 

这些事件将在 StaffEventHandler 中处理。我们与 StaddCommandHandler 的处理方式类似,所以请自己阅读代码。

运行“添加员工”功能的最终结果如下:

输入新员工的信息。

该员工的数据存储在 MSSQL(写端)数据库中。

并且也存储在读端(使用 mongodb)。

然后,我们可以在“管理员工”页面上看到员工列表。

本文摘要。

  • 应用程序应分解为更小的独立域。
  • 每个操作(修改域数据)都需要转换为请求(或 CQRS 中所说的命令)。
  • 域聚合将订阅并处理适当的请求,然后发出必要的事件。
  • 我们可以监听这些事件并更新读端的数据或执行其他操作(例如:为新注册会员发送激活邮件,......)。
  • 数据以反规范化的格式存储,并且可以快速读取。

有关此部分源代码的更多信息,请参阅 https://github.com/tranthanhtu0vn/TinyERP (在 feature/add_staff 分支中)。

 

感谢阅读,

 

© . All rights reserved.