Commit df704292 authored by 祁新's avatar 祁新

Merge branch 'develop' of http://gitlab.dev.shxrtech.com/doc/ldp-docs into develop

parents 8ab51227 7f2bcea5
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
## 2021-07-16 ## 2021-07-16
版本号: **1.2.43** 版本号: **1.2.45**
**base-service** **base-service**
...@@ -64,8 +64,6 @@ ...@@ -64,8 +64,6 @@
**gen-service** **gen-service**
`feat` `feat`
...@@ -81,7 +79,7 @@ ...@@ -81,7 +79,7 @@
**bpm-service** **bpm-service**
版本:**1.0.20** 版本:**1.0.22**
`Fix` `Fix`
...@@ -89,6 +87,24 @@ ...@@ -89,6 +87,24 @@
### 本次更新方式
1. 替换ldp-manage/fatjar 目录下对应的jar包
2. 替换nginx中mcs、bpm的前端代码
3. 脚手架更新到`1.2.45`
- 下载gen-service配置文件:[gen-service配置文件](http://devdown.shxrtech.com/nacos_config_genservice_2021-07-15.zip),并导入Nacos。
修改`gen-service-dev.yml``gen-service-prod.yml` 数据库连接配置及redis配置
- 新版代码生成使用说明文档:
- 新版hibernate自动填充文档:
## 2021-06-15 ## 2021-06-15
版本号: **1.2.42** 版本号: **1.2.42**
......
...@@ -219,6 +219,9 @@ DEPLOY_ENV=prod ...@@ -219,6 +219,9 @@ DEPLOY_ENV=prod
#### 新建数据库 #### 新建数据库
- 在mysql内创建ldp数据库: sinra_ldp_db ,字符集:utf8mb4 - 在mysql内创建ldp数据库: sinra_ldp_db ,字符集:utf8mb4
- 在mysql内创建代码生成数据库: sinra_ldp_generator,字符集:utf8mb4(可选,不创建则代码生成服务无法使用)
- 在mysql内创建流程系统数据库: sinra_bpm_db,字符集:utf8mb4(可选,不安装流程系统则可以不创建)
- 在mysql内创建代码生成数据库: sinra_ldp_report,字符集:utf8mb4(可选,不使用报表服务则可以不创建)
#### nacos 启动 & 访问 #### nacos 启动 & 访问
...@@ -268,6 +271,16 @@ cd nacos/bin ...@@ -268,6 +271,16 @@ cd nacos/bin
* 修改后保存即可 * 修改后保存即可
#### ***代码生成服务配置(1.2.45及以上版本)
- 修改 gen-service-dev.yml或gen-service-prod.yml配置文件,包含**数据库配置、redis配置**
修改方式,和上述方式一致
------
### 启动 ldp 服务 ### 启动 ldp 服务
```shell ```shell
...@@ -286,6 +299,13 @@ cd tool/ ...@@ -286,6 +299,13 @@ cd tool/
./ldp-boot.sh status ./ldp-boot.sh status
``` ```
PS:如果需要使用`报表服务`,则需要单独启动
```shell
# 启动报表服务
./report-boot.sh start
```
也可以访问地址:http://nacos-server0:8018/nacos 也可以访问地址:http://nacos-server0:8018/nacos
点击服务列表,即可看到以启动并注册的服务信息。如下图: 点击服务列表,即可看到以启动并注册的服务信息。如下图:
...@@ -399,3 +419,8 @@ http://服务域名或ip:9080/mcs ...@@ -399,3 +419,8 @@ http://服务域名或ip:9080/mcs
初始用户名:admin 初始用户名:admin
初始密码:1234561 初始密码:1234561
PS:如果需用部署流程系统,则参照[BPM安装部署文档.md](流程系统集成/BPM安装部署文档.md)
​ 如果需要部署报表系统,则参照[报表系统安装部署文档.md](报表系统集成/报表系统安装部署文档.md)
...@@ -278,7 +278,7 @@ page(page); ...@@ -278,7 +278,7 @@ page(page);
public List<LdpArea> ldpAreas; public List<LdpArea> ldpAreas;
``` ```
关联查询,在mapper.xml中先声明一个resultMap,再写查询语句,返回指定为对应的resultMap,由于这里是一多,所以使用`collection`作为关联填充,如果是一对一,则使用`association` 关联查询,在mapper.xml中先声明一个resultMap,再写查询语句,返回指定为对应的resultMap,由于这里是一多,所以使用`collection`作为关联填充,如果是一对一,则使用`association`
```xml ```xml
<resultMap id="BaseResultMapToOne" type="com.sinra.ldp.example.entity.LdpCity"> <resultMap id="BaseResultMapToOne" type="com.sinra.ldp.example.entity.LdpCity">
......
...@@ -4,459 +4,254 @@ ...@@ -4,459 +4,254 @@
## 一、自动填充注解 ## 一、自动填充注解
### 1.1. @AutoComputed ### 1.1. @AutoFilled
依赖:base-api 依赖:base-api
#### 1.1.1 注解介绍 #### 1.1.1 注解介绍
**@AutoComputed**是一个字段注解,标识**实体类**字段为自动填充的注解,需要配合服务层的 **@AutoService** 使用,拥有四个属性,command、ref、refs、format。注解可以通过command标识字段自动装配的方式,根据字段名查找对应计算方法,或根据command类型查找对应系统计算方法。command默认值为 **ComputedCommand.AUTO** ,以下是内置command每个值的解释: **@AutoComputed**是一个字段注解,标识**实体类**字段为自动填充的注解,拥有三个属性,value、strategy、scope。
**AUTO**:根据字段名进行填充。
**MD5**:将字段值进行一次md5加密。
**DATETIME**:当前时间。
**USERID**:当前登录用户。
**UUID**:生成32位随机字符串。
**PINYIN**:将ref字段的拼音填充到此字段中 **strategy**`FillStrategy.BEAN_NAME``FillStrategy.CLASS_NAME` strategy标识value填充实现是bean还是带包名的类名。默认值是`FillStrategy.BEAN_NAME`
**REFERENCE**:将refs字段的值通过format规则填充到此字段 **scope**`FillScope.INSERT``FillScope.UPDATE``FillScope.INSERT_UPDATE` 标识什么时候执行填充,INSERT插入时填充、UPDATE更新时填充,INSERT_UPDATE插入及更新时都会填充。默认值是`FillScope.INSERT`
**value**:自动填充实现值,当strategy是`FillStrategy.BEAN_NAME`时,则获取bean实现,如果是`FillStrategy.CLASS_NAME`则通过反射获取实现。有几个默认实现如下:
| 默认填充 | 填充值 | 替换方式 |
| --------------------------------- | ------------------ | ------------------------------------------------------------ |
| DefaultFillType.FILL_DATE | 填充当前时间 | 实现AutoFillHandler接口,并注册Bean,Bean名称为DefaultFillType.FILL_DATE |
| DefaultFillType.FILL_USER_ACCOUNT | 填充用户账号 | 实现AutoFillHandler接口,并注册Bean,Bean名称为DefaultFillType.FILL_USER_ACCOUNT |
| DefaultFillType.FILL_USER_ID | 填充用户ID | 实现AutoFillHandler接口,并注册Bean,Bean名称为DefaultFillType.FILL_USER_ID |
| DefaultFillType.FILL_MD5 | 填充原字段md5值 | 实现AutoFillHandler接口,并注册Bean,Bean名称为DefaultFillType.FILL_MD5 |
| DefaultFillType.FILL_PINYIN | 填充原字段pinyin值 | 实现AutoFillHandler接口,并注册Bean,Bean名称为DefaultFillType.FILL_PINYIN |
| DefaultFillType.FILL_VERSION | 填充version值 | 实现AutoFillHandler接口,并注册Bean,Bean名称为DefaultFillType.FILL_VERSION |
#### 1.1.2 使用案例 #### 1.1.2 使用案例
```java ```java
// 值为AUTO,通过属性名查找计算方式,这里的计算方式就是createId /**
@AutoComputed * 创建人,scope为INSERT,新增时填充,填充值为用户account
private String createId; */
@ApiModelProperty(value = "创建人")
// 值为MD5,将原本的明文做一次md5加密并重新赋值到字段中 @Column(name = "create_id")
@AutoComputed(command = ComputedCommand.MD5) @AutoFilled(scope = FillScope.INSERT, value = DefaultFillType.FILL_USER_ACCOUNT)
private String userPassword; private String createId;
// 值为DATETIME,将当前时间填充到字段中
@AutoComputed(command = ComputedCommand.DATETIME)
private Date createTime;
// 值为USERID,将当前登录用户ID填充到字段中
@AutoComputed(command = ComputedCommand.USERID)
private String updateId;
// 值为UUID,将随机生成的UUID填充到字段中 /**
@AutoComputed(command = ComputedCommand.UUID) * 创建时间,scope为INSERT,新增时填充,填充值为当前时间
private String uuId; */
@ApiModelProperty(value = "创建时间")
@Column(name = "create_time")
@AutoFilled(scope = FillScope.INSERT, value = DefaultFillType.FILL_DATE)
private Date createTime;
// 值为PINYIN,ref为userName,将userName字段的中文拼音自动填充到字段中 /**
@AutoComputed(command = ComputedCommand.PINYIN, ref = "userName") * 更新人,scope为INSERT_UPDATE,新增和更新都会填充,填充值为用户account
private String sortName; */
@ApiModelProperty(value = "更新人")
@Column(name = "update_id")
@AutoFilled(scope = FillScope.INSERT_UPDATE, value = DefaultFillType.FILL_USER_ACCOUNT)
private String updateId;
// 值为REFERENCE,refs是一个数组, 将数组中的字段值按照format的规则填充到字段中 /**
@AutoComputed(command = ComputedCommand.REFERENCE, refs = {"name", "age", "gender"}, format = "%s-%s-%s") * 更新时间,scope为INSERT_UPDATE,新增和更新都会填充,填充值为当前时间
private String referenceValue; */
@ApiModelProperty(value = "更新时间")
@Column(name = "update_time")
@AutoFilled(scope = FillScope.INSERT_UPDATE, value = DefaultFillType.FILL_DATE)
private Date updateTime;
``` ```
#### 1.1.3 自定义填充算法 #### 1.1.3 扩展填充算法
当command值为 **ComputedCommand.AUTO** 时,会通过字段名查找计算方式,这里介绍一下如何自定义填充算法。自动填充算法分为三种:
- 第一种是有参算法,例如MD5算法需要使用到原字段中的值作为参数:
```java
// 值为MD5,将原本的明文做一次md5加密并重新赋值到字段中
@AutoComputed(command = ComputedCommand.MD5)
private String userPassword;
```
需要实现 **ComputerArgs** 接口,将自动填充算法写到computed中。例如Md5填充算法代码如下: 扩展填充算法的方式,新建一个类,并实现AutoFillHandler,根据业务判断需要 填充的值,这里举例,例如需要填充一个随机数,新建一个RandomFillHandler类,实现AutoFillHandler方法
```java ```java
public class ComputerMd5 implements ComputerArgs { /**
* 随机数填充实现
*/
public class RandomFillHandler implements AutoFillHandler<Integer> {
/**
* 自动填充方法
* 部分自动填充需要使用实体的其它字段值或者当前字段值,例如MD5填充,需要先获取到当前字段值,再进行md5编码。
*
* entity 为当前实体
* currentValue 为注解字段值
*/
@Override @Override
public String computed(Object plain) { public Integer getFillValue(Object entity, Object currentValue) {
//这里进行处理,并返回自动填充内容 // 返回随机数
return DigestUtils.md5Hex(plain.toString()); return new Random().nextInt();
} }
} }
``` ```
- 第二种也是有参算法,主要是为了按照format进行自动填充,例如: 这样一个自动填充算法就是实现了,下面写如何使用。
指定策略为CLASS_NAME,value值为带全包名的实现类。
```java ```java
// 值为REFERENCE,refs是一个数组, 将数组中的字段值按照format的规则填充到字段中 // 执行插入时自动填充
@AutoComputed(command = ComputedCommand.REFERENCE, refs = {"name", "age", "gender"}, format = "%s-%s-%s") @AutoFilled(strategy = FillStrategy.CLASS_NAME, scope = FillScope.INSERT, value = "com.sinra.ldp.demo.RandomFillHandler")
private String referenceValue; private int randomNum;
``` ```
需要实现 **ComputerFormatArgs** 接口,将自动填充算法写到computed中。例如REFERENCE填充算法如下: **另一种写法**则是先注册为bean,再使用
```java ```java
public class ComputerReference implements ComputerFormatArgs { // 注册bean
@Override @Bean("fill_random")
public String computed(Object target, String format) { public AutoFillHandler randomFillHandler() {
String result = ""; return new RandomFillHandler();
//核心代码如下,如果format为空则默认以下划线拼接,否则按fromat格式化
if (StringUtils.isEmpty(format)) {
result = StringUtils.joinWith("_", objects);
} else {
result = String.format(format, objects);
}
return result;
} }
}
``` ```
- 第三种是无参算法,例如DATETIME算法就是一个无参算法,只需要获取当前时间 使用时,在实体类字段标记
```java ```java
// 值为DATETIME,将当前时间填充到字段中 // 执行插入时自动填充
@AutoComputed(command = ComputedCommand.DATETIME) @AutoFilled(scope = FillScope.INSERT, value = "fill_random")
private Date createTime; private int randomNum;
``` ```
无参算法与有参类似,只是没有参数,需要实现 **ComputerNoArgs** 接口
```java
public class ComputerDate implements ComputerNoArgs<Date> {
@Override
public Date computed() {
return new Date();
}
}
```
- 完成算法编写后,还需要将计算类与字段名对应起来。 ## 二 、缓存注解
在service包中新建computed包(推荐),新建一个类,加上 **@Configuration** 注解,在 **@Bean** 注解中的name属性填上字段名。例如字段createId的填充算法是一个无参算法,方法返回类型为 **ComputerNoArgs** ,如果为有参算法,则应该返回 **ComputerArgs** 或者 **ComputerFormatArgs** ### 2.1. @ApplicationCacheable
```java 依赖:common-cache
@Configuration
public class ComputedResolver {
@Bean(name = "createId") #### 2.1.1 注解介绍
public ComputerNoArgs getCreateId() {
return new ComputerUser();
}
}
```
当AOP在处理自动填充时,这个字段就会根据上面编辑的算法进行自动填充 **@ApplicationCacheable** 注解是方法注解,将注解添加到方法上,会自动将方法结果缓存下来,之后再次调用此接口,会直接从缓存中获取结果。缓存key是自动生成的,生成算法为application:+包名+类名+方法名+:参数长度。其它属性字段与Spring cache 注解 **@Cacheable** 属性字段一致
### 1.2. @AutoService #### 2.1.2 使用案例
依赖:mcs-common 一般此注解在REST层使用,需要注意的是Application缓存不会过期,会一直存在,如果需要刷新,需要另外写刷新方法,建议在数据变化较小,数据量大的接口使用。
#### 1.2.1 注解介绍 ```java
/**
* rest获取用户分页数据
*
* @return
*/
@GetMapping("/rest/page")
@ApplicationCacheable
public RestResult getListByRest() {
return new RestResult(ResultMsg.SUCCESS, exampleService.getUserListByRest());
}
```
**@AutoService**是一个方法注解,被 **@AutoService** 注解的方法,都会被AOP拦截,并根据实体类字段中的 **@AutoComputed** 规则对字段自动填充。拥有两个属性 mode、init,mode为填充方式,默认为 **ComputedMode.COMPUTED** ,init为是否初始化,初始化则全部填充,否则只填充updateId、updateTime、sortName。 ### 2.2. @SessionCacheable
默认取方法的第一个参数进行填充,支持单个实体,也支持List集合。 依赖:common-cache
#### 1.2.2 使用案例 #### 2.2.1 注解介绍
一般@AutoService用于新增方法,以及更新方法 **@SessionCacheable** 注解是方法注解,用法与 **@ApplicationCacheable** 注解基本一致,不一致的地方有两点:1、SessionCache生命周期是跟随登录的,也就是退出登录后失效; 2、缓存key生成规则不一致,生成算法:token:+包名+类名+方法名+:参数长度。
```java #### 2.2.2 使用案例
// 新增方法
@AutoService
public void add(LdpMcsElementInfo elementInfo) {
genericDaoService.insert(elementInfo);
}
// 更新方法 退出登录后,Session缓存失效
@AutoService(init = false)
public void update(LdpMcsElementInfo elementInfo) { ```java
genericDaoService.dynamicUpdate(elementInfo); /**
* jdbc 获取用户list
*
* @param paramMap
* @return
* @throws Exception
*/
@GetMapping("/jdbc/list")
@SessionCacheable
public RestResult getListByJdbc(@RequestParamMap(typeCovertMapping = ExampleUserInfo.class) Map<String, Object> paramMap) throws Exception {
List<ExampleUserInfo> userInfoList = exampleService.getUserListByJDBC(StringUtils.EMPTY, paramMap);
return new RestResult(ResultMsg.SUCCESS, userInfoList);
} }
``` ```
### 1.3. @AutoRelativeComputed ## 三、日志注解
依赖:mcs-common ### 3.1. @AvoidRepeatableSubmit
#### 1.3.1 注解介绍 #### 3.1.1 注解介绍
**@AutoRelativeComputed** 是一个方法注解,对使用JPA配置了关联关系实体类进行自动填充,主要作用于Service层,对新增关联和移除关联方法参数进行自动填充 此注解配合定时任务进行使用,在被需要定时任务调用的服务接口上,添加此注解防止接口由于网络波动或其他原因重复调用
@AutoRelativeComputed字段详解: #### 3.2.2 使用案例
```java
@PostMapping(value = "/testExampleJob")
@AvoidRepeatableSubmit
public RestResult testJob(@RequestParam String taskJobId, @RequestBody ExampleUserInfo userInfo){
exampleService.add(userInfo);
return new RestResult(ResultMsg.SUCCESS,"");
}
```
**使用时仅需要在被定时任务调用的接口上添加@AvoidRepeatableSubmit即可**
**mode**:关联类型,目前支持RelativeMode.MTM(多对多)、RelativeMode.OTM(一对多)、RelativeMode.MTO(多对一)。**PS:一对多、多对一暂时不建议使用** ### 3.2. @SysAuditLog
**inverse**:是否为逆向,false为单向绑定,true为双向绑定,默认为false。 #### 3.2.1 注解介绍
@SysAuditLog 是一个方法注解,需要放在controller方法上。 会拦截包裹的方法,会根据登陆信息自动获取当前用户。此注解进行被请求方法拦截,获取请求地址、请求参数、请求内容、请求方法(GET OR POST)、客户端浏览器信息。
根据请求状态,判断是异常请求还是正常请求。
**source**:参数源类型。 #### 3.2.2 使用案例
**target**:参数目标类型。 ```java
/**
* 用户列表(非分页)
*
* @param paramMap 字段条件
* @return
*/
@GetMapping("/get/list")
@SysAuditLog(moduleKey = "example",subKey = "test")
public RestResult findList(@RequestParamMap(typeCovertMapping = ExampleUserInfo.class) Map<String, Object> paramMap) throws Exception {
List<ExampleUserInfo> userInfoList = exampleService.findList(paramMap);
return new RestResult(ResultMsg.SUCCESS, userInfoList);
}
```
**joinField**:关联字段。 **使用时需传递 moduleKey(模块名称,如果是新模块需提前建立表,以ldp_audit_log_ 开头+ modulekey 录入的值结尾的表 如ldp_audit_log_example , 表结构可参考 ldp_audit_log_sys), subKey 子模块名称最终会保存在日志中作为二级子模块**;
**inverseField**:逆向关联字段。 ### 3.3. @SysJobLog
**exComputed**:扩展的填充字段。 #### 3.3.1 注解介绍
#### 1.3.2 使用案例 由于系统架构为微服务,服务与服务之间解耦合。因此定时任务设计之初,考虑为定时接口任务调用。
定时任务执行接口调用, 在被调用接口引用的Service具体业务方法添加此@SysJobLog 注解。
注解包含两大功能,1 异步执行,防止接口执行时间过长,否则定时任务调用机器一直连接被占用; 2 更新定时任务执行情况。
定时任务执行流程:
1 定时任务调度服务启动后,满足定时条件自动执行任务调度。 根据后台配置好的服务名称、接口信息调用其他服务接口。
2 被调用接口,必须添加@RequestParam String taskJobId 作为接收参数,此参数后续作为更新任务执行状态使用。由于在业务方法上添加@SysJobLog, 业务方法与Controller不在同一个线程中,无法
获取上一个线程中的信息。因此具体业务Service也必须添加名称为String taskJobId 的参数,后续会自动获取参数的值进行更新任务执行情况。
3 被调用接口也必须添加 @AvoidRepeatableSubmit 防止重复执行
##### 1). 多对多关系
这里举例为用户和角色的多对多关系。
用户表(user):
| 字段名称 | 类型 | 备注 | #### 3.3.2 使用案例
| --------- | ------ | ---------------- |
| id | String | 用户ID,唯一标识 |
| user_name | String | 用户名 |
角色表(role):
| 字段名称 | 类型 | 备注 | Controller
| --------- | ------ | ---------------- |
| id | String | 角色ID,唯一标识 |
| role_name | String | 角色名称 |
用户角色关联表(user_role): ```java
| 字段名称 | 类型 | 备注 | @PostMapping(value = "/testExampleJob")
| -------- | ------ | ------------ | @AvoidRepeatableSubmit
| id | String | ID,唯一标识 | public RestResult testJob(@RequestBody ExampleUserInfo userInfo, @RequestParam String taskJobId){
| user_id | String | 用户ID | exampleService.doJob (userInfo, taskJobId);
| role_id | String | 角色ID | return new RestResult(ResultMsg.SUCCESS,"");
}
```
1. 使用JPA配置实体类关联关系 具体Service
**User.java**
```java
/**
* 与角色的多对多关联
*/
@JsonIgnoreProperties("userInfos")//避免互相关联死循环
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_role", //中间表的名称
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
private Set<Role> roleInfos = new HashSet<>();
```
**Role.java**
```java
@JsonIgnoreProperties("roleInfos")
@ManyToMany(mappedBy = "roleInfos", fetch = FetchType.EAGER, targetEntity = User.class)
private Set<User> userInfos = new HashSet<>(0);
```
2. **UserServiceImpl**中添加方法和注解
```java
/**
* 添加角色关联
*
* @param id 用户ID
* @param list 通过注解将List<Role>转换为List<UserRole>
* @param param 作为删除条件,避免重复数据
*/
@AutoRelativeComputed(mode = RelativeMode.MTM, source = Role.class, target = UserRole.class, joinField = "userId", inverseField = "roleId")
public void addRoleToUser(String id, List list, Map<String, Object> param) {
LinkedList<TranscationEntity> entityList = new LinkedList<>();
// 删除重复数据
entityList.add(new TranscationEntity(TransItemExeType.DELETE_ENTITY, UserRole.class, param));
// 新增
entityList.add(new TranscationEntity(TransItemExeType.INSERT_ENTITY, list));
genericDaoService.executeTrans(entityList);
}
/**
* 移除角色关联
*
* @param id 用户ID
* @param list 通过注解将List<Role>转换为List<UserRole>
* @param param 作为删除条件,避免重复数据
*/
@AutoRelativeComputed(mode = RelativeMode.MTM, source = Role.class, target = UserRole.class,joinField = "userId", inverseField = "roleId")
public void removeRoleFromUser(String id, List list, Map<String, Object> param) {
genericDaoService.deleteList(UserRole.class, param);
}
```
#### 1.3.3 扩展填充算法
**@AutoRelativeComputed** 注解主要填充中间关联数据,有部分数据和关联无关,但是又必须填充,这种时候就需要 **exComputed** 来扩展填充算法,可以通过算法来填充其它字段。例如,岗位和组织多对多关联自动填充, exComputed字段值为**orgPosition**
```java
@AutoRelativeComputed(mode = RelativeMode.MTM, source = LdpMcsOrganization.class, target = LdpMcsPositionOrganization.class,joinField = "postId", inverseField = "orgId", exComputed = "orgPosition")
```
编写扩展算法需要实现 **CustomComputer** 接口,并在computed方法中编写自动填充逻辑。computed方法有一个参数,这个参数是注解中target类的对象,可以在方法中拿到对象,并对对象中的其它字段进行填充。这里对manager字段进行填充:
```java
public class ComputerOrgPosition implements CustomComputer {
@Override
public void computed(Object target) {
LdpMcsPositionOrganization positionOrganization = (LdpMcsPositionOrganization) target;
positionOrganization.setManager(false);
}
}
```
在有 **@Configuration** 注解类中完成Bean的装配:
```java
@Configuration
public class ExtraComputer {
/**
* 关联表其它业务数据填充
*
* @return
*/
@Bean(name = "orgPosition")
public CustomComputer getUserPositionComputer() {
return new ComputerOrgPosition();
}
}
```
## 二 、缓存注解
### 2.1. @ApplicationCacheable
依赖:common-cache
#### 2.1.1 注解介绍
**@ApplicationCacheable** 注解是方法注解,将注解添加到方法上,会自动将方法结果缓存下来,之后再次调用此接口,会直接从缓存中获取结果。缓存key是自动生成的,生成算法为application:+包名+类名+方法名+:参数长度。其它属性字段与Spring cache 注解 **@Cacheable** 属性字段一致。
#### 2.1.2 使用案例
一般此注解在REST层使用,需要注意的是Application缓存不会过期,会一直存在,如果需要刷新,需要另外写刷新方法,建议在数据变化较小,数据量大的接口使用。
```java
/**
* rest获取用户分页数据
*
* @return
*/
@GetMapping("/rest/page")
@ApplicationCacheable
public RestResult getListByRest() {
return new RestResult(ResultMsg.SUCCESS, exampleService.getUserListByRest());
}
```
### 2.2. @SessionCacheable
依赖:common-cache
#### 2.2.1 注解介绍
**@SessionCacheable** 注解是方法注解,用法与 **@ApplicationCacheable** 注解基本一致,不一致的地方有两点:1、SessionCache生命周期是跟随登录的,也就是退出登录后失效; 2、缓存key生成规则不一致,生成算法:token:+包名+类名+方法名+:参数长度。
#### 2.2.2 使用案例
退出登录后,Session缓存失效
```java
/**
* jdbc 获取用户list
*
* @param paramMap
* @return
* @throws Exception
*/
@GetMapping("/jdbc/list")
@SessionCacheable
public RestResult getListByJdbc(@RequestParamMap(typeCovertMapping = ExampleUserInfo.class) Map<String, Object> paramMap) throws Exception {
List<ExampleUserInfo> userInfoList = exampleService.getUserListByJDBC(StringUtils.EMPTY, paramMap);
return new RestResult(ResultMsg.SUCCESS, userInfoList);
}
```
## 三、日志注解
### 3.1. @AvoidRepeatableSubmit
#### 3.1.1 注解介绍
此注解配合定时任务进行使用,在被需要定时任务调用的服务接口上,添加此注解防止接口由于网络波动或其他原因重复调用。
#### 3.2.2 使用案例
```java
@PostMapping(value = "/testExampleJob")
@AvoidRepeatableSubmit
public RestResult testJob(@RequestParam String taskJobId, @RequestBody ExampleUserInfo userInfo){
exampleService.add(userInfo);
return new RestResult(ResultMsg.SUCCESS,"");
}
```
**使用时仅需要在被定时任务调用的接口上添加@AvoidRepeatableSubmit即可**
### 3.2. @SysAuditLog
#### 3.2.1 注解介绍
@SysAuditLog 是一个方法注解,需要放在controller方法上。 会拦截包裹的方法,会根据登陆信息自动获取当前用户。此注解进行被请求方法拦截,获取请求地址、请求参数、请求内容、请求方法(GET OR POST)、客户端浏览器信息。
根据请求状态,判断是异常请求还是正常请求。
#### 3.2.2 使用案例
```java
/**
* 用户列表(非分页)
*
* @param paramMap 字段条件
* @return
*/
@GetMapping("/get/list")
@SysAuditLog(moduleKey = "example",subKey = "test")
public RestResult findList(@RequestParamMap(typeCovertMapping = ExampleUserInfo.class) Map<String, Object> paramMap) throws Exception {
List<ExampleUserInfo> userInfoList = exampleService.findList(paramMap);
return new RestResult(ResultMsg.SUCCESS, userInfoList);
}
```
**使用时需传递 moduleKey(模块名称,如果是新模块需提前建立表,以ldp_audit_log_ 开头+ modulekey 录入的值结尾的表 如ldp_audit_log_example , 表结构可参考 ldp_audit_log_sys), subKey 子模块名称最终会保存在日志中作为二级子模块**;
### 3.3. @SysJobLog
#### 3.3.1 注解介绍
由于系统架构为微服务,服务与服务之间解耦合。因此定时任务设计之初,考虑为定时接口任务调用。
定时任务执行接口调用, 在被调用接口引用的Service具体业务方法添加此@SysJobLog 注解。
注解包含两大功能,1 异步执行,防止接口执行时间过长,否则定时任务调用机器一直连接被占用; 2 更新定时任务执行情况。
定时任务执行流程:
1 定时任务调度服务启动后,满足定时条件自动执行任务调度。 根据后台配置好的服务名称、接口信息调用其他服务接口。
2 被调用接口,必须添加@RequestParam String taskJobId 作为接收参数,此参数后续作为更新任务执行状态使用。由于在业务方法上添加@SysJobLog, 业务方法与Controller不在同一个线程中,无法
获取上一个线程中的信息。因此具体业务Service也必须添加名称为String taskJobId 的参数,后续会自动获取参数的值进行更新任务执行情况。
3 被调用接口也必须添加 @AvoidRepeatableSubmit 防止重复执行
#### 3.3.2 使用案例
Controller
```java
@PostMapping(value = "/testExampleJob")
@AvoidRepeatableSubmit
public RestResult testJob(@RequestBody ExampleUserInfo userInfo, @RequestParam String taskJobId){
exampleService.doJob (userInfo, taskJobId);
return new RestResult(ResultMsg.SUCCESS,"");
}
```
具体Service
```java ```java
...@@ -775,3 +570,328 @@ public class RootCodeDTO { ...@@ -775,3 +570,328 @@ public class RootCodeDTO {
第二步与8.1.2中的填充方式一致。 第二步与8.1.2中的填充方式一致。
## 老版本注解
### 一、自动填充
### 1.1. ~~@AutoComputed(1.2.45版已弃用)~~
依赖:base-api。
#### 1.1.1 注解介绍
**@AutoComputed**是一个字段注解,标识**实体类**字段为自动填充的注解,需要配合服务层的 **@AutoService** 使用,拥有四个属性,command、ref、refs、format。注解可以通过command标识字段自动装配的方式,根据字段名查找对应计算方法,或根据command类型查找对应系统计算方法。command默认值为 **ComputedCommand.AUTO** ,以下是内置command每个值的解释:
**AUTO**:根据字段名进行填充。
**MD5**:将字段值进行一次md5加密。
**DATETIME**:当前时间。
**USERID**:当前登录用户。
**UUID**:生成32位随机字符串。
**PINYIN**:将ref字段的拼音填充到此字段中
**REFERENCE**:将refs字段的值通过format规则填充到此字段
#### 1.1.2 使用案例
```java
// 值为AUTO,通过属性名查找计算方式,这里的计算方式就是createId
@AutoComputed
private String createId;
// 值为MD5,将原本的明文做一次md5加密并重新赋值到字段中
@AutoComputed(command = ComputedCommand.MD5)
private String userPassword;
// 值为DATETIME,将当前时间填充到字段中
@AutoComputed(command = ComputedCommand.DATETIME)
private Date createTime;
// 值为USERID,将当前登录用户ID填充到字段中
@AutoComputed(command = ComputedCommand.USERID)
private String updateId;
// 值为UUID,将随机生成的UUID填充到字段中
@AutoComputed(command = ComputedCommand.UUID)
private String uuId;
// 值为PINYIN,ref为userName,将userName字段的中文拼音自动填充到字段中
@AutoComputed(command = ComputedCommand.PINYIN, ref = "userName")
private String sortName;
// 值为REFERENCE,refs是一个数组, 将数组中的字段值按照format的规则填充到字段中
@AutoComputed(command = ComputedCommand.REFERENCE, refs = {"name", "age", "gender"}, format = "%s-%s-%s")
private String referenceValue;
```
#### 1.1.3 自定义填充算法
当command值为 **ComputedCommand.AUTO** 时,会通过字段名查找计算方式,这里介绍一下如何自定义填充算法。自动填充算法分为三种:
- 第一种是有参算法,例如MD5算法需要使用到原字段中的值作为参数:
```java
// 值为MD5,将原本的明文做一次md5加密并重新赋值到字段中
@AutoComputed(command = ComputedCommand.MD5)
private String userPassword;
```
需要实现 **ComputerArgs** 接口,将自动填充算法写到computed中。例如Md5填充算法代码如下:
```java
public class ComputerMd5 implements ComputerArgs {
@Override
public String computed(Object plain) {
//这里进行处理,并返回自动填充内容
return DigestUtils.md5Hex(plain.toString());
}
}
```
- 第二种也是有参算法,主要是为了按照format进行自动填充,例如:
```java
// 值为REFERENCE,refs是一个数组, 将数组中的字段值按照format的规则填充到字段中
@AutoComputed(command = ComputedCommand.REFERENCE, refs = {"name", "age", "gender"}, format = "%s-%s-%s")
private String referenceValue;
```
需要实现 **ComputerFormatArgs** 接口,将自动填充算法写到computed中。例如REFERENCE填充算法如下:
```java
public class ComputerReference implements ComputerFormatArgs {
@Override
public String computed(Object target, String format) {
String result = "";
//核心代码如下,如果format为空则默认以下划线拼接,否则按fromat格式化
if (StringUtils.isEmpty(format)) {
result = StringUtils.joinWith("_", objects);
} else {
result = String.format(format, objects);
}
return result;
}
}
```
- 第三种是无参算法,例如DATETIME算法就是一个无参算法,只需要获取当前时间:
```java
// 值为DATETIME,将当前时间填充到字段中
@AutoComputed(command = ComputedCommand.DATETIME)
private Date createTime;
```
无参算法与有参类似,只是没有参数,需要实现 **ComputerNoArgs** 接口
```java
public class ComputerDate implements ComputerNoArgs<Date> {
@Override
public Date computed() {
return new Date();
}
}
```
- 完成算法编写后,还需要将计算类与字段名对应起来。
在service包中新建computed包(推荐),新建一个类,加上 **@Configuration** 注解,在 **@Bean** 注解中的name属性填上字段名。例如字段createId的填充算法是一个无参算法,方法返回类型为 **ComputerNoArgs** ,如果为有参算法,则应该返回 **ComputerArgs** 或者 **ComputerFormatArgs**。
```java
@Configuration
public class ComputedResolver {
@Bean(name = "createId")
public ComputerNoArgs getCreateId() {
return new ComputerUser();
}
}
```
当AOP在处理自动填充时,这个字段就会根据上面编辑的算法进行自动填充。
### 1.2. ~~@AutoService(1.2.45版已弃用)~~
依赖:mcs-common
#### 1.2.1 注解介绍
**@AutoService**是一个方法注解,被 **@AutoService** 注解的方法,都会被AOP拦截,并根据实体类字段中的 **@AutoComputed** 规则对字段自动填充。拥有两个属性 mode、init,mode为填充方式,默认为 **ComputedMode.COMPUTED** ,init为是否初始化,初始化则全部填充,否则只填充updateId、updateTime、sortName。
默认取方法的第一个参数进行填充,支持单个实体,也支持List集合。
#### 1.2.2 使用案例
一般@AutoService用于新增方法,以及更新方法
```java
// 新增方法
@AutoService
public void add(LdpMcsElementInfo elementInfo) {
genericDaoService.insert(elementInfo);
}
// 更新方法
@AutoService(init = false)
public void update(LdpMcsElementInfo elementInfo) {
genericDaoService.dynamicUpdate(elementInfo);
}
```
### 1.3. ~~@AutoRelativeComputed(1.2.45版已弃用)~~
依赖:mcs-common
#### 1.3.1 注解介绍
**@AutoRelativeComputed** 是一个方法注解,对使用JPA配置了关联关系实体类进行自动填充,主要作用于Service层,对新增关联和移除关联方法参数进行自动填充。
@AutoRelativeComputed字段详解:
**mode**:关联类型,目前支持RelativeMode.MTM(多对多)、RelativeMode.OTM(一对多)、RelativeMode.MTO(多对一)。**PS:一对多、多对一暂时不建议使用**
**inverse**:是否为逆向,false为单向绑定,true为双向绑定,默认为false。
**source**:参数源类型。
**target**:参数目标类型。
**joinField**:关联字段。
**inverseField**:逆向关联字段。
**exComputed**:扩展的填充字段。
#### 1.3.2 使用案例
##### 1). 多对多关系
这里举例为用户和角色的多对多关系。
用户表(user):
| 字段名称 | 类型 | 备注 |
| --------- | ------ | ---------------- |
| id | String | 用户ID,唯一标识 |
| user_name | String | 用户名 |
角色表(role):
| 字段名称 | 类型 | 备注 |
| --------- | ------ | ---------------- |
| id | String | 角色ID,唯一标识 |
| role_name | String | 角色名称 |
用户角色关联表(user_role):
| 字段名称 | 类型 | 备注 |
| -------- | ------ | ------------ |
| id | String | ID,唯一标识 |
| user_id | String | 用户ID |
| role_id | String | 角色ID |
1. 使用JPA配置实体类关联关系
**User.java**
```java
/**
* 与角色的多对多关联
*/
@JsonIgnoreProperties("userInfos")//避免互相关联死循环
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_role", //中间表的名称
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
private Set<Role> roleInfos = new HashSet<>();
```
**Role.java**
```java
@JsonIgnoreProperties("roleInfos")
@ManyToMany(mappedBy = "roleInfos", fetch = FetchType.EAGER, targetEntity = User.class)
private Set<User> userInfos = new HashSet<>(0);
```
2. 在**UserServiceImpl**中添加方法和注解
```java
/**
* 添加角色关联
*
* @param id 用户ID
* @param list 通过注解将List<Role>转换为List<UserRole>
* @param param 作为删除条件,避免重复数据
*/
@AutoRelativeComputed(mode = RelativeMode.MTM, source = Role.class, target = UserRole.class, joinField = "userId", inverseField = "roleId")
public void addRoleToUser(String id, List list, Map<String, Object> param) {
LinkedList<TranscationEntity> entityList = new LinkedList<>();
// 删除重复数据
entityList.add(new TranscationEntity(TransItemExeType.DELETE_ENTITY, UserRole.class, param));
// 新增
entityList.add(new TranscationEntity(TransItemExeType.INSERT_ENTITY, list));
genericDaoService.executeTrans(entityList);
}
/**
* 移除角色关联
*
* @param id 用户ID
* @param list 通过注解将List<Role>转换为List<UserRole>
* @param param 作为删除条件,避免重复数据
*/
@AutoRelativeComputed(mode = RelativeMode.MTM, source = Role.class, target = UserRole.class,joinField = "userId", inverseField = "roleId")
public void removeRoleFromUser(String id, List list, Map<String, Object> param) {
genericDaoService.deleteList(UserRole.class, param);
}
```
#### 1.3.3 扩展填充算法
**@AutoRelativeComputed** 注解主要填充中间关联数据,有部分数据和关联无关,但是又必须填充,这种时候就需要 **exComputed** 来扩展填充算法,可以通过算法来填充其它字段。例如,岗位和组织多对多关联自动填充, exComputed字段值为**orgPosition** :
```java
@AutoRelativeComputed(mode = RelativeMode.MTM, source = LdpMcsOrganization.class, target = LdpMcsPositionOrganization.class,joinField = "postId", inverseField = "orgId", exComputed = "orgPosition")
```
编写扩展算法需要实现 **CustomComputer** 接口,并在computed方法中编写自动填充逻辑。computed方法有一个参数,这个参数是注解中target类的对象,可以在方法中拿到对象,并对对象中的其它字段进行填充。这里对manager字段进行填充:
```java
public class ComputerOrgPosition implements CustomComputer {
@Override
public void computed(Object target) {
LdpMcsPositionOrganization positionOrganization = (LdpMcsPositionOrganization) target;
positionOrganization.setManager(false);
}
}
```
在有 **@Configuration** 注解类中完成Bean的装配:
```java
@Configuration
public class ExtraComputer {
/**
* 关联表其它业务数据填充
*
* @return
*/
@Bean(name = "orgPosition")
public CustomComputer getUserPositionComputer() {
return new ComputerOrgPosition();
}
}
```
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment