在网上银行系统中使用RxJs库的功能

引言


设计现代的在线银行系统是一项相当复杂的任务。 同时,用于开发应用程序客户端部分的许多任务与处理大量同时来自多个信息源的数据的过程相关。 来自远程银行系统(RBS),即时消息服务,各种信息服务的数据应在此处和现在进行实时接收和处理。 为了解决这种问题,当今广泛使用了反应式编程方法。

广义上的术语“反应式编程”是指应用程序的这种组织,其中,由于处理数据流的状态而在系统中传播更改。 此方法的重要问题是呈现信息流的简便性和响应呈现结果异步处理期间发生的错误的可能性。

从狭义上讲,响应式Web UI编程可能意味着使用现成的开发人员工具,例如RxJs库。 该库使用Observable对象提供数据序列的离散表示形式,该对象充当以一定间隔进入应用程序的信息源。

在为小型企业设计在线银行的Web界面示例中考虑使用库的功能。 开发UI时,我们将Google Angular 6平台与内置的RxJs库版本6配合使用。

响应式UI设计任务


对于用户而言,Internet Bank中的大多数操作通常分为三个阶段:

  • 从列表中选择必要的操作,例如偿还贷款或补充帐户;
  • 相应表格的部分填写(付款明细由组织名称或用户输入的收款人姓名自动填写);
  • 使用SMS消息或电子签名自动确认操作。

从开发人员的角度来看,这些阶段的实现包括以下任务的解决方案:

  • 检查苏格兰皇家银行系统的状况,确保数据与清单中各项业务的相关性;
  • 填写表单时,对数据流进行异步处理,包括用户输入并从信息消息服务(例如,银行的名称,TIN和BIC)接收的数据;
  • 确认填写的表格;
  • 自动保存表格中的数据。

检查RBS系统的状态


从RB系统获取相关数据的过程(例如,有关信贷额度或付款订单状态的信息)包括两个阶段:

  • 检查数据可用性状态;
  • 接收更新的数据。

要检查数据的当前状态,请在一定时间段内向系统的API发出请求,直到收到有关数据就绪的响应为止

RB系统有几种可能的答案:

  • {empty:true}-数据尚未准备好;
  • 客户端可以接收更新的数据;

{ empty: false // -   } 

  • 一个错误。

结果,获得相关数据的形式为:

 const MIN_TIME = 2000; const MAX_TIME = 60000; const EXP_BASE = 1.4; request() //     .pipe( expand((response, index) => { const delayTime = Math.min(MIN_TIME * Math.pow(EXP_BASE, index), MAX_TIME); return response.empty ? request().pipe(delay(delayTime)) : EMPTY; }), last() ) .subscribe((response) => { /** -  */ }); 


让我们逐步分析:

  1. 我们发送一个请求。 要求()
  2. 答案就扩大了。 Expand是一个RxJS语句,它为内部和外部Observable的每个下一个警报递归地在其块中重复代码,直到线程宣布成功完成为止。 因此,为了完成流程,有必要返回这样一个Observable,以便没有单个next-EMPTY。
  3. 如果响应为{empty:true},那么我们会在一定的时间延迟(delayTime)之后发出第二个请求。 为了不使服务器超载请求,我们为每个新请求增加了ping的时间间隔。
  4. 如果在下一个请求期间有其他响应,请停止ping(返回EMPTY)并将最后一个请求的结果返回给订户(last()运算符)。
  5. 收到答案后,我们将得到结果并进行处理。 表单的对象将进入subscribe:

 { empty: false // -   } 


反应形式


考虑使用来自Angular框架的ReactiveForms库设计付款文档的反应式Web表单的任务。

库FormControl,FormGroup和FormArray的三个基类允许您使用对表单字段的声明性描述,设置字段的初始值以及为每个字段设置验证规则:

 this.myForm = new FormGroup({ name: new FormControl('', Validators.required), //          surname: new FormControl('') }); 


对于具有大量字段的表单,习惯上使用FormBuilder服务,该服务允许您使用更紧凑的代码来创建它们

 this.myForm = this.fb.group({ name: ['', Validators.required], surname: '' }); 


在付款订单页面的模板中创建表单后,只需指定指向myForm表单的链接,以及其字段名称和姓氏的名称

 <form [formGroup]="myForm"> <label>Name: <input formControlName="name"> </label> <label>Surname: <input formControlName="surname"> </label> </form> 


最终的设计使您可以根据用户输入并基于应用程序的业务逻辑来生成和跟踪通过表单字段传递的任何信息流。 为此,只需订阅由ValueChanges表单的异步观察器生成的事件

 this.myForm.valueChanges .subscribe(value => { … //     }) 


假设业务逻辑定义了当用户输入收件人的TIN或组织名称时自动填写付款目的地详细信息的要求。 用户在组织的TIN /名称中输入的数据处理代码如下所示:

 this.payForm.valueChanges .pipe( mergeMap(value => this.getRequisites(value)) //       ) .subscribe(requisites => { this.patchFormByRequisites(requisites) //        }) 


验证方式


验证器有两种形式:

  • 同步
  • 异步的。

我们经常遇到同步验证器-这些功能在处理字段时会检查输入的数据。 就反应形式而言:
“同步验证器是一种采用控制形式的函数,如果有错误,则返回真实值,否则返回虚假值。”

 function customValidator(control) { return isInvalid(control.value) ? { code: "mistake", message: "smth wents wrong" } : null; } 


我们将实现一个验证器,该验证器将检查用户是否以一系列文件的形式指示了护照(如果护照先前已被用作身份证明文件的类型):

 function requredSeria(control) { const docType = control.parent.get("docType"); let error = null; if (docType && docType.value === "passport" && !control.value) { error = { code: "wrongSeria", message: "  " } } return error; } 


在这里,我们还引用父表单,并使用它来获取另一个字段的值。 可以返回true作为错误,但是在这种情况下,它决定执行其他操作。 您可以在控件或窗体的错误字段中捕获这些错误消息。 如果该字段具有多个验证器,则可以确切指定哪些验证器未能显示所需的错误消息或调整其他字段的验证。

验证器将添加到表单中,如下所示:

  this.documentForm = this.fb.group({ docType: ['', Validators.required], seria: ['', requredSeria], number: '' }); 


开箱即用,也可以使用几个常见的验证器。 它们全部由Validators类的静态方法表示。 也有一些组成验证器的方法。
一个字段的不正确会立即导致整个表单的无效。 如果表单中至少有一个无效字段,则需要停用某个“确定”按钮时可以使用此方法。 然后全部归结为检查一个条件“ myform.invalid”,如果表单无效,则返回true。

异步验证器有一个区别-返回值​​的类型。 真实值或虚假值必须在承诺或可观察值中传递。

每个控件或每个窗体都有一个状态(mySuperForm.status),可以是“ VALID”,“ INVALID”,“ DISABLED”。 由于使用异步验证器时可能不清楚表单当前处于何种状态,因此存在特殊状态“ PENDING”。 由于这种情况(mySuperForm.status ===“ PENDING”),您可以显示预加载器或进行任何其他表单样式设置。

自动保存


银行软件(软件)的开发涉及使用各种标准文档。 例如,这些是申请表或调查表,可以包含数十个必填字段。 在处理大量文档时,需要自动保存支持才能为用户带来更多便利,因此,如果您失去Internet连接或其他技术问题,则用户先前输入的数据将保留在服务器上的草稿版本中。

这是客户端-服务器体系结构的自动保存过程的主要方面:

  1. 服务器必须按照更改的顺序处理保存请求。 如果您立即向每个更改发送请求,则不能保证之前的请求不会再出现并且不会覆盖新的更改。
  2. 在用户完成输入之前,没有必要向服务器发送大量请求,这足以通过计时来完成。
  3. 如果已经进行了一些较大延迟的更改,并且尚未返回对第一个更改的请求,则无需在第一个请求返回时立即为每个后续更改发送请求。 您只能使用后者,以免发送无关的数据。

第一种情况可以使用concatMap运算符轻松处理。 使用debounceTime可以解决第二种情况,而不会出现任何问题。 第三种逻辑可描述为:

 const lastRequest$ = new BehaviorSubject(null); //   queue$.subscribe(lastRequest$); queue$ .pipe( debounceTime(1000), exaustMap(request$ => request$.pipe( //  ,     map(response => ({request$, response})), //       catchError(() => of(null) //   ) ) .subscribe(({request$, response}) => { if (lastRequest$.value !== request$) { queue$.next(lastRequest$.value); //     } }); 


它保留在saveQueue $中以发送请求。 请注意,存在exaustMap运算符而不是concatMap。 该操作员必须忽略外部Observable的所有通知,直到内部Observable完成其观察(“已编译”)为止。 但是在我们的情况下,如果在请求期间将有新的通知队列,我们​​必须采用最后一个通知,并丢弃其余的通知。 exaustMap将删除所有内容,包括最后一个。 因此,我们将最后一个通知保存在BehaviorSubject中,并在预订中,如果当前已完成的请求与最后一个请求不同,则将最后一个请求再次放入队列。

还值得注意的是,忽略了使用catchError语句实现的查询过程中的错误。 您可以编写更复杂的错误处理,并向用户发送保存时发生错误的通知。 但是其实质是,当流中发生错误时,不应关闭流,就像发生错误和完整通知时一样。

结论


使用RxJS库的当今响应式编程技术的开发水平,使您可以为在线银行系统创建完整的客户端应用程序,而无需花费额外的人力来组织与远程银行系统的高负载接口的交互。

初次接触RxJS甚至可以吓an那些面临实现设计模式“观察者”的库“复杂性”的经验丰富的开发人员。 但是,也许克服了这些困难,将来,RxJS将成为实时解决异构数据流异步处理问题中必不可少的工具。

Source: https://habr.com/ru/post/zh-CN425897/


All Articles