项目介绍
1. 技术选型
1.1 核心框架
- SpringBoot 2.3.0.RELEASE
- SpringCloud Hoxton.SR6
- SpringCloud Alibaba 2.1.1.RELEASE
- nacos (注册中心 | 配置中心)
- sentinel (限流熔断)
- seata (分布式事务——可选)
- SpringSecurity 5.3.2
- Spring WebMvc
- 数据持久层框架
- mybatis plus
- 三方服务
2. 系统架构图
3. 环境地址
名称 | 地址 | 备注 |
---|---|---|
MySQL | 192.168.30.4:3306 | 用户名/密码:root /Ams_2020 |
Redis | 192.168.30.20:6379 | |
Nacos | 192.168.30.20:8848 | 用户名/密码:nacos /nacos ——(ZMUU_IP) |
SVN | 192.168.30.4:8443/svn/java-rms | |
MQ | XXX.XXX.XXX.XXX | 开发到了在补充 |
FILE | XXX.XXX.XXX.XXX | 开发到了在补充 |
ELK | XXX.XXX.XXX.XXX | 开发到了在补充 |
4. 后端服务
4.1 系统服务
项目名称 | 说明 | 端口 |
---|---|---|
注册中心服务 | 使用nacos | 默认8848 |
配置中心服务 | 使用nacos | 默认8848 |
rms-gateway | 网关服务 | 8080 |
rms-auth | 认证中心服务 | 8081 |
sentinel控制台 | 限流熔断监控中心 | 8082 |
4.2 业务服务
项目名称 | 说明 | 端口 |
---|---|---|
rms-core | 档案核心业务服务 | 9090 |
rms-base | 系统基础信息服务 | 9091 |
rms-file | 文件服务 | 9092 |
rms-flow | 工作流服务 | 9093 |
rms-xxx | 其他服务待拆分(搜索...) | todo |
5. 目录结构
5.1 全局服务目录
rms
│
├─rms-auth //认证服务
│
├─rms-common //公共模块
│ ├─rms-common-cache //缓存模块
│ ├─rms-common-core //核心模块
│ ├─rms-common-log //日志模块
│ ├─rms-common-mybatis //mybtais配置
│ ├─rms-common-security //安全模块
│ └─rms-common-swagger //swagger配置
│
├─rms-gateway //网关服务
│
├─rms-modules //业务服务
│ ├─rms-base //系统服务
│ ├─rms-core //核心服务
│ ├─rms-file //文件服务
│ └─rms-flow //流程服务
│ └─rms-xxx //其他业务服务
│
├─rms-modules-api //服务接口模块--对应业务服务
│ ├─rms-base-api //系统接口模块
│ ├─rms-core-api //核心接口模块
│ └─rms-file-api //文件接口模块
│
└─pom.xml //全局pom文件(依赖版本控制)
5.2 详细模块目录
5.2.1 公共模块
rms-common-core
│
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com.rms.common.core
│ │ │ ├─constant //公共常量定义
│ │ │ ├─enums //公共枚举定义
│ │ │ ├─exception //公共异常--controller层异常、service层异常
│ │ │ ├─model //公共模型
│ │ │ └─util //公共工具类
│ │ └─resources
│ │ └─META-INF //springboot启动需要被扫描的组件
│ └─test
│ └─java //单元测试
└─ pom.xml //依赖管理
5.2.2 业务模块
以rms-base
rms-base-api
基础系统服务为例说明业务模块目录结构
rms-base
│
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com.rms.base //包:命名com.rms.模块名称
│ │ │ ├─annotation //自定义注解(非必须)
│ │ │ ├─aspect //切面(非必须)
│ │ │ ├─controller //controller层
│ │ │ ├─constant //常量
│ │ │ ├─enums //枚举
│ │ │ ├─mapper //mapper接口
│ │ │ └─service //业务接口
│ │ │ └─impl //业务实现
│ │ │ └─RmsBaseApplication.java //服务启动类
│ │ └─resources //资源服务目录(spring.yml配置文件、日志等)
│ │ ├─mapper //mapper-xml文件
│ │ └─META-INF //springboot启动需要被扫描的组件
│ └─test //测试目录
│ └─java //在该目录下创建创建包,单元测试类
└─ pom.xml //本模块依赖及打包方式等
服务模块所使用到的数据实体定义到相应的xxx-xxx-api
模块中(为其他业务模块提供本模块api)
rms-base-api
├─src
│ └─main
│ ├─java
│ │ └─com.rms.base.api
│ │ ├─dto //数据传输对象
│ │ ├─entity //数据库实体
│ │ ├─feign //feign远程调用接口
│ │ └─vo //展示对象
│ └─resources
│ └─META-INF //springboot启动需要被扫描的组件
└─pom.xml
⚾️环境要求
1. 准备工作
- JDK: 1.8+
- Maven: 3.3+
- MySQL: 5.7+
- IntelliJ IDEA | eclipse
2. IDEA插件
- lombok(必装)
- MyBatisX (选装)
3. 软件安装
3.1 IDEA安装
详情参见软件安装包中idea
文件夹,内附安装及破解教程。
3.2 JDK安装配置
运行软件安装包下jdk-8u221-windows-x64
下一步即可,安装完成后需修改配置文件:
- 新创建环境变量:
JAVA_HOME=jdk安装目录
CLASSPATH=%JAVA_HOME%\lib\tools.jar
- 编辑Path变量添加:
%JAVA_HOME%\bin
%JAVA_HOME%\jre\bin
3.3 MAVEN安装
- 解压软件安装包目录下的
apache-maven-3.5.3-bin
文件,解压后打开conf\settings.xml
文件 - 搜索
<localRepository>
标签,替换标签内的内容为自己本机的磁盘目录(存储远程仓库下载的jar包)
3.4 IDEA离线安装插件
IDEA导航栏选择file
-> settings
搜索 plugins
,点击齿轮图标选择Install Plugin from Disk...
选择lombook.jar
3.5 NACOS安装(选装)
解压nacos-server-1.4.0
安装,修改conf\application.properties
文件,结尾添加如下属性:
### 数据库配置 ### spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user=username db.password=password
application.properties
配置文件其他属性说明
server.servlet.contextPath=/nacos #项目访问路径前缀 server.port=8848 #访问端口(8848珠峰高度),可以自己指定
启动
- 双击执行解压后nacos文件夹下
bin\startup.cmd
(默认集群方式) startup.cmd -m standalone
单机模式启动 (✔)
访问路径:127.0.0.1:8848/nacos
默认用户名/密码:nacos/nacos
4. 软件安装包说明
文件名 | 备注 |
---|---|
idea | idea安装包(含破解教程) |
RedisDesktopManager | redis可视化工具 |
apache-maven-3.5.3-bin | maven仓库 |
jdk-8u221-windows-x64 | jdk安装 |
lombok | lombok插件(idea版) |
nacos_config | nacos配置文件(启动nacos服务后通过web控制台导入) |
nacos-server-1.4.0 | nacos注册中心|配置文件中心 |
powerdesigner | 数据库表结构设计 |
🚀启动
1. 配置文件
项目启动之前需要修改resources\bootstrap.yml
配置文件,修改注册中心、配置中心地址。
spring: profiles: active: dev application: name: rms-base #服务名称 cloud: nacos: discovery: server-addr: 192.168.10.161:8848 #注册中心地址 config: server-addr: 192.168.10.161:8848 #配置信息地址 file-extension: yml shared-dataids: application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
启动nacos后修改数据库、redis等其他服务器地址,如下图所示:
2. 后端服务启动顺序
- 先启动 MySQL 、redis 、nacos
- 启动除 rms-gateway 之外的其他服务
- 启动 rms-gateway 服务
各个服务启动成功后可以在nacos服务列表中查看服务状态,列表中显示即代表启动成功。
🔫业务异常
业务异常是自定义异常,本质是返回给前端JSON格式对象。在某些场景下抛出业务异常结束请求,抛出异常后由全局异常处理器进行拦截并构造JSON对象返回。RMS系统中业务异常分为HttpReqException
ServiceException
两种类型,两者都是继承自RuntimeException
。
1. 自定义异常类型
- HttpReqException:在controller层抛出该异常类型
- ServiceException:在业务层抛出异常类型
以ServiceException为例说明自定异常,构造函数ServiceException(ICodeEnum error)
内传入ICodeEnum
接口的具体实现枚举类型。
public class ServiceException extends RuntimeException { ICodeEnum error; //定义异常枚举格式 public ServiceException(ICodeEnum error){ super(error.getMsg()); this.error = error; } public ICodeEnum getCodeEnum(){ return this.error; } }
使用自定义异常方法
/* 1.传入枚举值CommonCodeEnum.ERROR_NOT_FOUND 2.CommonCodeEnum 实现 ICodeEnum接口,统一异常枚格式 */ throw new ServiceException(CommonCodeEnum.ERROR_NOT_FOUND);
响应返回对象
{ "code": 20002, "msg":"此操作需要登录系统!" }
2. 全局异常处理器
在rms-common-security
模块定义GlobalExceptionHandler
全局异常处理器 ,在业务层抛出异常后由全局异常处理器进行捕获。捕获后将构造ResponseResult(包括code和msg字段)对象进行统一的结果返回,前端根据请求响应状态码判定失败后统一对返回结果进行解析,从而友好的提示给用户。
@RestControllerAdvice public class GlobalExceptionHandler { /* * 参数绑定、参数校验失败 * HttpStatus.BAD_REQUEST * */ @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class}) @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseResult validatorExceptionHandler(Exception exception) {} /* * controller层请求相关错误捕获 * HttpStatus.BAD_REQUEST * */ @ExceptionHandler(HttpReqException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public ResponseResult httpReqExceptionHandler(HttpReqException e){} /* * service层抛出业务异常捕获 * HttpStatus.INTERNAL_SERVER_ERROR * */ @ExceptionHandler(ServiceException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ResponseResult serviceExceptionHandler(ServiceException e) {} /* * 捕获其他未定义异常 * HttpStatus.INTERNAL_SERVER_ERROR * */ @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ResponseResult ExceptionHandler(Exception e) {}
}
参数校验
参数校验框架使用hibernate validator
,通过pom引入spring-boot-starter-validation依赖进行导入。通过注解完成参数合法性校验,在参数非法时将抛出BindException,由全局异常处理器进行捕获处理。
1. 使用方法
👉 在参数前添加@Validated
注解
@ApiOperation("新增用户") @PostMapping("/addUser") public ResponseResult login(@RequestBody @Validated SysUserModel sysUserModel) { //... }
👉 在参数实体对应的字段上添加相应的校验注解即可实现参数校验功能。
@Data @NoArgsConstructor @ApiModel public class SysUserModel { /* 1.当校验失败时,将返回message上的信息 2.{sys.user.loginName.empty} 失败信息统一配置在messages.properties文件下: --------------------------------------- sys.user.loginName.length=用户名长度非法 sys.user.password.length=密码长度非法 sys.user.userId.empty=用户ID为空 sys.user.deptId.empty=部门ID为空 --------------------------------------- */ @NotBlank(message = "{sys.user.id.empty}",groups = GroupEdit.class) private String id; @NotBlank(message = "{sys.user.username.empty}",groups = {GroupAdd.class, GroupEdit.class}) @ApiModelProperty("用户名") private String username; @NotBlank(message = "{sys.user.password.empty}",groups = {GroupAdd.class, GroupEdit.class}) @ApiModelProperty("密码") private String password; }
2. 常用校验注解
注解名称 | 功能说明 |
---|---|
@Null | 检查该字段为空 |
@NotNull | 不能为null |
@NotBlank | 不能为空,常用于检查空字符串 |
@NotEmpty | 不能为空,多用于检测list是否size是0 |
@Max | 该字段的值只能小于或等于该值 |
@Min | 该字段的值只能大于或等于该值 |
@Past | 检查该字段的日期是在过去 |
@Future | 检查该字段的日期是否是属于将来的日期 |
检查是否是一个有效的email地址 | |
@Pattern(regex=,flag=) | 被注释的元素必须符合指定的正则表达式 |
@Range(min=,max=,message=) | 被注释的元素必须在合适的范围内 |
@Size(min=, max=) | 检查该字段的size是否在min和max之间,可以是字符串、数组、集合、Map等 |
@Length(min=,max=) | 检查所属的字段的长度是否在min和max之间,只能用于字符串 |
@AssertTrue | 用于boolean字段,该字段只能为true |
@AssertFalse | 该字段的值只能为false |
3. 校验分组
开发过程中大部分情况下CRUD都会使用同一个POJO。不同业务,可能参数校验规则不同,使用同一个POJO就需要进行分组校验。如:新增用户场景下不需要检验id属性,修改字段下必须要检验id属性。
以上面的新增用户接口为例,@Validated注解添加value属性,指定分组。
@ApiOperation("新增用户") @PostMapping("/addUser") public ResponseResult login(@RequestBody @Validated(GroupAdd.class) SysUserModel sysUserModel) { //... }
POJO的相应字段上的@Validated注解设置groups属性。
public class SysUserModel { /* 1.设置groups属性用于分组检验 */ @NotBlank(message = "{sys.user.id.empty}",groups = GroupEdit.class) private String id; @NotBlank(message = "{sys.user.username.empty}",groups = {GroupAdd.class, GroupEdit.class}) @ApiModelProperty("用户名") private String username; @NotBlank(message = "{sys.user.password.empty}",groups = {GroupAdd.class, GroupEdit.class}) @ApiModelProperty("密码") private String password; }
服务调用
服务之间的远程调用使用Netflix开源的Feign框架,Feign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用Feign,可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到这是一个远程方法,更感知不到这是一个HTTP请求。
1. Feign的使用
以rms-auth
服务调用rms-base
服务——根据用户名查询用户信息为例:
-
在
rms-base-api
模块(com.rms.base.api.feign包下)创建调用接口IUserClient
@FeignClient( value = AppConstants.RMS_BASE, //定义Feign指向的服务ID contextId = "userClient", fallbackFactory = UserClientFallback.class //回调 ) public interface IUserClient { /* * 根据用户名获取用户信息 * */ @GetMapping("/user/info/{username}") public SysUserModel loadUserByUserame(@PathVariable("username") String username); }