让ERP的服务更开放! ——用微服务架构搭建的一套基于EBS的API服务系统

8,910 阅读25分钟

1. 源码下载地址

源码链接: https://github.com/samt007/xygerp-api-demo

这是用Spring Cloud微服务架构搭建的一套基于EBS的API服务系统

如对本文有任何的疑问,请联系我:samt007@qq.com

2. Introduction介绍

这是一篇传统ERP系统和基于Java的微服务架构有效结合的技术文档。

传统ERP关注的是企业内部的信息化管理。当ERP系统能将其服务发布出去之后(结合微服务架构),就可以很好实现与第三方系统的无缝对接,同时也可以实现扩展ERP本身的功能。 目标是:让ERP的服务更开放!

2.1 它有什么用?

简单来说,就是:

相当于做一个中间服务平台,把ERP的API做成Web Service与其它系统集成。

下面具体说明它的作用。

1. 如果没有它...

如果没有一个统一的API对接平台,那么ERP和第三方系统做对接,那会是下图所示的结构:

这里写图片描述

从上图可以看出,各个第三方系统分别和ERP做对接,无论是DBLINK还是通过自己的Web Service, 都是杂乱的,没能统一管理的。简单来说就是各自为政,为对接而需要做的事情都是零碎的。

当第三方系统越来越多的时候,那对于日常的运维将会是一个灾难的问题。举个例子,就一个简单的运维:密码修改,需要调整的地方就会很多,也很容易遗漏。

特别指出的是,接口功能的复用方面也是个难题。假设一个查询库存的接口,CRM系统和在线下单系统都可以用的,需要开发2次。

2. 自从有了它...

有了这套统一的API系统之后,ERP系统和别的系统之间的对接就变成了这个结构:

这里写图片描述

所以,有了它,相当于ERP的API都可以通过这服务平台给开发出去,基本上所有的接口可以完成的业务,都可以通过这套服务平台来完成。

可以实现:

  • 对外服务的统一
  • API服务之间可以实现互相调用,并且实现服务取数和处理的逻辑的统一
  • 代码的统一,提高开发效率。特别是comm代码的部分。
  • 提高与第三方系统对接的稳定性:只需要关注该微服务系统的运行稳定性即可。

3. 有哪些实例?

举个例子:

1) 成品进出条码管理系统:

大概这个需求: 成品入库的时候,直接可以用条码枪扫条码或者二维码就可以入库;销售出库的时候,也可以通过刷成品的条码直接进行出库。JIT管理。

通过这个系统的实现逻辑是: 通过EBS的用户名和密码可以登入条码枪上的APP系统。然后,刷条码的时候,通过该Web Service可以产生对应的事务处理!例如完工入库,处理物料搬运单等。

下面是该系统的一些截图

注意:该功能后台API由该微服务提供,前台是安卓的APP

这里写图片描述

2) 与各个第三方系统的集成:

每个企业内部都有各种第三方系统,这些系统或多或少都需要和EBS进行集成。传统的集成方法是通过DBLINK。

但是有这套Web Service系统之后,就可以统一通过该Web Service作为中介,和EBS进行数据的集成交互!

2.2 什么是微服务?

关于它的解析,网上资料很多。 这里引用某位大神的总结(引用:http://blog.51cto.com/ityouknow/1974080),解析得比较到位:

微服务的概念源于2014年3月Martin Fowler所写的一篇文章“Microservices”。

微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立地部署到生产环境、类生产环境等。另外,应尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。

微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。

微服务架构优势

复杂度可控:在将应用分解的同时,规避了原本复杂度无止境的积累。每一个微服务专注于单一功能,并通过定义良好的接口清晰表述服务边界。由于体积小、复杂度低,每个微服务可由一个小规模开发团队完全掌控,易于保持高可维护性和开发效率。

独立部署:由于微服务具备独立的运行进程,所以每个微服务也可以独立部署。当某个微服务发生变更时无需编译、部署整个应用。由微服务组成的应用相当于具备一系列可并行的发布流程,使得发布更加高效,同时降低对生产环境所造成的风险,最终缩短应用交付周期。

技术选型灵活:微服务架构下,技术选型是去中心化的。每个团队可以根据自身服务的需求和行业发展的现状,自由选择最适合的技术栈。由于每个微服务相对简单,故需要对技术栈进行升级时所面临的风险就较低,甚至完全重构一个微服务也是可行的。

容错:当某一组建发生故障时,在单一进程的传统架构下,故障很有可能在进程内扩散,形成应用全局性的不可用。在微服务架构下,故障会被隔离在单个服务中。若设计良好,其他服务可通过重试、平稳退化等机制实现应用层面的容错。

扩展:单块架构应用也可以实现横向扩展,就是将整个应用完整的复制到不同的节点。当应用的不同组件在扩展需求上存在差异时,微服务架构便体现出其灵活性,因为每个服务可以根据实际需求独立进行扩展。

2.3 ERP API微服务系统架构说明

2.3.1 系统开发逻辑说明

从上面的解析得知:微服务是一种技术架构,将一个庞大的服务体系拆分为若干个子服务执行。 问题来了,应该如何拆分呢?就是服务的拆分原则是什么。

这个问题就像是一个大表如何进行分区一样,其实我觉得是具体问题具体分析。 由于我开发的是基于EBS的微服务系统,正常来说,比较合理的划分规则应该是以EBS的模块来分。

相当于每个模块都划分为一个单独的微服务。例如FND模块,INV模块,WIP模块等等。

有时候,为了某个目的,可能有些功能是定制的,需要提取几个模块的数据来用,而且被别的模块重用的概率很低。所以,实际上也可以以定制的功能来划分微服务。

目前来说,该系统包括2个子服务:

  • xygerp-ald服务:ald模块

这个是整个微服务API的核心ald模块。这个模块的主要功能是验证用户的登录,为所有的api模块提供统一的token认证。相当于ebs的FND模块。

  • xygerp-albc服务:albc子模块

这个项目是属于微服务的API模块之一:条码管理系统提供数据以及数据处理的API。 主要是为条形码传输系统用。

当然,未来可以添加若干个服务。微服务架构的优势是有很好的横向扩展能力!

2.3.2 微服务系统架构图

该系统的架构图如下所示。

这里写图片描述

注意:

1.Spring Cloud模块中,实际上Spring Security并不是单独的一个模块,而是融入到每一个业务微服务模块中! 每个微服务都必须要有token认证才允许访问API,它非常重要! 所以我将它给列到Spring Cloud模块中。

2.图中有些模块目前还没有实现。 目前实现了架构整体,包括以下的服务(下一个章节会具体说明每个模块的用途):

xygerp-ald

xygerp-albc

xygerp-server-eureka

xygerp-server-zuul

注意: 以后会按需添加别的模块。

3 系统开发流程

接下来是一步一步来开发一套这个基于Spring Cloud的微服务系统。

3.1 必须掌握的基础开发技术知识点

开发系统都必须要打好基础。所以,这里列出了开发基于Spring Cloud的微服务系统需要掌握的开发技术。

下面我不会具体解说每一个开发技术如何学习,因为这并不是本文的重点。工欲善其事必先利其器,基础还是必须要打好。

1)Java语言

必须要熟悉java,否则基本不用看文档了。先打好基础吧!

2)Maven项目管理工具

系统开发的项目都是以maven做项目管理的,所以必须要先安装并掌握maven工具。

3)Oracle数据库+PLSQL+SQL语言

数据库端的开发技术。这里选用Oracle数据库,因为EBS就是基于Oracle数据库的ERP系统。

3.2 需要熟悉的java框架

Java技术发展到现在,已经出现了许多非常优秀的开源框架,我们可以借助这些框架来快速开发系统。

3.2.1 Spring框架技术栈(全家桶)

Spring是目前开源的主流的技术包。 该系统主要用到的技术栈是:Spring boot,Spring Security以及Spring Cloud。

  1. Spring Boot

系统基于SpringBoot快速开发。选择目前热度很高的SpringBoot,最大限度地降低配置复杂度,把大量的精力投入到业务开发中来。

  1. Spring MVC

利用Spring MVC框架处理所有的url请求,简单易用。

  1. Spring Security

Spring security是一个强大的和高度可定制的身份验证和访问控制框架。它是确保基于Spring的应用程序的标准。 这里主要是用Spring Security框架处理Token机制。

  1. Spring Cloud

Spring Cloud是一系列框架的有序集合。 它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。 注意:本系统目前使用Spring Cloud的2个模块

  • 请求统一通过API网关(Zuul)来访问内部服务.
  • 网关接收到请求后,从注册中心(Eureka)获取可用服务

3.2.2 MyBatis

ORM框架选用MyBatis。

主要是考虑到它能够很好支持SQL语句:MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架。 另外,还用到了MyBatis的一些提高开发效率的插件,特别是通用Mapper和PageHelper分页插件!

3.2.3 Alibaba druid

DRUID是阿里巴巴开源平台上一个数据库连接池实现。它结合了C3P0、DBCP、PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况。

3.2.4 Swagger

前端和后端的唯一联系,变成了API接口。

API文档变成了前后端开发人员联系的纽带,变得越来越重要,swagger就是一款让你更好的书写API文档的框架。

3.3 需要准备的软件工具

3.3.1Redis

目前用Redis的主要作用是存取token,以配合实现Spring Security完成api访问的安全机制。

以后可以考虑做缓存或者消息队列等高级功能。

3.3.2Docker

基于Docker的容器化部署。

由于使用了微服务架构后,我们的系统将会由很多子系统构成。 为了达到多个系统之间环境隔离的目的,我们可以将它们部署在多台服务器上,可这样的成本会比较高,而且每台服务器的性能可能都没有充分利用起来。

所以我们很自然地想到了虚拟机,在同一台服务器上运行多个虚拟机,从而实现环境的隔离,每个虚拟机上运行独立的服务。

然而虚拟机的隔离成本依旧很高,因为它需要占用服务器较多的硬件资源和软件资源。 所以,在微服务结构下,要实现服务环境的隔离,Docker是最佳选择。它比虚拟机更加轻量级,占用资源较少,而且能够实现快速部署。

备注:后面有专题说明这个工具如何安装使用。由于篇幅原因,本文档暂时不讲解容器化部署。

3.3.3 Jenkins

Jenkins自动化构建工具。

当我们采用了微服务架构后,我们会发现这样一个问题。整个系统由许许多多的服务构成,这些服务都需要运行在单独的容器中,那么每次发布的复杂度将非常高。

首先你要搞清楚这些服务之间的依赖关系、启动的先后顺序,然后再将多个子系统挨个编译、打包、发布。这些操作技术难度低,却又容易出错。

那么有什么工具能够帮助我们解决这些问题呢?答案就是——Jenkins。

它是一款自动化构建的工具,简单的来说,就是我们只需要在它的界面上按一个按钮,就可以实现上述一系列复杂的过程。

备注:后面有专题说明这个工具如何安装使用。由于篇幅原因,本文档暂时不讲解自动化构建。

3.4 具体开发流程

现在开始手把手来搭建一套这样子的系统。

3.4.1 创建Maven项目的组织结构

先创建一个微服务系统的父级项目:xygerp-api

再在这个项目下面分别创建下面几个子项目:

项目名称 说明
xygerp-ald ald模块,端口:8180。这个是整个微服务API的核心ald模块。这个模块的主要功能是验证用户的登录,为所有的api模块提供统一的token认证。相当于ebs的FND模块。
xygerp-albc albc子模块,端口:8181。这个项目是属于微服务的API模块之一:条码管理系统提供数据以及数据处理的API。主要是为条形码传输系统用。
xygerp-comm comm模块这个项目是所有API项目的核心依赖项目。说白了就是将API微服务架构的所有项目的公用代码可以抽取在这里。
xygerp-basic-support 核心基础支撑模块这个项目是所有API项目的基础数据支撑项目。这里统一归集了所有的Entity!因为对于Entity来说,应该是整个微服务都公用的。
xygerp-server-eureka Spring Cloud的服务与发现的服务中心。端口:8101。这个模块是Spring cloud的最核心的模块了,用来处理各个微服务之间的服务调用的。
xygerp-server-zuul Spring Cloud服务网关。端口:8102。在Spring Cloud架构体系内的所有微服务都通过Zuul来对外提供统一的访问入口,所有需要和微服务架构内部服务进行通讯的请求都走统一网关。

它们的目录结果是这样子的:

这里写图片描述

注意: 关于xygerp-basic-support:核心基础支撑模块

可能您会有疑问:为什么不将entity归并在它所属的模块?其实是这样的,我主要是考虑到服务之间的互相调用的问题。

微服务虽然客观上是一个单独的服务,但是,实际上大部分的功能肯定是互相调用的。举个例子,销售订单模块调用库存模块的功能查询个库存是很正常的业务吧?

如果entity不共用的话,相当于销售模块得到的库存模块的结果无法归集为bean来处理,这样子对于后台的处理会带来极大的不便!

3.4.2 构建模块的依赖关系

接着需要通过pom文件来指定它们之间的依赖关系,依赖关系如下图所示。

1. 业务服务部分:

这里写图片描述
注意,上面的4个项目是有依赖关系的。 所以,xygerp-ald和xygerp-albc部署方式 改为war部署 (主要是为了利用jenkins进行自动化部署)。

而xygerp-comm和xygerp-basic-support只是为各个微服务提供基础代码支撑,所以是jar部署即可。

此外,为了简化各个模块的配置,我们将所有模块的通用依赖放在Project的pom文件中,然后让所有模块作为Project的子模块。 这样子模块就可以从父模块中继承所有的依赖,而不需要自己再配置了。

在父pom中指定子模块

modules标签指定了当前模块的子模块是谁,但是仅在父模块的pom文件中指定子模块还不够,还需要在子模块的pom文件中指定父模块是谁。

    <modules>
        <module>xygerp-comm</module>          <!--核心Comm模块 -->
        <module>xygerp-basic-support</module>    <!--核心基础支撑模块 -->
        <module>xygerp-server-eureka</module>   <!--Eureka服务治理模块 -->
        <module>xygerp-server-zuul</module>      <!--zuul动态路由模块 -->
        <module>xygerp-ald</module>             <!--核心ald模块 -->
        <module>xygerp-albc</module>           <!--子模块:albc模块,提供条码对接API服务 -->
    </modules>

需要在子模块中指定父模块

<parent>
        <artifactId>xygerp</artifactId>
        <groupId>com.xygerp</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
</parent>

备注:具体代码直接看源码吧。这里只是提及了一些重点设置而已。

所以,到此为止,模块的依赖关系配置完毕!但要注意模块打包的顺序。

由于所有模块都依赖于xygerp-comm模块和xygerp-basic-support模块,因此在构建模块时,首先需要编译、打包、安装xygerp-comm模块和xygerp-basic-support模块,将它打包进本地仓库中,这样上层模块才能引用到。当该模块安装完毕后,再构建上层模块。 否则在构建上层模块的时候会出现找不到xygerp-comm模块中类库的问题。

Tips: 其实,如果是在父级目录直接用mvn package整体打包的话,那打包构建的顺序在父pom中是直接指定了!

2. 微服务架构服务治理部分

xygerp-server-eureka:Spring Cloud服务注册和发现。就是处理服务之间的治理。

xygerp-server-zuul:Spring Cloud的统一API网关服务。

Tips: 这2个项目是为了实现微服务架构而用到的核心服务。所以,它们是相对独立的。不需要依赖父pom。

3.4.3用mvn编译命令打包代码

上面的项目都建立好之后,再添加所有项目都需要用到的依赖(具体代码可以参考我的源码)。

都没问题的话,就可以用mvn命令进行打包项目了:

mvn clean install -Dmaven.test.skip=true -P dev

这里简单解析一下指令:

mvn:Maven的统一指令。

clean install:表示要构建该项目。

-Dmaven.test.skip=true:表示构建的时候要跳过测试模块。

-P dev:表示构建的时候,启用 dev 的Spring boot参数运行系统。

如果一切都OK,那正常的结果如下:

这里写图片描述

3.5 本地电脑测试系统

代码搞定了,接下来需要考虑的事情应该是如何测试。 毕竟所有的系统都必须要经过测试,特别是这种配置多,涉及范围广的系统。

这个就不得不说一下Spring boot的优势了。Spring boot的打包应用默认内置了tomcat服务。 换句话说,只需要java命令执行一下Spring boot打包的target结果,就可以启动一个tomcat服务啦!真挺方便测试的!

3.5.1 启动本地系统的服务

假设我的xygerp-api项目在这里:D:\JSP_MyEclipse\xygerp-api

然后分别打开4个cmd命令窗口,执行:

D:\JSP_MyEclipse\xygerp-api\xygerp-server-eureka\target>java -jar xygerp-server-eureka-1.0-SNAPSHOT.war
D:\JSP_MyEclipse\xygerp-api\xygerp-server-zuul\target>java -jar xygerp-server-zuul-1.0-SNAPSHOT.war
D:\JSP_MyEclipse\xygerp-api\xygerp-ald\target>java -jar xygerp-ald-1.0-SNAPSHOT.war
D:\JSP_MyEclipse\xygerp-api\xygerp-albc\target>java -jar xygerp-albc-1.0-SNAPSHOT.war

如下图:

这里写图片描述

3.5.2 本地测试API服务系统

本地测试环境的服务启动起来了,接着就是进行具体的数据测试。

首先测试Eureka的服务注册以及发现,确认服务是否都已经注册到系统中:

这里写图片描述

然后,用swagger测试用户登录的功能: http://127.0.0.1:8102/xygerp/ald/swagger-ui.html 目前是测试是否可以正常产生token。

这里写图片描述
这里写图片描述
说明已经登录成功,并且产生了本次访问的token!

将token记录下来,接着测试。 继续测试一个查询的功能: http://127.0.0.1:8102/xygerp/albc/swagger-ui.html

这里写图片描述

注意,这里用了Spring Security框架,所以的API请求头都必须携带token信息。否则请求会返回401。

如果测试OK,那说明基本上系统已经成功搭建好了。 下一步就是如何在测试环境或者正式环境部署它,以及如何一键构建项目的问题了。

简单来说,系统的部署是用 docker工具 ,一键部署项目用的是 Jenkins工具。后面将会用专题来说明这2个工具的使用。

3.6 该API微服务系统实现的功能难点

3.6.1 解决数据库Session的环境变量问题,特别是语言环境和用户环境。

关于这个问题,目前我用的办法可能不一定是最优的,如果有别的兄台有更好的解决办法,请留言给我,十分感谢!

问题来源:

熟悉EBS开发的兄台都应该知道,登录ERP之后,我们每次打开Form,系统就会申请一个新的数据库Session,这时候,EBS系统会 自动帮我们初始化该Session的环境变量 :例如基本的语言环境,用户环境,业务实体等等。

这时候,我们在包里面可以直接用FND_GLOBAL.USER_ID之类的函数就可以非常方便获取环境变量的信息。

但是,在Java Web开发里面就不一样了!

在Java访问数据库的理念中,Session的申请是一个极耗资源的动作!所以,大部分连接数据库的Java软件都提出了一个 数据库连接池 的概念(例如DRUID数据库连接池)。简单来说就是session共用!

Session公用就会带来一个并发问题:A用户使用系统,并初始化了该Session的环境变量为A用户;当A用户不用系统的时候,Session会闲置并放回连接池里面等待别的用户使用。

这时候如果B的用户很可能会使用该Session,如果不重新初始化环境变量的话,那B用户使用系统的Session的环境变量还是A用户,就会导致数据的bug! 如何处理该问题是开发该系统碰到的一个难题。

问题解决:

我目前的处理办法是:在Service层,用AOP统一自动监控Service层的这个参数AuthUser user

只要在Service层将参数AuthUser user放在最后,AOP会自动初始化Session的环境变量。(需要注意的是,我这个系统的数据库Transaction在Service层启用!)

另外,语言环境变量,登录ID环境变量等,会一并自动初始化。因为AuthUser会携带该定义!

核心处理代码如下:

private static final String SQL_GLOBAL_INIT
	    = " DECLARE "
		+ "    L_session_id NUMBER;L_user_id NUMBER;L_login_id NUMBER;L_LANG VARCHAR2(10); "
		+ " BEGIN "
		+ "    L_user_id:=:P_USER_ID; L_login_id:=:P_LOGIN_ID; L_LANG:=:P_LANG;"
		+ "    APPS.fnd_global.INITIALIZE("
		+ "       session_id=>L_session_id, user_id =>L_user_id, resp_id =>NULL, "
		+ "       resp_appl_id=>NULL, security_group_id=>NULL, site_id=>NULL, login_id =>L_login_id, "
		+ "       conc_login_id=>NULL, prog_appl_id=>NULL, conc_program_id=>NULL, conc_request_id=>NULL, "
		+ "       conc_priority_request=>NULL"
		+ "     ); "
		+ "    IF NVL(L_LANG,'US') <> USERENV('LANG') THEN "
		+ "        IF L_LANG='ZHS' THEN "
		+ "            APPS.fnd_global.set_nls_context(p_nls_language => 'SIMPLIFIED CHINESE'); "
		+ "        ELSE "
		+ "            APPS.fnd_global.set_nls_context(p_nls_language => 'AMERICAN'); "
		+ "         END IF;"
		+ "    END IF;"
		+ " END; ";
	
    /*** 
     * service层调用之前先自动初始化环境变量
     * 需要注意的是,用户变量必须的参数放在最后!
     * 只要在Service层将参数AuthUser user放在最后,AOP会自动初始化Session的环境变量。
     * @throws Exception 
     */  
	@SuppressWarnings("static-access")
	@Before("execution(* com.jebms.*.service..*.*(..))  && args(..,user)")  
    public void oracleDBInit(JoinPoint joinPoint,AuthUser user) throws Exception{
		Long dbLoginId=devDao.getJdbcTemplate().queryForObject("SELECT FND_GLOBAL.LOGIN_ID FROM DUAL", Long.class);
if(user.getLoginId()!=null&&user.getLoginId()>0&&!user.getLoginId().equals(dbLoginId)){
			Map<String,Object> inParamMap=new HashMap<String,Object>();
	    	inParamMap.put("P_USER_ID", user.getUserId());
	    	inParamMap.put("P_LOGIN_ID", user.getLoginId());
	    	inParamMap.put("P_LANG", user.getLanguage());
			devDao.getDevJdbcTemplate().execute(this.SQL_GLOBAL_INIT, inParamMap);
        }
    }

源代码在:com.jebms.comm.utils. AopUtil

3.6.2 解决EBS的用户的登录问题:统一用EBS系统的帐号密码登录API系统。

问题描述:

由于我这个是第三方的API系统,所以,用户名和密码信息实际上并不是该API系统需要管理的事情。

相当于说,API系统无法按照正常的流程来验证用户名和密码:输入用户名和密码,系统验证后台数据库的用户名和密码,再返回验证结果。

而是:输入用户名和密码,系统 调用ERP的用户名密码验证包 进行验证,再返回结果。 简单来说:需要添加自定义验证的逻辑。

还好Spring Security框架支持灵活的验证逻辑。

添加步骤:

首先,写一个自定义验证的类:MyAuthenticationProvider

接着,在Spring Security框架的定义中,添加这个自定义的验证。

AbstractWebSecurityConfig

private MyAuthenticationProvider provider;//自定义验证 auth.authenticationProvider(provider);

即可以完美实现这个效果

核心代码:

    /**
     * 自定义验证方式
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = (String) authentication.getCredentials();
        AuthUser user = (AuthUser) userService.loadUserByUsername(username);
        System.out.println("username:"+username+",password:"+password);
        if(user == null){
            throw new BadCredentialsException("Username not found.");
        }

        //加密过程在这里体现
        if (!sysService.xygErpValidateLogin(username, password)) {
            throw new BadCredentialsException("Wrong password.");
        }
        
        user.setPassword(passwordEncoder.encode(password));

        Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
        return new UsernamePasswordAuthenticationToken(user, password, authorities);
    }

3.6.3 统一的开发风格。

1.Entity基类封装。

封装了5who栏位,以及类似Form的FND_SET_WHO的方法,可以很方便进行开发。

另外,为了防止丢失更新,这边每次更新前实际上会先检测数据的一致性,对应的动作也有做了封装。

2.查询逻辑的封装。

查询功能相对来说还是会很多,对于复杂的查询条件如何传值是一个难题。

这里封装了一个SearchInfo积累,可以统一将所有的查询条件都放在这个类,然后在java的Controller层定义好查询条件对应匹配栏位,系统就可以自动产生对应的and条件。

例如:

   @GetMapping(value = "/getPageLocator")
    @ApiOperation(value = "货位分页列表接口")
    public ResultEntity<PageInfo<EslipLocatorRE>> getPageLocator(
    		@ApiParam(value = "库存组织ID",required = true) @RequestParam(required = true) int organizationId,
    		@ApiParam(value = "库别代码",required = true) @RequestParam(required = true) String subinventoryCode,
    		@ApiParam(value = "货位代码",required = false) @RequestParam(required = false) String locatorCode,
    		SearchInfo searchInfo) throws Exception {
    	searchInfo.getConditionMap().put("organizationId", organizationId);
    	searchInfo.getConditionMap().put("subinventoryCode", subinventoryCode);
    	searchInfo.getConditionMap().put("locatorCode", locatorCode);
    	searchInfo.setAuthUser(this.authUser);
    	searchInfo.initSqlCondition();
    	searchInfo.andSqlCondition("MIL.ORGANIZATION_ID","organizationId");
    	searchInfo.andSqlCondition("MIL.SUBINVENTORY_CODE","subinventoryCode");
    	searchInfo.andSqlCondition("MIL.SEGMENT1","locatorCode");
        return eslipService.selectForPageLocator(searchInfo);
    }

3.统一的处理结果的封装。

基本上任何一个处理,要不成功,要不失败(警告其实也算失败)。

这里封装了一个返回结果的基类ResultEntity<T>,可以进行有效的应用端或者java端的交互。

这里写图片描述

@ApiModelProperty(value = "状态码,0表示成功 其他表示失败", example = "0",position = 1)
private String code;

特别需要指出的是,前端获取或者处理数据,也是统一要用这个处理结果基类的返回。

简单来说,就是数据处理成功/失败,会有一个统一的返回结果标识。注意,这个标识和请求的响应结果标识(200)是有所不同的!

请求响应标识只是说明web服务器的响应是正常,但,具体的处理结果可能是处理失败。

这里写图片描述

下面是一个具体的例子(到时候实际开发处理的接口处理结果也是这样子):

{
	"code": "0",
	"message": "",
	"description": "",
	"obj": [{
		"createdBy": -1,
		"creationDate": "2017-10-10 09:37:03",
		"lastUpdatedBy": 10,
		"lastUpdateDate": "2017-11-16 14:47:48",
		"lastUpdateLogin": 96,
		"valueUUID": null,
		"id": 2,
		"applId": 1,
		"respCode": "BASIC_SET",
		"menuId": 2,
		"startDate": "2017-10-10 09:37:03",
		"endDate": null,
		"respName": "系统设置职责",
		"description": "系统设置职责",
		"menuCode": "SYSTEM_SET",
		"menuName": "系统设置菜单",
		"enabled": true
	}],
	"param1": null,
	"param2": null,
	"param3": null,
	"param4": null,
	"param5": null,
	"ok": true
}

文档参考链接: https://juejin.cn/post/6844903560010792968 http://blog.51cto.com/ityouknow/1974080