源码连接: https://github.com/samt007/xy...html
这是用Spring Cloud微服务架构搭建的一套基于EBS的API服务系统如对本文有任何的疑问,请联系我:samt007@qq.com前端
这是一篇传统ERP系统和基于Java的微服务架构有效结合的技术文档。java
传统ERP关注的是企业内部的信息化管理。当ERP系统能将其服务发布出去以后(结合微服务架构),就能够很好实现与第三方系统的无缝对接,同时也能够实现扩展ERP自己的功能。
目标是:让ERP的服务更开放!
简单来讲,就是:git
至关于作一个中间服务平台,把ERP的功能作成Web Service与其它系统集成。github
下面具体说明它的做用。web
若是没有一个统一的API对接平台,那么ERP和第三方系统作对接,那会是下图所示的结构:docker
从上图能够看出,各个第三方系统分别和ERP作对接,不管是DBLINK仍是经过本身的Web Service, 都是杂乱的,没能统一管理的。简单来讲就是各自为政,为对接而须要作的事情都是零碎的。数据库
当第三方系统愈来愈多的时候,那对于平常的运维将会是一个灾难的问题。举个例子,就一个简单的运维:密码修改,须要调整的地方就会不少,也很容易遗漏。后端
特别指出的是,接口功能的复用方面也是个难题。假设一个查询库存的接口,CRM系统和在线下单系统均可以用的,须要开发2次。api
有了这套统一的API系统以后,ERP系统和别的系统之间的对接就变成了这个结构:
因此,有了它,至关于ERP的API均可以经过这服务平台给开发出去,基本上全部的接口能够完成的业务,均可以经过这套服务平台来完成。
能够实现:
举个例子:
大概这个需求:
成品入库的时候,直接能够用条码枪扫条码或者二维码就能够入库;销售出库的时候,也能够经过刷成品的条码直接进行出库。JIT管理。
经过这个系统的实现逻辑是:
经过EBS的用户名和密码能够登入条码枪上的APP系统。而后,刷条码的时候,经过该Web Service能够产生对应的事务处理!例如完工入库,处理物料搬运单等。
下面是该系统的一些截图
注意:该功能后台API由该微服务提供,前台是安卓的APP
每一个企业内部都有各类第三方系统,这些系统或多或少都须要和EBS进行集成。传统的集成方法是经过DBLINK。
可是有这套Web Service系统以后,就能够统一经过该Web Service做为中介,和EBS进行数据的集成交互!
关于它的解析,网上资料不少。
这里引用某位大神的总结(引用:http://blog.51cto.com/ityoukn...),解析得比较到位:
微服务的概念源于2014年3月Martin Fowler所写的一篇文章“Microservices”。微服务架构是一种架构模式,它提倡将单一应用程序划分红一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每一个服务运行在其独立的进程中,服务与服务间采用轻量级的通讯机制互相沟通(一般是基于HTTP的RESTful
API)。每一个服务都围绕着具体业务进行构建,而且可以被独立地部署到生产环境、类生产环境等。另外,应尽可能避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每一个微服务仅关注于完成一件任务并很好地完成该任务。在全部状况下,每一个任务表明着一个小的业务能力。
微服务架构优点
复杂度可控:在将应用分解的同时,规避了本来复杂度无止境的积累。每个微服务专一于单一功能,并经过定义良好的接口清晰表述服务边界。因为体积小、复杂度低,每一个微服务可由一个小规模开发团队彻底掌控,易于保持高可维护性和开发效率。
独立部署:因为微服务具有独立的运行进程,因此每一个微服务也能够独立部署。当某个微服务发生变动时无需编译、部署整个应用。由微服务组成的应用至关于具有一系列可并行的发布流程,使得发布更加高效,同时下降对生产环境所形成的风险,最终缩短应用交付周期。
技术选型灵活:微服务架构下,技术选型是去中心化的。每一个团队能够根据自身服务的需求和行业发展的现状,自由选择最适合的技术栈。因为每一个微服务相对简单,故须要对技术栈进行升级时所面临的风险就较低,甚至彻底重构一个微服务也是可行的。
容错:当某一组建发生故障时,在单一进程的传统架构下,故障颇有可能在进程内扩散,造成应用全局性的不可用。在微服务架构下,故障会被隔离在单个服务中。若设计良好,其余服务可经过重试、平稳退化等机制实现应用层面的容错。
扩展:单块架构应用也能够实现横向扩展,就是将整个应用完整的复制到不一样的节点。当应用的不一样组件在扩展需求上存在差别时,微服务架构便体现出其灵活性,由于每一个服务能够根据实际需求独立进行扩展。
从上面的解析得知:微服务是一种技术架构,将一个庞大的服务体系拆分为若干个子服务执行。
问题来了,应该如何拆分呢?就是服务的拆分原则是什么。
这个问题就像是一个大表如何进行分区同样,其实我以为是具体问题具体分析。
因为我开发的是基于EBS的微服务系统,正常来讲,比较合理的划分规则应该是以EBS的模块来分。
至关于每一个模块都划分为一个单独的微服务。例如FND模块,INV模块,WIP模块等等。
有时候,为了某个目的,可能有些功能是定制的,须要提取几个模块的数据来用,并且被别的模块重用的几率很低。因此,实际上也能够以定制的功能来划分微服务。
目前来讲,该系统包括2个子服务:
这个是整个微服务API的核心ald模块。这个模块的主要功能是验证用户的登陆,为全部的api模块提供统一的token认证。 至关于ebs的FND模块。
这个项目是属于微服务的API模块之一:条码管理系统提供数据以及数据处理的API。
主要是为条形码传输系统用。固然,将来能够添加若干个服务。微服务架构的优点是有很好的横向扩展能力!
该系统的架构图以下所示。
注意:
1.Spring Cloud模块中,实际上Spring Security并非单独的一个模块,而是融入到每个业务微服务模块中! 每一个微服务都必需要有token认证才容许访问API,它很是重要! 因此我将它给列到Spring Cloud模块中。
2.图中有些模块目前尚未实现。
目前实现了架构总体,包括如下的服务(下一个章节会具体说明每一个模块的用途):
xygerp-ald
xygerp-albc
xygerp-server-eureka
xygerp-server-zuul
注意: 之后会按需添加别的模块。
接下来是一步一步来开发一套这个基于Spring Cloud的微服务系统。
开发系统都必需要打好基础。因此,这里列出了开发基于Spring Cloud的微服务系统须要掌握的开发技术。
下面我不会具体解说每个开发技术如何学习,由于这并非本文的重点。工欲善其事必先利其器,基础仍是必需要打好。
必需要熟悉java,不然基本不用看文档了。先打好基础吧!
系统开发的项目都是以maven作项目管理的,因此必需要先安装并掌握maven工具。
数据库端的开发技术。这里选用Oracle数据库,由于EBS就是基于Oracle数据库的ERP系统。
Java技术发展到如今,已经出现了许多很是优秀的开源框架,咱们能够借助这些框架来快速开发系统。
Spring是目前开源的主流的技术包。
该系统主要用到的技术栈是:Spring boot,Spring Security以及Spring Cloud。
系统基于SpringBoot快速开发。选择目前热度很高的SpringBoot,最大限度地下降配置复杂度,把大量的精力投入到业务开发中来。
利用Spring MVC框架处理全部的url请求,简单易用。
Spring security是一个强大的和高度可定制的身份验证和访问控制框架。它是确保基于Spring的应用程序的标准。
这里主要是用Spring Security框架处理Token机制。
Spring Cloud是一系列框架的有序集合。
它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,均可以用Spring Boot的开发风格作到一键启动和部署。
注意:本系统目前使用Spring Cloud的2个模块
ORM框架选用MyBatis。
主要是考虑到它可以很好支持SQL语句:MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架。
另外,还用到了MyBatis的一些提升开发效率的插件,特别是通用Mapper和PageHelper分页插件!
DRUID是阿里巴巴开源平台上一个数据库链接池实现。它结合了C3P0、DBCP、PROXOOL等DB池的优势,同时加入了日志监控,能够很好的监控DB池链接和SQL的执行状况。
前端和后端的惟一联系,变成了API接口。
API文档变成了先后端开发人员联系的纽带,变得愈来愈重要,swagger就是一款让你更好的书写API文档的框架。
目前用Redis的主要做用是存取token,以配合实现Spring Security完成api访问的安全机制。
之后能够考虑作缓存或者消息队列等高级功能。
基于Docker的容器化部署。
因为使用了微服务架构后,咱们的系统将会由不少子系统构成。
为了达到多个系统之间环境隔离的目的,咱们能够将它们部署在多台服务器上,可这样的成本会比较高,并且每台服务器的性能可能都没有充分利用起来。
因此咱们很天然地想到了虚拟机,在同一台服务器上运行多个虚拟机,从而实现环境的隔离,每一个虚拟机上运行独立的服务。
然而虚拟机的隔离成本依旧很高,由于它须要占用服务器较多的硬件资源和软件资源。
因此,在微服务结构下,要实现服务环境的隔离,Docker是最佳选择。它比虚拟机更加轻量级,占用资源较少,并且可以实现快速部署。
备注:后面有专题说明这个工具如何安装使用。因为篇幅缘由,本文档暂时不讲解容器化部署。
Jenkins自动化构建工具。
当咱们采用了微服务架构后,咱们会发现这样一个问题。整个系统由许许多多的服务构成,这些服务都须要运行在单独的容器中,那么每次发布的复杂度将很是高。
首先你要搞清楚这些服务之间的依赖关系、启动的前后顺序,而后再将多个子系统挨个编译、打包、发布。这些操做技术难度低,却又容易出错。
那么有什么工具可以帮助咱们解决这些问题呢?答案就是——Jenkins。
它是一款自动化构建的工具,简单的来讲,就是咱们只须要在它的界面上按一个按钮,就能够实现上述一系列复杂的过程。
备注:后面有专题说明这个工具如何安装使用。因为篇幅缘由,本文档暂时不讲解自动化构建。
如今开始手把手来搭建一套这样子的系统。
先建立一个微服务系统的父级项目: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来处理,这样子对于后台的处理会带来极大的不便!
接着须要经过pom文件来指定它们之间的依赖关系,依赖关系以下图所示。
注意,上面的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中是直接指定了!
xygerp-server-eureka:Spring Cloud服务注册和发现。就是处理服务之间的治理。
xygerp-server-zuul:Spring Cloud的统一API网关服务。
Tips: 这2个项目是为了实现微服务架构而用到的核心服务。因此,它们是相对独立的。不须要依赖父pom。
上面的项目都创建好以后,再添加全部项目都须要用到的依赖(具体代码能够参考个人源码)。
都没问题的话,就能够用mvn命令进行打包项目了:
mvn clean install -Dmaven.test.skip=true -P dev
这里简单解析一下指令:mvn:Maven的统一指令。
clean install:表示要构建该项目。
-Dmaven.test.skip=true:表示构建的时候要跳过测试模块。
-P dev:表示构建的时候,启用 dev 的Spring boot参数运行系统。
若是一切都OK,那正常的结果以下:
代码搞定了,接下来须要考虑的事情应该是如何测试。
毕竟全部的系统都必需要通过测试,特别是这种配置多,涉及范围广的系统。
这个就不得不说一下Spring boot的优点了。Spring boot的打包应用默认内置了tomcat服务。
换句话说,只须要java命令执行一下Spring boot打包的target结果,就能够启动一个tomcat服务啦!真挺方便测试的!
假设个人xygerp-api项目在这里:D:JSP_MyEclipsexygerp-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
以下图:
本地测试环境的服务启动起来了,接着就是进行具体的数据测试。
首先测试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个工具的使用。
关于这个问题,目前我用的办法可能不必定是最优的,若是有别的兄台有更好的解决办法,请留言给我,十分感谢!
问题来源:
熟悉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
问题描述:
因为我这个是第三方的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); }
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/5a781...
http://blog.51cto.com/ityoukn...