讓ERP的服務更開放! ——用微服務架構搭建的一套基於EBS的API服務系統

1. 源碼下載地址

源碼連接: https://github.com/samt007/xygerp-api-demohtml

這是用Spring Cloud微服務架構搭建的一套基於EBS的API服務系統前端

如對本文有任何的疑問,請聯繫我:samt007@qq.comjava

2. Introduction介紹

這是一篇傳統ERP系統和基於Java的微服務架構有效結合的技術文檔。git

傳統ERP關注的是企業內部的信息化管理。當ERP系統能將其服務發佈出去以後(結合微服務架構),就能夠很好實現與第三方系統的無縫對接,同時也能夠實現擴展ERP自己的功能。 目標是:讓ERP的服務更開放!github

2.1 它有什麼用?

簡單來講,就是:web

至關於作一箇中間服務平臺,把ERP的API作成Web Service與其它系統集成。docker

下面具體說明它的做用。數據庫

1. 若是沒有它...

若是沒有一個統一的API對接平臺,那麼ERP和第三方系統作對接,那會是下圖所示的結構: 後端

這裏寫圖片描述

從上圖能夠看出,各個第三方系統分別和ERP作對接,不管是DBLINK仍是經過本身的Web Service, 都是雜亂的,沒能統一管理的。簡單來講就是各自爲政,爲對接而須要作的事情都是零碎的。api

當第三方系統愈來愈多的時候,那對於平常的運維將會是一個災難的問題。舉個例子,就一個簡單的運維:密碼修改,須要調整的地方就會不少,也很容易遺漏。

特別指出的是,接口功能的複用方面也是個難題。假設一個查詢庫存的接口,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.im/entry/5a7812906fb9a0635014f19a http://blog.51cto.com/ityouknow/1974080

相關文章
相關標籤/搜索