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.
Spring Boot
系统基于 SpringBoot 快速开发. 选择目前热度很高的 SpringBoot, 最大限度地降低配置复杂度, 把大量的精力投入到业务开发中来.
Spring MVC
利用 Spring MVC 框架处理所有的 url 请求, 简单易用.
Spring Security
Spring security 是一个强大的和高度可定制的身份验证和访问控制框架. 它是确保基于 Spring 的应用程序的标准.
这里主要是用 Spring Security 框架处理 Token 机制.
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_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
如下图:
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 的环境变量问题, 特别是语言环境和用户环境.
关于这个问题, 目前我用的办法可能不一定是最优的, 如果有别的兄台有更好的解决办法, 请留言给我, 十分感谢!
- 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);
- }
- }
- 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);
- }
- @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);
- }
- @ApiModelProperty(value = "状态码, 0 表示成功 其他表示失败", example = "0",position = 1)
- private String code;
- {
- "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
来源: https://segmentfault.com/a/1190000014451475