mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-06-01 18:35:37 +08:00
第一波重构
This commit is contained in:
133
README.md
133
README.md
@@ -4,136 +4,5 @@
|
||||
[](https://travis-ci.org/hs-web/hsweb-framework)
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
|
||||
[贡献代码](CONTRIBUTING.md) [用户手册](https://docs.hsweb.io)
|
||||
|
||||
## 应用场景
|
||||
1. 完全开源的后台管理系统.
|
||||
2. 细粒度(按钮,行,列)权限控制的后台管理系统.
|
||||
3. 模块化的后台管理系统.
|
||||
4. 功能可拓展的后台管理系统.
|
||||
5. 集成各种常用功能的后台管理系统.
|
||||
6. 前后分离的后台管理系统.
|
||||
|
||||
注意:
|
||||
项目主要基于`spring-boot`,`mybatis`. 在使用`hsweb`之前,你应该对`spring-boot`有一定的了解.
|
||||
|
||||
项目模块太多?不要被吓到.我们不推荐将本项目直接`clone`后修改,运行.而是使用maven依赖的方式使用`hsweb`.
|
||||
选择自己需要的模块进行依赖,正式版发布后,所有模块都将发布到maven中央仓库.
|
||||
你可以参照[demo](https://github.com/hs-web/hsweb3-demo)进行使用.
|
||||
|
||||
## 文档
|
||||
各个模块的使用方式查看对应模块下的 `README.md`,在使用之前,
|
||||
你可以先粗略浏览一下各个模块,对每个模块的作用有大致的了解.
|
||||
|
||||
## 核心技术选型
|
||||
|
||||
1. Java 8
|
||||
2. Maven3
|
||||
3. Spring Boot 1.5.x
|
||||
4. Mybatis
|
||||
5. Hsweb Easy Orm (使用`hsweb-easy-orm`拓展`Myabtis`实现动态条件)
|
||||
|
||||
## 模块简介
|
||||
|
||||
| 模块 | 说明 | 进度 |
|
||||
| ------------- |:-------------:| ----|
|
||||
|[hsweb-authorization](hsweb-authorization)|权限控制| 100%|
|
||||
|[hsweb-commons](hsweb-commons) |基础通用功能| 100%|
|
||||
|[hsweb-concurrent](hsweb-concurrent)|并发包,缓存,锁,计数器等| 80%|
|
||||
|[hsweb-core](hsweb-core)|框架核心,基础工具类| 100%|
|
||||
|[hsweb-datasource](hsweb-datasource)|数据源| 100%|
|
||||
|[hsweb-logging](hsweb-logging)| 日志| 100%|
|
||||
|[hsweb-starter](hsweb-starter)|模块启动器| 100%|
|
||||
|[hsweb-system](hsweb-system)|**系统常用功能**| 80%|
|
||||
|[hsweb-thirdparty](hsweb-thirdparty)| 第三方插件 | 100% |
|
||||
|
||||
## 核心特性
|
||||
1. DSL风格,可拓展的通用curd,支持前端直接传参数,无需担心任何sql注入.
|
||||
```java
|
||||
//where name = #{name} limit 0,20
|
||||
createQuery().where("name",name).list(0,20);
|
||||
|
||||
//update s_user set name = #{user.name} where id = #{user.id}
|
||||
createUpdate().set(user::getName).where(user::getId).exec();
|
||||
```
|
||||
|
||||
2. 灵活的权限控制
|
||||
```java
|
||||
|
||||
@PostMapping("/account")
|
||||
@Authorize(permission="account-manager",action="add")
|
||||
public ResponseMessage<Sring> addAccount(@RequestBody Account account){
|
||||
return ok(accountService.addAccount(account));
|
||||
}
|
||||
|
||||
@GettMapping("/account")
|
||||
@Authorize(permission="account-manager",action="query",dataAccess=@RequiresDataAccess)//开启数据权限控制
|
||||
public ResponseMessage<PageResult<Account>> addAccount(QueryParamEntity query){
|
||||
|
||||
//用户设置了数据权限后,query的参数属性将被修改
|
||||
|
||||
return ok(accountService.selectPager(query));
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
3. 灵活的模块版本维护脚本
|
||||
|
||||
`resources/hsweb-starter.js`
|
||||
|
||||
```js
|
||||
//组件信息
|
||||
var info = {
|
||||
groupId: "com.company",
|
||||
artifactId: "module-name",
|
||||
version: "1.0.2",
|
||||
website: "company.com",
|
||||
author: "作者",
|
||||
comment: "模块名称"
|
||||
};
|
||||
|
||||
//版本更新信息
|
||||
var versions = [
|
||||
{
|
||||
version: "1.0.2", //当info.version大于等于此版本号时,执行upgrade
|
||||
upgrade: function (context) {
|
||||
var database = context.database;
|
||||
//增加冻结金额字段
|
||||
database.createOrAlter("acc_account")
|
||||
.addColumn().name("freeze_balance").jdbcType(JDBCType.BIGINT).comment("冻结金额").commit()
|
||||
.comment("资金账户")
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
];
|
||||
var JDBCType = java.sql.JDBCType;
|
||||
|
||||
//首次引入依赖,将执行安装操作
|
||||
function install(context) {
|
||||
var database = context.database;
|
||||
database.createOrAlter("acc_account")
|
||||
.addColumn().name("id").varchar(32).notNull().primaryKey().comment("ID").commit()
|
||||
.addColumn().name("account_no").varchar(32).notNull().comment("资金账户号").commit()
|
||||
//更多字段
|
||||
//索引
|
||||
.index().name("idx_acc_account_no")
|
||||
.column("account_no").commit()//account_no索引
|
||||
.comment("资金账户").commit();
|
||||
}
|
||||
|
||||
//设置依赖,固定代码,无需修改
|
||||
dependency.setup(info)
|
||||
.onInstall(install) //安装时执行
|
||||
.onUpgrade(function (context) { //更新时执行
|
||||
var upgrader = context.upgrader;
|
||||
upgrader.filter(versions) //过滤版本信息
|
||||
.upgrade(function (newVer) { //执行更新
|
||||
newVer.upgrade(context);
|
||||
});
|
||||
})
|
||||
.onUninstall(function (context) { //卸载时执行
|
||||
|
||||
});
|
||||
```
|
||||
# 基于spring-boot2,全响应式的后台管理框架
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
<description>授权,权限管理API</description>
|
||||
<artifactId>hsweb-authorization-api</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-boost-aop</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-core</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@ import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.hswebframework.expands.script.engine.DynamicScriptEngine;
|
||||
import org.hswebframework.expands.script.engine.DynamicScriptEngineFactory;
|
||||
import org.hswebframework.utils.StringUtils;
|
||||
import org.hswebframework.web.BusinessException;
|
||||
import org.hswebframework.web.exception.BusinessException;
|
||||
import org.hswebframework.web.authorization.access.DataAccessConfig;
|
||||
import org.hswebframework.web.authorization.access.DataAccessHandler;
|
||||
import org.hswebframework.web.authorization.access.ScriptDataAccessConfig;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
package org.hswebframework.web.authorization.oauth2.client.simple;
|
||||
|
||||
import org.hswebframework.utils.ClassUtils;
|
||||
import org.hswebframework.web.NotFoundException;
|
||||
import org.hswebframework.web.exception.NotFoundException;
|
||||
import org.hswebframework.web.authorization.oauth2.client.OAuth2RequestBuilderFactory;
|
||||
import org.hswebframework.web.authorization.oauth2.client.OAuth2RequestService;
|
||||
import org.hswebframework.web.authorization.oauth2.client.OAuth2ServerConfig;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
package org.hswebframework.web.authorization.oauth2.client.simple;
|
||||
|
||||
import org.hswebframework.web.NotFoundException;
|
||||
import org.hswebframework.web.exception.NotFoundException;
|
||||
import org.hswebframework.web.authorization.oauth2.client.*;
|
||||
import org.hswebframework.web.authorization.oauth2.client.request.OAuth2Session;
|
||||
import org.hswebframework.web.authorization.oauth2.client.simple.session.AuthorizationCodeSession;
|
||||
@@ -28,9 +28,7 @@ import org.hswebframework.web.authorization.oauth2.client.simple.session.Passwor
|
||||
import org.hswebframework.web.oauth2.core.GrantType;
|
||||
import org.hswebframework.web.oauth2.core.OAuth2Constants;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.parser.Feature;
|
||||
import org.hswebframework.web.BusinessException;
|
||||
import org.hswebframework.web.exception.BusinessException;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.builder.AuthenticationBuilderFactory;
|
||||
import org.hswebframework.web.authorization.oauth2.client.exception.OAuth2RequestException;
|
||||
@@ -30,8 +30,6 @@ import org.hswebframework.web.authorization.oauth2.client.request.definition.Res
|
||||
import org.hswebframework.web.authorization.oauth2.client.response.OAuth2Response;
|
||||
import org.hswebframework.web.controller.message.ResponseMessage;
|
||||
import org.hswebframework.web.oauth2.core.ErrorType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
package org.hswebframework.web.authorization.oauth2.client.simple.session;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.hswebframework.web.BusinessException;
|
||||
import org.hswebframework.web.exception.BusinessException;
|
||||
import org.hswebframework.web.authorization.oauth2.client.*;
|
||||
import org.hswebframework.web.authorization.oauth2.client.exception.OAuth2RequestException;
|
||||
import org.hswebframework.web.authorization.oauth2.client.request.OAuth2Request;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.hswebframework.web.authorization.oauth2.client.simple.provider;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.hswebframework.web.BusinessException;
|
||||
import org.hswebframework.web.exception.BusinessException;
|
||||
import org.hswebframework.web.authorization.oauth2.client.exception.OAuth2RequestException;
|
||||
import org.hswebframework.web.authorization.simple.SimpleUser;
|
||||
import org.hswebframework.web.authorization.simple.builder.SimpleAuthenticationBuilderFactory;
|
||||
@@ -12,8 +12,6 @@ import org.junit.Test;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* TODO 完成注释
|
||||
*
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# 增强模块,提供一些增强工具如验证器
|
||||
@@ -1,3 +0,0 @@
|
||||
# AOP增强模块
|
||||
提供aop常用操作需要的公共类
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2019 http://www.hswebframework.org
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
~
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-boost</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hsweb-boost-aop</artifactId>
|
||||
|
||||
<description>对AOP提供一些增强功能</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-commons-utils</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework</groupId>
|
||||
<artifactId>hsweb-utils</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-boost</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hsweb-boost-excel</artifactId>
|
||||
|
||||
<description>便捷EXCEL操作工具</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework</groupId>
|
||||
<artifactId>hsweb-expands-office</artifactId>
|
||||
<version>${hsweb.expands.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-commons-entity</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,303 +0,0 @@
|
||||
package org.hswebframework.web.excel;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hswebframework.expands.office.excel.ExcelIO;
|
||||
import org.hswebframework.web.ApplicationContextHolder;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
import org.hswebframework.web.dict.EnumDict;
|
||||
import org.hswebframework.web.dict.ItemDefine;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
@Slf4j
|
||||
@SuppressWarnings("all")
|
||||
public class DefaultExcelImporter implements ExcelImporter {
|
||||
|
||||
protected static Map<Class, Map<Class, HeaderMapper>> headerMappings = new ConcurrentHashMap<>();
|
||||
|
||||
protected static ExcelCellConverter DEFAULT_CONVERTER = new ExcelCellConverter() {
|
||||
@Override
|
||||
public Object convertFromCell(Object from) {
|
||||
return from;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertToCell(Object from) {
|
||||
if (from instanceof EnumDict) {
|
||||
return ((EnumDict) from).getText();
|
||||
}
|
||||
return from;
|
||||
}
|
||||
};
|
||||
|
||||
protected ExcelCellConverter defaultConvert = DEFAULT_CONVERTER;
|
||||
|
||||
protected Map<Class, HeaderMapper> createHeaderMapping(Class type) {
|
||||
//一些基本类型不做处理
|
||||
if (type == String.class
|
||||
|| Number.class.isAssignableFrom(type)
|
||||
|| ClassUtils.isPrimitiveWrapper(type)
|
||||
|| type.isPrimitive()
|
||||
|| type.isEnum()
|
||||
|| type.isArray()
|
||||
|| Date.class.isAssignableFrom(type)) {
|
||||
return new java.util.HashMap<>();
|
||||
}
|
||||
AtomicInteger index = new AtomicInteger(0);
|
||||
Map<Class, DefaultHeaderMapper> headerMapperMap = new HashMap<>();
|
||||
ReflectionUtils.doWithFields(type, field -> {
|
||||
Excel excel = field.getAnnotation(Excel.class);
|
||||
ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
|
||||
if ((excel == null && apiModelProperty == null) || (excel != null && excel.ignore())) {
|
||||
return;
|
||||
}
|
||||
String header = excel == null ? apiModelProperty.value() : excel.value();
|
||||
HeaderMapping mapping = new HeaderMapping();
|
||||
mapping.header = header;
|
||||
mapping.field = field.getName();
|
||||
mapping.index = excel == null || excel.exportOrder() == -1 ? index.getAndAdd(1) : excel.exportOrder();
|
||||
if (null != excel) {
|
||||
mapping.enableImport = excel.enableImport();
|
||||
mapping.enableExport = excel.enableExport();
|
||||
mapping.children = () -> getHeaderMapper(field.getType(), excel.group());
|
||||
for (Class group : excel.group()) {
|
||||
headerMapperMap.computeIfAbsent(group, DefaultHeaderMapper::new)
|
||||
.mappings
|
||||
.add(mapping);
|
||||
}
|
||||
mapping.converter = createConvert(excel.converter(), field.getType());
|
||||
} else {
|
||||
mapping.converter = createConvert(ExcelCellConverter.class, field.getType());
|
||||
mapping.children = () -> getHeaderMapper(field.getType());
|
||||
headerMapperMap.computeIfAbsent(Void.class, DefaultHeaderMapper::new)
|
||||
.mappings
|
||||
.add(mapping);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return (Map) headerMapperMap;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
protected <T> ExcelCellConverter<T> createConvert(Class<? extends ExcelCellConverter> converterClass, Class<T> type) {
|
||||
if (converterClass != ExcelCellConverter.class) {
|
||||
try {
|
||||
return ApplicationContextHolder.get().getBean(converterClass);
|
||||
} catch (Exception e) {
|
||||
log.warn("can not get bean ({}) from spring context.", converterClass, e);
|
||||
return converterClass.newInstance();
|
||||
}
|
||||
}
|
||||
return defaultConvert;
|
||||
}
|
||||
|
||||
@Getter
|
||||
class HeaderMapping implements Comparable<HeaderMapping> {
|
||||
private String field;
|
||||
|
||||
private String header;
|
||||
|
||||
private int index;
|
||||
|
||||
private boolean enableExport = true;
|
||||
|
||||
private boolean enableImport = true;
|
||||
|
||||
private Supplier<HeaderMapper> children;
|
||||
|
||||
private ExcelCellConverter converter;
|
||||
|
||||
public HeaderMapping copy() {
|
||||
HeaderMapping mapping = new HeaderMapping();
|
||||
mapping.children = children;
|
||||
mapping.field = field;
|
||||
mapping.header = header;
|
||||
mapping.index = index;
|
||||
mapping.enableImport = enableImport;
|
||||
mapping.enableExport = enableExport;
|
||||
mapping.children = children;
|
||||
mapping.converter = converter;
|
||||
return mapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(HeaderMapping o) {
|
||||
return Integer.compare(index, o.index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DefaultHeaderMapper implements HeaderMapper {
|
||||
|
||||
@Getter
|
||||
private Class group;
|
||||
|
||||
public DefaultHeaderMapper(Class group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
private Map<String, HeaderMapping> fastMapping = new HashMap<>();
|
||||
|
||||
private final List<HeaderMapping> mappings = new ArrayList<HeaderMapping>() {
|
||||
private static final long serialVersionUID = 5995980497414973311L;
|
||||
|
||||
@Override
|
||||
public boolean add(HeaderMapping o) {
|
||||
fastMapping.put(o.header, o);
|
||||
fastMapping.put(o.field, o);
|
||||
return super.add(o);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@Override
|
||||
public Optional<HeaderMapping> getMapping(String key) {
|
||||
return Optional.ofNullable(fastMapping.computeIfAbsent(key, k -> {
|
||||
//尝试获取嵌套的属性
|
||||
for (HeaderMapping mapping : mappings) {
|
||||
String newKey = key;
|
||||
//字段嵌套
|
||||
if (newKey.startsWith(mapping.field)) {
|
||||
newKey = newKey.substring(mapping.field.length());
|
||||
}
|
||||
//表头嵌套
|
||||
else if (newKey.startsWith(mapping.header)) {
|
||||
newKey = newKey.substring(mapping.header.length());
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
HeaderMapper mapper = mapping.children.get();
|
||||
if (null != mapper) {
|
||||
HeaderMapping map = mapper.getMapping(newKey).orElse(null);
|
||||
if (map != null) {
|
||||
map = map.copy();
|
||||
map.field = mapping.field.concat(".").concat(map.field);
|
||||
map.header = mapping.header.concat(map.header);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
interface HeaderMapper {
|
||||
Optional<HeaderMapping> getMapping(String key);
|
||||
}
|
||||
|
||||
protected HeaderMapper getHeaderMapper(Class type, Class... group) {
|
||||
Map<Class, HeaderMapper> mapperMap = headerMappings.computeIfAbsent(type, this::createHeaderMapping);
|
||||
|
||||
if (group != null && group.length > 0) {
|
||||
return Arrays.stream(group)
|
||||
.map(mapperMap::get)
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
} else {
|
||||
return mapperMap.get(Void.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public <T> Result<T> doImport(InputStream inputStream, Class<T> type, Function<T, Error> afterParsed, Class... group) {
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
AtomicInteger errorCounter = new AtomicInteger(0);
|
||||
List<T> data = new ArrayList<>();
|
||||
List<Error> errors = new ArrayList<>();
|
||||
HeaderMapper headerMapper = getHeaderMapper(type, group);
|
||||
if (headerMapper == null) {
|
||||
throw new UnsupportedOperationException("不支持导入此类型");
|
||||
}
|
||||
ExcelIO.read(inputStream, row -> {
|
||||
counter.getAndAdd(1);
|
||||
Map<String, Object> mapValue = row.getResult();
|
||||
|
||||
Map<String, Object> newValue = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, Object> entry : mapValue.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
HeaderMapping mapping = headerMapper.getMapping(key).orElse(null);
|
||||
|
||||
if (mapping == null || !mapping.enableImport) {
|
||||
continue;
|
||||
}
|
||||
Object value = mapping.getConverter().convertFromCell(entry.getValue());
|
||||
|
||||
String field = mapping.getField();
|
||||
//嵌套的字段
|
||||
if (field.contains(".")) {
|
||||
String tmpField = field;
|
||||
Map<String, Object> nestMapValue = newValue;
|
||||
|
||||
while (tmpField.contains(".")) {
|
||||
|
||||
// nest.obj.name => [nest,obj.name]
|
||||
String[] nestFields = tmpField.split("[.]", 2);
|
||||
|
||||
//nest
|
||||
String nestField = nestFields[0];
|
||||
|
||||
//obj.name
|
||||
tmpField = nestFields[1];
|
||||
|
||||
Object nestValue = nestMapValue.get(nestField);
|
||||
//构造嵌套对象为map
|
||||
if (nestValue == null) {
|
||||
nestMapValue.put(nestField, nestMapValue = new HashMap<>());
|
||||
} else {
|
||||
if (nestValue instanceof Map) {
|
||||
nestMapValue = ((Map) nestValue);
|
||||
} else {
|
||||
//这里几乎不可能进入...
|
||||
nestMapValue.put(nestField, nestMapValue = FastBeanCopier.copy(nestValue, new HashMap<>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
//最后nestMapValue就为最里层嵌套的对象了
|
||||
nestMapValue.put(tmpField, value);
|
||||
} else {
|
||||
newValue.put(field, value);
|
||||
}
|
||||
}
|
||||
//创建实例并将map复制到实例中
|
||||
T instance = FastBeanCopier.getBeanFactory().newInstance(type);
|
||||
|
||||
FastBeanCopier.copy(newValue, instance);
|
||||
|
||||
data.add(instance);
|
||||
|
||||
Error error = afterParsed.apply(instance);
|
||||
if (null != error) {
|
||||
errorCounter.getAndAdd(1);
|
||||
error.setRowIndex(counter.get());
|
||||
error.setSheetIndex(row.getSheet());
|
||||
errors.add(error);
|
||||
}
|
||||
});
|
||||
return Result.<T>builder()
|
||||
.data(data)
|
||||
.errors(errors)
|
||||
.success(counter.get() - errorCounter.get())
|
||||
.total(counter.get())
|
||||
.error(errorCounter.get())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package org.hswebframework.web.excel;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Excel {
|
||||
|
||||
/**
|
||||
* @return EXCEL表头
|
||||
* @see ApiModelProperty#value()
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* @return 读取指定的工作薄,-1为默认
|
||||
*/
|
||||
int sheetIndex() default -1;
|
||||
|
||||
/**
|
||||
* @return 是否取消EXCEL导入导出功能
|
||||
*/
|
||||
boolean ignore() default false;
|
||||
|
||||
/**
|
||||
* @return 是否开启导入, 开启后的字段才能进行导入
|
||||
*/
|
||||
boolean enableImport() default true;
|
||||
|
||||
/**
|
||||
* @return 是否开启导出, 开启后的字段才能进行导出
|
||||
*/
|
||||
boolean enableExport() default true;
|
||||
|
||||
/**
|
||||
* @return 导出时, 表头的顺序
|
||||
*/
|
||||
int exportOrder() default -1;
|
||||
|
||||
/**
|
||||
* @return 导出分组, 可通过分组导入导出不同的字段信息
|
||||
*/
|
||||
Class[] group() default Void.class;
|
||||
|
||||
/**
|
||||
* 自定义单元格转换器, 用于对数据字典等字段进行自定义转换
|
||||
*
|
||||
* @return 实例必须注入到spring容器中
|
||||
* @see org.springframework.context.ApplicationContext#getBean(Class)
|
||||
*/
|
||||
Class<? extends ExcelCellConverter> converter() default ExcelCellConverter.class;
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package org.hswebframework.web.excel;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
public interface ExcelCellConverter<T> {
|
||||
T convertFromCell(Object from);
|
||||
|
||||
Object convertToCell(T from);
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package org.hswebframework.web.excel;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
public interface ExcelImporter {
|
||||
|
||||
ExcelImporter instance = new DefaultExcelImporter();
|
||||
|
||||
/**
|
||||
* 解析excel为指定的class对象,并返回解析结果.类上的属性需要注解{@link Excel}或者{@link io.swagger.annotations.ApiModelProperty}.
|
||||
*
|
||||
* @param inputStream excel文件流,支持xls和xlsx
|
||||
* @param type 要解析为的类型
|
||||
* @param afterParsed 每解析完一个对象都会调用此接口,用于自定义操作,如: 数据校验
|
||||
* @param group 导入的分组 {@link Excel#group()},如果不指定则为 {@link Void#getClass()}
|
||||
* @param <T> 泛型
|
||||
* @return 导入结果, 包含了成功, 失败信息
|
||||
* @see Excel
|
||||
* @see FastBeanCopier#getBeanFactory()
|
||||
* @see ExcelImporter#instance 默认的实现
|
||||
*/
|
||||
<T> Result<T> doImport(InputStream inputStream, Class<T> type, Function<T, Error> afterParsed, Class... group);
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@Setter
|
||||
class Result<T> {
|
||||
int total;
|
||||
int success;
|
||||
int error;
|
||||
List<Header> headers;
|
||||
List<T> data;
|
||||
List<Error> errors;
|
||||
}
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@Setter
|
||||
class Error {
|
||||
int sheetIndex;
|
||||
int rowIndex;
|
||||
int errorType;
|
||||
Object reason;
|
||||
}
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@Setter
|
||||
class Header {
|
||||
int sheetIndex;
|
||||
@SuppressWarnings("all")
|
||||
String header;
|
||||
String field;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package org.hswebframework.web.excel;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.hswebframework.web.commons.bean.Bean;
|
||||
import org.hswebframework.web.commons.entity.DataStatusEnum;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
public class DefaultExcelImporterTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ExcelImporter.Result<TestBean> result = ExcelImporter
|
||||
.instance
|
||||
.doImport(this.getClass().getResourceAsStream("/test.xls"), TestBean.class, bean -> null);
|
||||
|
||||
Assert.assertEquals(result.success, 1);
|
||||
System.out.println(JSON.toJSONString(result.getData(), SerializerFeature.PrettyFormat));
|
||||
TestBean bean = result.getData().get(0);
|
||||
Assert.assertNotNull(bean.status);
|
||||
Assert.assertNotNull(bean.nest);
|
||||
Assert.assertNotNull(bean.nest.nest);
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public static class TestBean implements Bean {
|
||||
|
||||
private static final long serialVersionUID = -5394537136669692305L;
|
||||
|
||||
@Excel("姓名")
|
||||
private String name;
|
||||
|
||||
@Excel("年龄")
|
||||
private int age;
|
||||
|
||||
@Excel("状态")
|
||||
private DataStatusEnum status;
|
||||
|
||||
@Excel("嵌套-")
|
||||
private TestBean nest;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-boost</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hsweb-boost-ftp</artifactId>
|
||||
|
||||
<description>ftp操作工具,使用ftp连接池.</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId>
|
||||
<version>3.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jooq</groupId>
|
||||
<artifactId>jool-java-8</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,80 +0,0 @@
|
||||
package org.hswebframework.web.ftp;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.hswebframework.web.ftp.pool.FTPClientPool;
|
||||
import org.jooq.lambda.Unchecked;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class DefaultFTPOperation implements FTPOperation {
|
||||
|
||||
private FTPClientPool pool;
|
||||
|
||||
@SneakyThrows
|
||||
protected FTPClient getClient() {
|
||||
return pool.borrowObject();
|
||||
}
|
||||
|
||||
protected void returnClient(FTPClient client) {
|
||||
pool.returnObject(client);
|
||||
}
|
||||
|
||||
public <T> T doExecute(Function<FTPClient, T> function) {
|
||||
FTPClient client = getClient();
|
||||
try {
|
||||
return function.apply(client);
|
||||
} finally {
|
||||
returnClient(client);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(String fileName) {
|
||||
return doExecute(Unchecked.function(client -> client.deleteFile(fileName)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rename(String from, String to) {
|
||||
return doExecute(Unchecked.function(client -> client.rename(from, to)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean download(String fileName, OutputStream outputStream) {
|
||||
return doExecute(Unchecked.function(client -> client.retrieveFile(fileName, outputStream)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean upload(String fileName, InputStream input) {
|
||||
return doExecute(Unchecked.function(client -> client.storeFile(fileName, input)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean upload(String fileName, String text) {
|
||||
return doExecute(Unchecked.function(client -> {
|
||||
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(text.getBytes())) {
|
||||
return client.storeFile(fileName, inputStream);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void list(String path, Consumer<FTPFile> consumer) {
|
||||
doExecute(Unchecked.function(client -> {
|
||||
Arrays.stream(client.listFiles(path)).forEach(consumer);
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package org.hswebframework.web.ftp;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface FTPOperation {
|
||||
|
||||
boolean delete(String fileName);
|
||||
|
||||
boolean rename(String from, String to);
|
||||
|
||||
boolean download(String fileName, OutputStream outputStream);
|
||||
|
||||
boolean upload(String fileName, InputStream input);
|
||||
|
||||
boolean upload(String fileName, String text);
|
||||
|
||||
void list(String path, Consumer<FTPFile> consumer);
|
||||
|
||||
<T> T doExecute(Function<FTPClient, T> command);
|
||||
|
||||
interface HandleExceptionFunction<T>{
|
||||
T apply(FTPClient client) throws Exception;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package org.hswebframework.web.ftp.pool;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPReply;
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.PooledObjectFactory;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
|
||||
|
||||
public class FTPClientFactory implements PooledObjectFactory<FTPClient> {
|
||||
private static Logger logger = LoggerFactory.getLogger(FTPClientFactory.class);
|
||||
private FTPClientProperties config;
|
||||
|
||||
public FTPClientFactory(FTPClientProperties config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public PooledObject<FTPClient> makeObject() throws Exception {
|
||||
FTPClient ftpClient = new FTPClient();
|
||||
ftpClient.setConnectTimeout(config.getClientTimeout());
|
||||
ftpClient.connect(config.getHost(), config.getPort());
|
||||
int reply = ftpClient.getReplyCode();
|
||||
if (!FTPReply.isPositiveCompletion(reply)) {
|
||||
ftpClient.disconnect();
|
||||
logger.warn("FTPServer refused connection");
|
||||
return null;
|
||||
}
|
||||
boolean result = ftpClient.login(config.getUsername(), config.getPassword());
|
||||
if (!result) {
|
||||
throw new ConnectException("ftp登陆失败:" + config.getUsername() + "/password:" + config.getPassword() + "@" + config.getHost());
|
||||
}
|
||||
ftpClient.setFileType(config.getTransferFileType());
|
||||
ftpClient.setBufferSize(1024);
|
||||
ftpClient.setControlEncoding(config.getEncoding());
|
||||
if (config.isPassiveMode()) {
|
||||
ftpClient.enterLocalPassiveMode();
|
||||
}
|
||||
return new DefaultPooledObject<>(ftpClient);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyObject(PooledObject<FTPClient> p) throws Exception {
|
||||
try {
|
||||
p.getObject().logout();
|
||||
} finally {
|
||||
p.getObject().disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateObject(PooledObject<FTPClient> p) {
|
||||
try {
|
||||
p.getObject().sendNoOp();
|
||||
} catch (IOException e) {
|
||||
logger.warn("validateObject ftp error!", e);
|
||||
return false;
|
||||
}
|
||||
return p.getObject().isConnected() && p.getObject().isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateObject(PooledObject<FTPClient> p) throws Exception {
|
||||
p.getObject().sendNoOp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void passivateObject(PooledObject<FTPClient> p) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.hswebframework.web.ftp.pool;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.pool2.PooledObjectFactory;
|
||||
import org.apache.commons.pool2.impl.AbandonedConfig;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
|
||||
public class FTPClientPool extends GenericObjectPool<FTPClient> {
|
||||
|
||||
public FTPClientPool(PooledObjectFactory<FTPClient> factory) {
|
||||
super(factory);
|
||||
}
|
||||
|
||||
public FTPClientPool(PooledObjectFactory<FTPClient> factory, GenericObjectPoolConfig config) {
|
||||
super(factory, config);
|
||||
}
|
||||
|
||||
public FTPClientPool(PooledObjectFactory<FTPClient> factory, GenericObjectPoolConfig config, AbandonedConfig abandonedConfig) {
|
||||
super(factory, config, abandonedConfig);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.hswebframework.web.ftp.pool;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
public class FTPClientProperties extends GenericObjectPoolConfig {
|
||||
private String host;
|
||||
private int port = 22;
|
||||
private String username;
|
||||
private String password;
|
||||
private boolean passiveMode = true;
|
||||
private String encoding = "utf-8";
|
||||
private int clientTimeout = 10 * 1000;
|
||||
private int threadNum = 20;
|
||||
private int transferFileType = FTPClient.BINARY_FILE_TYPE;
|
||||
private boolean renameUploaded = false;
|
||||
private int retryTimes = 3;
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2019 http://www.hswebframework.org
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
~
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-framework</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<description>一些增强功能</description>
|
||||
<artifactId>hsweb-boost</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>hsweb-boost-aop</module>
|
||||
<module>hsweb-boost-ftp</module>
|
||||
<module>hsweb-boost-excel</module>
|
||||
</modules>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -1,52 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-commons</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hsweb-commons-bean</artifactId>
|
||||
|
||||
<description>通用增删改查-通用Bean模块</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.athaydes</groupId>
|
||||
<artifactId>spock-reports</artifactId>
|
||||
<version>1.2.13</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>javax.el</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,38 +0,0 @@
|
||||
package org.hswebframework.web.commons.bean;
|
||||
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface Bean extends Serializable {
|
||||
/**
|
||||
* 从指定的对象中复制属性到本对象
|
||||
*
|
||||
* @param from 要复制的对象
|
||||
* @param ignore 不复制的字段
|
||||
* @param <T> 对象类型
|
||||
* @return 原始对象
|
||||
* @see FastBeanCopier
|
||||
*/
|
||||
@SuppressWarnings("all")
|
||||
default <T extends Bean> T copyFrom(Object from, String... ignore) {
|
||||
return (T) FastBeanCopier.copy(from, this, ignore);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象的属性复制到指定的对象中
|
||||
*
|
||||
* @param to 要复制到的对象
|
||||
* @param ignore 不复制的字段
|
||||
* @param <T> 对象类型
|
||||
* @return 复制后的对象
|
||||
* @see FastBeanCopier
|
||||
*/
|
||||
default <T> T copyTo(T to, String... ignore) {
|
||||
return FastBeanCopier.copy(this, to, ignore);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package org.hswebframework.web.commons.bean;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hswebframework.web.validate.SimpleValidateResults;
|
||||
import org.hswebframework.web.validate.ValidationException;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.ValidatorFactory;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
@Slf4j
|
||||
public final class BeanValidator {
|
||||
|
||||
private BeanValidator() {
|
||||
}
|
||||
|
||||
static volatile Validator validator;
|
||||
|
||||
public static Validator getValidator() {
|
||||
if (validator == null) {
|
||||
synchronized (BeanValidator.class) {
|
||||
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
|
||||
return validator = factory.getValidator();
|
||||
}
|
||||
}
|
||||
return validator;
|
||||
}
|
||||
|
||||
public static <T> T tryValidate(T bean, Class... group) {
|
||||
Set<ConstraintViolation<T>> violations = getValidator().validate(bean, group);
|
||||
if (!violations.isEmpty()) {
|
||||
SimpleValidateResults results = new SimpleValidateResults();
|
||||
for (ConstraintViolation<T> violation : violations) {
|
||||
results.addResult(violation.getPropertyPath().toString(), violation.getMessage());
|
||||
}
|
||||
throw new ValidationException(results);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package org.hswebframework.web.commons.bean;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.ValidatorFactory;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
@Configuration
|
||||
public class BeanValidatorAutoConfiguration implements BeanPostProcessor {
|
||||
@Bean(name = "validator")
|
||||
@ConditionalOnMissingBean(Validator.class)
|
||||
public Validator validator() {
|
||||
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
|
||||
return factory.getValidator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof Validator) {
|
||||
BeanValidator.validator = ((Validator) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package org.hswebframework.web.commons.bean;
|
||||
|
||||
/**
|
||||
* 支持验证的bean
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface ValidateBean extends Bean {
|
||||
|
||||
/**
|
||||
* 尝试验证此bean,如果验证未通过,将抛出{@link org.hswebframework.web.validate.ValidationException}
|
||||
*
|
||||
* @param group 验证分组
|
||||
* @param <T> 当前对象类型
|
||||
* @return 当前对象
|
||||
*/
|
||||
default <T extends ValidateBean> T tryValidate(Class... group) {
|
||||
BeanValidator.tryValidate(this, group);
|
||||
return (T) this;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.hswebframework.web.commons.bean.BeanValidatorAutoConfiguration
|
||||
@@ -1,43 +0,0 @@
|
||||
package org.hswebframework.web.commons.bean
|
||||
|
||||
import org.hibernate.validator.constraints.NotBlank
|
||||
import org.hswebframework.web.validator.group.CreateGroup
|
||||
import org.hswebframework.web.validator.group.UpdateGroup
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.2
|
||||
*/
|
||||
class BeanValidatorTest extends spock.lang.Specification {
|
||||
|
||||
def "测试初始化验证器"() {
|
||||
given: "初始化"
|
||||
def validator = BeanValidator.getValidator();
|
||||
expect: "成功"
|
||||
null != validator
|
||||
}
|
||||
|
||||
|
||||
def doValidate(TestBean bean, Class group) {
|
||||
try {
|
||||
bean.tryValidate(group);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
return e.message;
|
||||
}
|
||||
}
|
||||
|
||||
def "测试group验证"() {
|
||||
expect: "验证多个group"
|
||||
doValidate(new TestBean(name: name), group as Class) == message
|
||||
where:
|
||||
name | group | message
|
||||
null | CreateGroup.class | "姓名不能为空"
|
||||
"" | CreateGroup.class | "姓名不能为空"
|
||||
null | UpdateGroup.class | null
|
||||
"" | UpdateGroup.class | "长度必须在2-20之间"
|
||||
"张三" | UpdateGroup.class | null
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package org.hswebframework.web.commons.bean;
|
||||
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
import org.hibernate.validator.constraints.NotBlank;
|
||||
import org.hswebframework.web.validator.group.CreateGroup;
|
||||
import org.hswebframework.web.validator.group.UpdateGroup;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.2
|
||||
*/
|
||||
@Data
|
||||
public class TestBean implements ValidateBean {
|
||||
|
||||
@NotBlank(groups = CreateGroup.class, message = "姓名不能为空")
|
||||
@Length(min = 2, max = 20, message = "长度必须在2-20之间", groups = UpdateGroup.class)
|
||||
private String name;
|
||||
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
# 通用Controller
|
||||
提供增删改查的restful接口
|
||||
|
||||
以`RequestMapping("/user)`为例
|
||||
|
||||
| 功能 | http method&url | 响应 | 说明 |
|
||||
| ------------- | -------------| ------------- | ----|
|
||||
|查询|GET /user|HTTP Status:200 {"status":200,"result":{"data":[],"total":0}} |可进行[动态查询](#动态查询)|
|
||||
|不分页查询|GET /user/no-paging|HTTP Status:200 {"status":200,"result":[]} |可进行[动态查询](#动态查询)|
|
||||
|获取指定id的数据|GET /user/{id}|HTTP Status:200 {"status":200,"result":{"name":""} |可进行[动态查询](#动态查询)|
|
||||
|新增|POST /user|HTTP Status:201 {"status":201,"result":"{id}"} |contentType='application/json' |
|
||||
|更新|PUT /user/{id}|HTTP Status:200 {"status":200} |contentType='application/json'|
|
||||
|新增或者更新|PATCH /user|HTTP Status:200 {"status":200,"result":"{id}"} |contentType='application/json' |
|
||||
|删除|DELETE /user/{id}|HTTP Status:200 {"status":200} | |
|
||||
|
||||
# 动态查询
|
||||
|
||||
目前支持动态查询条件类 `QueryParamEntity`:
|
||||
前端传参数:
|
||||
1. 普通条件
|
||||
```html
|
||||
terms[0].column=name&terms[0].termType=like&terms[0].value=张三
|
||||
```
|
||||
等同于sql
|
||||
```sql
|
||||
where name like ?
|
||||
where name like '张三'
|
||||
```
|
||||
|
||||
2. 复杂条件
|
||||
```html
|
||||
terms[0].column=name&terms[0].termType=eq&terms[0].value=张三
|
||||
&terms[1].column=name&terms[1].termType=eq&terms[1].type=or&terms[1].value=李四
|
||||
```
|
||||
等同于sql
|
||||
```sql
|
||||
where name =? or name = ?
|
||||
where name = '张三' or name = '李四'
|
||||
```
|
||||
|
||||
3. 嵌套条件
|
||||
```html
|
||||
terms[0].column=name&terms[0].termType=like&terms[0].value=张%
|
||||
&terms[1].type=and
|
||||
&terms[1].terms[0].column=age&terms[1].terms[0].termType=gt&terms[1].terms[0].value=10
|
||||
&terms[1].terms[1].column=age&terms[1].terms[1].termType=lt&terms[1].terms[1].value=18
|
||||
|
||||
```
|
||||
等同于sql
|
||||
```sql
|
||||
where name like ? and (age>? and age <?)
|
||||
where name like '张%' and (age>10 and age <18)
|
||||
|
||||
```
|
||||
|
||||
4. 排序
|
||||
```html
|
||||
sorts[0].name=age&sorts[0].order=desc
|
||||
```
|
||||
等同于sql
|
||||
```sql
|
||||
order by age desc
|
||||
```
|
||||
|
||||
5. 分页
|
||||
```html
|
||||
pageIndex=0&pageSize=20
|
||||
```
|
||||
|
||||
不分页查询
|
||||
```html
|
||||
paging=false
|
||||
```
|
||||
|
||||
6. 指定要查询的列
|
||||
```html
|
||||
includes=id,name,age
|
||||
```
|
||||
等同于sql
|
||||
```sql
|
||||
select id,name,age from ......
|
||||
```
|
||||
不查询的列参数为excludes,如:`excludes=comment,phone`
|
||||
|
||||
注意: 以上参数都进行了验证,不会有sql注入问题。
|
||||
@@ -1,78 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ /*
|
||||
~ * Copyright 2019 http://www.hswebframework.org
|
||||
~ *
|
||||
~ * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ * you may not use this file except in compliance with the License.
|
||||
~ * You may obtain a copy of the License at
|
||||
~ *
|
||||
~ * http://www.apache.org/licenses/LICENSE-2.0
|
||||
~ *
|
||||
~ * Unless required by applicable law or agreed to in writing, software
|
||||
~ * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ * See the License for the specific language governing permissions and
|
||||
~ * limitations under the License.
|
||||
~ */
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-commons</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hsweb-commons-controller</artifactId>
|
||||
|
||||
<description>通用增删改查-通用Controller模块</description>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-commons-service-simple</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework</groupId>
|
||||
<artifactId>hsweb-easy-orm-rdb</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-access-logging-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-authorization-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-commons-model</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 http://www.hswebframework.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.controller;
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiResponse;
|
||||
import io.swagger.annotations.ApiResponses;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.Permission;
|
||||
import org.hswebframework.web.authorization.User;
|
||||
import org.hswebframework.web.authorization.annotation.Authorize;
|
||||
import org.hswebframework.web.commons.entity.RecordCreationEntity;
|
||||
import org.hswebframework.web.commons.entity.RecordModifierEntity;
|
||||
import org.hswebframework.web.controller.message.ResponseMessage;
|
||||
import org.hswebframework.web.logging.AccessLogger;
|
||||
import org.hswebframework.web.service.CreateEntityService;
|
||||
import org.hswebframework.web.service.InsertService;
|
||||
import org.hswebframework.web.validator.group.CreateGroup;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import static org.hswebframework.web.controller.message.ResponseMessage.ok;
|
||||
|
||||
/**
|
||||
* 通用新增控制器<br>
|
||||
* 使用:实现该接口,注解@RestController 以及@RequestMapping("/myController")
|
||||
* 客户端调用: 通过POST请求,contentType为application/json 。参数为E泛型的json格式
|
||||
* <pre>
|
||||
* curl -l -H "Content-type: application/json" -X POST -d '{"field1":"value1","field2":"value2"}' http://domain/contextPath/myController
|
||||
* </pre>
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface CreateController<E, PK, M> {
|
||||
|
||||
@Authorize(ignore = true)
|
||||
<S extends InsertService<E, PK> & CreateEntityService<E>> S getService();
|
||||
|
||||
@Authorize(action = Permission.ACTION_ADD)
|
||||
@PostMapping
|
||||
@ResponseStatus(HttpStatus.CREATED)
|
||||
@ApiOperation(value = "新增")
|
||||
default ResponseMessage<PK> add(@RequestBody M data) {
|
||||
E entity = modelToEntity(data, getService().createEntity());
|
||||
//自动添加创建人和创建时间
|
||||
if (entity instanceof RecordCreationEntity) {
|
||||
RecordCreationEntity creationEntity = (RecordCreationEntity) entity;
|
||||
creationEntity.setCreateTimeNow();
|
||||
// creationEntity.setCreatorId(Authentication.current()
|
||||
// .map(Authentication::getUser)
|
||||
// .map(User::getId)
|
||||
// .orElse(null));
|
||||
}
|
||||
//修改人和修改时间
|
||||
if (entity instanceof RecordModifierEntity) {
|
||||
RecordModifierEntity creationEntity = (RecordModifierEntity) entity;
|
||||
creationEntity.setModifyTimeNow();
|
||||
// creationEntity.setModifierId(Authentication.current()
|
||||
// .map(Authentication::getUser)
|
||||
// .map(User::getId)
|
||||
// .orElse(null));
|
||||
}
|
||||
return ok(getService().insert(entity));
|
||||
}
|
||||
|
||||
@Authorize(ignore = true)
|
||||
E modelToEntity(M model, E entity);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.controller;
|
||||
|
||||
import org.hswebframework.web.authorization.annotation.Authorize;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
import org.hswebframework.web.commons.entity.Entity;
|
||||
import org.hswebframework.web.service.CrudService;
|
||||
|
||||
/**
|
||||
* 通用增删改查控制器
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see QueryController
|
||||
* @see CreateController
|
||||
* @see UpdateController
|
||||
* @see DeleteController
|
||||
* @see CrudService
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface CrudController<E, PK, Q extends Entity, M>
|
||||
extends QueryController<E, PK, Q>
|
||||
, UpdateController<E, PK, M>
|
||||
, CreateController<E, PK, M>
|
||||
, DeleteController<E,PK> {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@Authorize(ignore = true)
|
||||
CrudService<E, PK> getService();
|
||||
|
||||
@Override
|
||||
@Authorize(ignore = true)
|
||||
default E modelToEntity(M model, E entity) {
|
||||
return FastBeanCopier.copy(model, entity);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 http://www.hswebframework.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.controller;
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiResponse;
|
||||
import io.swagger.annotations.ApiResponses;
|
||||
import org.hswebframework.web.authorization.Permission;
|
||||
import org.hswebframework.web.authorization.annotation.Authorize;
|
||||
import org.hswebframework.web.controller.message.ResponseMessage;
|
||||
import org.hswebframework.web.logging.AccessLogger;
|
||||
import org.hswebframework.web.service.DeleteService;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
import static org.hswebframework.web.controller.message.ResponseMessage.ok;
|
||||
|
||||
/**
|
||||
* 通用删除控制器
|
||||
*
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface DeleteController<E,PK> {
|
||||
|
||||
@Authorize(ignore = true)
|
||||
DeleteService<E,PK> getService();
|
||||
|
||||
@Authorize(action = Permission.ACTION_DELETE)
|
||||
@DeleteMapping(path = "/{id:.+}")
|
||||
@ApiOperation("删除数据")
|
||||
default ResponseMessage<E> deleteByPrimaryKey(@PathVariable PK id) {
|
||||
return ok(getService().deleteByPk(id));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.controller;
|
||||
|
||||
import org.hswebframework.web.authorization.annotation.Authorize;
|
||||
import org.hswebframework.web.commons.entity.Entity;
|
||||
import org.hswebframework.web.commons.entity.GenericEntity;
|
||||
import org.hswebframework.web.service.CrudService;
|
||||
|
||||
/**
|
||||
* 通用实体的增删改查控制器
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see GenericEntity
|
||||
* @see CrudController
|
||||
* @see CrudService
|
||||
*/
|
||||
public interface GenericEntityController<E extends GenericEntity<PK>, PK, Q extends Entity, M>
|
||||
extends CrudController<E, PK, Q, M> {
|
||||
|
||||
@Override
|
||||
@Authorize(ignore = true)
|
||||
CrudService<E, PK> getService();
|
||||
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 http://www.hswebframework.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.controller;
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.hswebframework.web.NotFoundException;
|
||||
import org.hswebframework.web.authorization.Permission;
|
||||
import org.hswebframework.web.authorization.annotation.Authorize;
|
||||
import org.hswebframework.web.commons.entity.Entity;
|
||||
import org.hswebframework.web.commons.entity.PagerResult;
|
||||
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
|
||||
import org.hswebframework.web.controller.message.ResponseMessage;
|
||||
import org.hswebframework.web.service.QueryByEntityService;
|
||||
import org.hswebframework.web.service.QueryService;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hswebframework.web.controller.message.ResponseMessage.ok;
|
||||
|
||||
/**
|
||||
* 通用查询控制器。
|
||||
*
|
||||
* @param <E> 实体类型
|
||||
* @param <PK> 主键类型
|
||||
* @param <Q> 查询条件实体类型,默认提供{@link QueryParamEntity}实现
|
||||
* @author zhouhao
|
||||
* @see QueryParamEntity
|
||||
* @see 3.0
|
||||
*/
|
||||
public interface QueryController<E, PK, Q extends Entity> {
|
||||
|
||||
/**
|
||||
* 获取实现了{@link QueryByEntityService}和{@link QueryService}的服务类
|
||||
*
|
||||
* @param <T> 服务类泛型
|
||||
* @return 服务类实例
|
||||
*/
|
||||
@Authorize(ignore = true)
|
||||
<T extends QueryByEntityService<E> & QueryService<E, PK>> T getService();
|
||||
|
||||
/**
|
||||
* 根据参数动态查询。<br>
|
||||
* 参数泛型如果为QueryParamEntity,
|
||||
* 客户的参数 ?terms[0].column=name&terms[0].value=小明
|
||||
* 则执行查询条件 where name = '小明'
|
||||
* 具体使用方法参照 {@link QueryParamEntity}
|
||||
*
|
||||
* @param param 参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
@Authorize(action = Permission.ACTION_QUERY)
|
||||
@GetMapping
|
||||
@ApiOperation(value = "根据动态条件查询", responseReference = "get")
|
||||
default ResponseMessage<PagerResult<E>> list(Q param) {
|
||||
return ok(getService().selectPager(param));
|
||||
}
|
||||
|
||||
@Authorize(action = Permission.ACTION_QUERY)
|
||||
@GetMapping("/no-paging")
|
||||
@ApiOperation(value = "不分页动态查询", responseReference = "get")
|
||||
default ResponseMessage<List<E>> listNoPaging(Q param) {
|
||||
if (param instanceof QueryParamEntity) {
|
||||
((QueryParamEntity) param).setPaging(false);
|
||||
}
|
||||
return ok(getService().select(param));
|
||||
}
|
||||
|
||||
@Authorize(action = Permission.ACTION_QUERY)
|
||||
@GetMapping("/count")
|
||||
@ApiOperation(value = "根据动态条件统计", responseReference = "get")
|
||||
default ResponseMessage<Integer> count(Q param) {
|
||||
return ok(getService().count(param));
|
||||
}
|
||||
|
||||
|
||||
@Authorize(action = Permission.ACTION_GET)
|
||||
@GetMapping(path = "/{id:.+}")
|
||||
@ApiOperation("根据主键查询")
|
||||
default ResponseMessage<E> getByPrimaryKey(@PathVariable PK id) {
|
||||
return ok(assertNotNull(getService().selectByPk(id)));
|
||||
}
|
||||
|
||||
@Authorize(action = Permission.ACTION_GET)
|
||||
@GetMapping(path = "/ids")
|
||||
@ApiOperation("根据主键查询多条记录")
|
||||
default ResponseMessage<List<E>> getByPrimaryKey(@RequestParam List<PK> ids) {
|
||||
return ok(assertNotNull(getService().selectByPk(ids)));
|
||||
}
|
||||
|
||||
@Authorize(ignore = true)
|
||||
static <T> T assertNotNull(T obj) {
|
||||
if (null == obj) {
|
||||
throw new NotFoundException("{data_not_exist}");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.controller;
|
||||
|
||||
import org.hswebframework.web.authorization.annotation.Authorize;
|
||||
import org.hswebframework.web.commons.entity.Entity;
|
||||
import org.hswebframework.web.service.CrudService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
/**
|
||||
* 通用增删改查控制器
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see QueryController
|
||||
* @see CreateController
|
||||
* @see UpdateController
|
||||
* @see DeleteController
|
||||
* @see CrudService
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface SimpleCrudController<E, PK, Q extends Entity>
|
||||
extends QueryController<E, PK, Q>
|
||||
, UpdateController<E, PK, E>
|
||||
, CreateController<E, PK, E>
|
||||
, DeleteController<E,PK> {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@Authorize(ignore = true)
|
||||
CrudService<E, PK> getService();
|
||||
|
||||
@Override
|
||||
@Authorize(ignore = true)
|
||||
default E modelToEntity(E model, E entity) {
|
||||
// model = entity
|
||||
return model;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.controller;
|
||||
|
||||
import org.hswebframework.web.authorization.annotation.Authorize;
|
||||
import org.hswebframework.web.commons.entity.Entity;
|
||||
import org.hswebframework.web.commons.entity.GenericEntity;
|
||||
import org.hswebframework.web.service.CrudService;
|
||||
|
||||
/**
|
||||
* 通用实体的增删改查控制器
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see GenericEntity
|
||||
* @see CrudController
|
||||
* @see CrudService
|
||||
*/
|
||||
public interface SimpleGenericEntityController<E extends GenericEntity<PK>, PK, Q extends Entity>
|
||||
extends SimpleCrudController<E, PK, Q> {
|
||||
|
||||
@Override
|
||||
@Authorize(ignore = true)
|
||||
CrudService<E, PK> getService();
|
||||
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 http://www.hswebframework.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.controller;
|
||||
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiResponse;
|
||||
import io.swagger.annotations.ApiResponses;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.Permission;
|
||||
import org.hswebframework.web.authorization.User;
|
||||
import org.hswebframework.web.authorization.annotation.Authorize;
|
||||
import org.hswebframework.web.authorization.annotation.Logical;
|
||||
import org.hswebframework.web.commons.entity.RecordCreationEntity;
|
||||
import org.hswebframework.web.commons.entity.RecordModifierEntity;
|
||||
import org.hswebframework.web.controller.message.ResponseMessage;
|
||||
import org.hswebframework.web.logging.AccessLogger;
|
||||
import org.hswebframework.web.service.CreateEntityService;
|
||||
import org.hswebframework.web.service.UpdateService;
|
||||
import org.springframework.web.bind.annotation.PatchMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
/**
|
||||
* 通用更新控制器
|
||||
*
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface UpdateController<E, PK, M> {
|
||||
<S extends UpdateService<E, PK> & CreateEntityService<E>> S getService();
|
||||
|
||||
@Authorize(action = Permission.ACTION_UPDATE)
|
||||
@PutMapping(path = "/{id}")
|
||||
@ApiOperation("修改数据")
|
||||
default ResponseMessage<Integer> updateByPrimaryKey(@PathVariable PK id, @RequestBody M data) {
|
||||
E entity = modelToEntity(data, getService().createEntity());
|
||||
if (entity instanceof RecordModifierEntity) {
|
||||
RecordModifierEntity creationEntity = (RecordModifierEntity) entity;
|
||||
creationEntity.setModifyTimeNow();
|
||||
creationEntity.setModifierId(Authentication.current()
|
||||
.map(Authentication::getUser)
|
||||
.map(User::getId)
|
||||
.orElse(null));
|
||||
}
|
||||
return ResponseMessage.ok(getService().updateByPk(id, entity));
|
||||
}
|
||||
|
||||
@Authorize(action = {Permission.ACTION_UPDATE, Permission.ACTION_ADD}, logical = Logical.AND)
|
||||
@PatchMapping
|
||||
@ApiOperation("新增或者修改")
|
||||
default ResponseMessage<PK> saveOrUpdate(@RequestBody M data) {
|
||||
E entity = modelToEntity(data, getService().createEntity());
|
||||
//自动添加创建人和创建时间
|
||||
if (entity instanceof RecordCreationEntity) {
|
||||
RecordCreationEntity creationEntity = (RecordCreationEntity) entity;
|
||||
creationEntity.setCreateTimeNow();
|
||||
creationEntity.setCreatorId(Authentication.current()
|
||||
.map(Authentication::getUser)
|
||||
.map(User::getId)
|
||||
.orElse(null));
|
||||
}
|
||||
//修改人和修改时间
|
||||
if (entity instanceof RecordModifierEntity) {
|
||||
RecordModifierEntity creationEntity = (RecordModifierEntity) entity;
|
||||
creationEntity.setModifyTimeNow();
|
||||
creationEntity.setModifierId(Authentication.current()
|
||||
.map(Authentication::getUser)
|
||||
.map(User::getId)
|
||||
.orElse(null));
|
||||
}
|
||||
return ResponseMessage.ok(getService().saveOrUpdate(entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将model转为entity
|
||||
*
|
||||
* @param model
|
||||
* @param entity
|
||||
* @return 转换后的结果
|
||||
* @see org.hswebframework.web.commons.model.Model
|
||||
* @see org.hswebframework.web.commons.entity.Entity
|
||||
*/
|
||||
@Authorize(ignore = true)
|
||||
E modelToEntity(M model, E entity);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package org.hswebframework.web.controller.message;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class MapResponseMessage extends ResponseMessage<Map<String, Object>> {
|
||||
public MapResponseMessage() {
|
||||
result(new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
public MapResponseMessage put(String key, Object value) {
|
||||
result.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public static MapResponseMessage ok() {
|
||||
return new MapResponseMessage();
|
||||
}
|
||||
|
||||
public static MapResponseMessage ok(String message) {
|
||||
MapResponseMessage responseMessage = new MapResponseMessage();
|
||||
responseMessage.message = message;
|
||||
return responseMessage;
|
||||
}
|
||||
|
||||
public static MapResponseMessage error() {
|
||||
return new MapResponseMessage();
|
||||
}
|
||||
|
||||
|
||||
public static MapResponseMessage error(String message) {
|
||||
MapResponseMessage mapResponseMessage = new MapResponseMessage();
|
||||
mapResponseMessage.message = message;
|
||||
return mapResponseMessage;
|
||||
}
|
||||
|
||||
public static MapResponseMessage error(int status, String message) {
|
||||
MapResponseMessage mapResponseMessage = new MapResponseMessage();
|
||||
mapResponseMessage.message = message;
|
||||
mapResponseMessage.status = status;
|
||||
return mapResponseMessage;
|
||||
}
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.controller.message;
|
||||
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 响应消息,controller中处理后,返回此对象,响应请求结果给客户端。
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
@ApiModel(description = "响应结果")
|
||||
public class ResponseMessage<T> implements Serializable {
|
||||
private static final long serialVersionUID = 8992436576262574064L;
|
||||
|
||||
protected String message;
|
||||
|
||||
protected T result;
|
||||
|
||||
protected int status;
|
||||
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
private String code;
|
||||
|
||||
@ApiModelProperty("调用结果消息")
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@ApiModelProperty(value = "状态码", required = true)
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@ApiModelProperty("成功时响应数据")
|
||||
public T getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
@ApiModelProperty(value = "时间戳", required = true, dataType = "Long")
|
||||
public Long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@ApiModelProperty(value = "业务代码")
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public static <T> ResponseMessage<T> error(String message) {
|
||||
return error(500, message);
|
||||
}
|
||||
|
||||
public static <T> ResponseMessage<T> error(int status, String message) {
|
||||
ResponseMessage<T> msg = new ResponseMessage<>();
|
||||
msg.message = message;
|
||||
msg.status(status);
|
||||
return msg.putTimeStamp();
|
||||
}
|
||||
|
||||
public static <T> ResponseMessage<T> ok() {
|
||||
return ok(null);
|
||||
}
|
||||
|
||||
private ResponseMessage<T> putTimeStamp() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
return this;
|
||||
}
|
||||
|
||||
public static <T> ResponseMessage<T> ok(T result) {
|
||||
return new ResponseMessage<T>()
|
||||
.result(result)
|
||||
.putTimeStamp()
|
||||
.status(200);
|
||||
}
|
||||
|
||||
public ResponseMessage<T> result(T result) {
|
||||
this.result = result;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public ResponseMessage<T> code(String code) {
|
||||
this.code = code;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤字段:指定需要序列化的字段
|
||||
*/
|
||||
private transient Map<Class<?>, Set<String>> includes;
|
||||
|
||||
/**
|
||||
* 过滤字段:指定不需要序列化的字段
|
||||
*/
|
||||
private transient Map<Class<?>, Set<String>> excludes;
|
||||
|
||||
public ResponseMessage() {
|
||||
|
||||
}
|
||||
|
||||
public ResponseMessage<T> include(Class<?> type, String... fields) {
|
||||
return include(type, Arrays.asList(fields));
|
||||
}
|
||||
|
||||
public ResponseMessage<T> include(Class<?> type, Collection<String> fields) {
|
||||
if (includes == null) {
|
||||
includes = new HashMap<>();
|
||||
}
|
||||
if (fields == null || fields.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
fields.forEach(field -> {
|
||||
if (field.contains(".")) {
|
||||
String tmp[] = field.split("[.]", 2);
|
||||
try {
|
||||
Field field1 = type.getDeclaredField(tmp[0]);
|
||||
if (field1 != null) {
|
||||
include(field1.getType(), tmp[1]);
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
} else {
|
||||
getStringListFromMap(includes, type).add(field);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseMessage<T> exclude(Class type, Collection<String> fields) {
|
||||
if (excludes == null) {
|
||||
excludes = new HashMap<>();
|
||||
}
|
||||
if (fields == null || fields.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
fields.forEach(field -> {
|
||||
if (field.contains(".")) {
|
||||
String tmp[] = field.split("[.]", 2);
|
||||
try {
|
||||
Field field1 = type.getDeclaredField(tmp[0]);
|
||||
if (field1 != null) {
|
||||
exclude(field1.getType(), tmp[1]);
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
} else {
|
||||
getStringListFromMap(excludes, type).add(field);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseMessage<T> exclude(Collection<String> fields) {
|
||||
if (excludes == null) {
|
||||
excludes = new HashMap<>();
|
||||
}
|
||||
if (fields == null || fields.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
Class type;
|
||||
if (getResult() != null) {
|
||||
type = getResult().getClass();
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
exclude(type, fields);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseMessage<T> include(Collection<String> fields) {
|
||||
if (includes == null) {
|
||||
includes = new HashMap<>();
|
||||
}
|
||||
if (fields == null || fields.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
Class type;
|
||||
if (getResult() != null) {
|
||||
type = getResult().getClass();
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
include(type, fields);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponseMessage<T> exclude(Class type, String... fields) {
|
||||
return exclude(type, Arrays.asList(fields));
|
||||
}
|
||||
|
||||
public ResponseMessage<T> exclude(String... fields) {
|
||||
return exclude(Arrays.asList(fields));
|
||||
}
|
||||
|
||||
public ResponseMessage<T> include(String... fields) {
|
||||
return include(Arrays.asList(fields));
|
||||
}
|
||||
|
||||
protected Set<String> getStringListFromMap(Map<Class<?>, Set<String>> map, Class type) {
|
||||
return map.computeIfAbsent(type, k -> new HashSet<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONStringWithDateFormat(this, "yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
public ResponseMessage<T> status(int status) {
|
||||
this.status = status;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ApiModelProperty(hidden = true)
|
||||
public Map<Class<?>, Set<String>> getExcludes() {
|
||||
return excludes;
|
||||
}
|
||||
|
||||
@ApiModelProperty(hidden = true)
|
||||
public Map<Class<?>, Set<String>> getIncludes() {
|
||||
return includes;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public void setResult(T result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public void setTimestamp(Long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"groupId": "${project.groupId}",
|
||||
"artifactId": "${project.artifactId}",
|
||||
"path": "hsweb-commons/hsweb-commons-controller",
|
||||
|
||||
"gitCommitHash": "${git.commit.hash}",
|
||||
"gitRepository": "http://github.com/hs-web/hsweb-framework",
|
||||
"version": "${project.version}"
|
||||
}
|
||||
@@ -28,7 +28,21 @@
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.1-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -16,10 +16,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
package org.hswebframework.web.crud.entity;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 实体总接口,所有实体需实现此接口
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface Service {
|
||||
public interface Entity extends Serializable {
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.hswebframework.web.crud.entity;
|
||||
|
||||
/**
|
||||
* 逻辑删除
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0.6
|
||||
*/
|
||||
public interface LogicalDeleteEntity {
|
||||
|
||||
Boolean getDeleted();
|
||||
|
||||
void setDeleted(boolean deleted);
|
||||
|
||||
Long getDeleteTime();
|
||||
|
||||
void setDeleteTime(Long deleteTime);
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@ package org.hswebframework.web.crud.entity;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -38,7 +39,7 @@ public class PagerResult<E> {
|
||||
return new PagerResult<>(total, list);
|
||||
}
|
||||
|
||||
public static <E> PagerResult<E> of(int total, List<E> list, QueryParamEntity entity) {
|
||||
public static <E> PagerResult<E> of(int total, List<E> list, QueryParam entity) {
|
||||
PagerResult<E> pagerResult = new PagerResult<>(total, list);
|
||||
pagerResult.setPageIndex(entity.getThinkPageIndex());
|
||||
pagerResult.setPageSize(entity.getPageSize());
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.hswebframework.web.crud.entity;
|
||||
|
||||
/**
|
||||
* 记录创建信息的实体类,包括创建人和创建时间。
|
||||
* 此实体类与行级权限控制相关联:只能操作自己创建的数据
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface RecordCreationEntity extends Entity {
|
||||
|
||||
String creatorId = "creatorId";
|
||||
String createTime = "createTime";
|
||||
|
||||
String getCreatorId();
|
||||
|
||||
void setCreatorId(String creatorId);
|
||||
|
||||
Long getCreateTime();
|
||||
|
||||
void setCreateTime(Long createTime);
|
||||
|
||||
default void setCreateTimeNow() {
|
||||
setCreateTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
default String getCreatorIdProperty() {
|
||||
return creatorId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.hswebframework.web.crud.entity;
|
||||
|
||||
/**
|
||||
* 记录修改信息的实体类,包括修改人和修改时间。
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0.6
|
||||
*/
|
||||
public interface RecordModifierEntity extends Entity {
|
||||
|
||||
String modifierId = "modifierId";
|
||||
String modifyTime = "modifyTime";
|
||||
|
||||
String getModifierId();
|
||||
|
||||
void setModifierId(String modifierId);
|
||||
|
||||
Long getModifyTime();
|
||||
|
||||
void setModifyTime(Long modifyTime);
|
||||
|
||||
default void setModifyTimeNow() {
|
||||
setModifyTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
default String getModifierIdProperty() {
|
||||
return modifierId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.hswebframework.web.crud.entity.factory;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 默认的实体映射
|
||||
*
|
||||
* @author zhouhao
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DefaultMapperFactory extends Function<Class, MapperEntityFactory.Mapper> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.hswebframework.web.crud.entity.factory;
|
||||
|
||||
/**
|
||||
* 默认的属性复制器
|
||||
*
|
||||
* @author zhouhao
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DefaultPropertyCopier extends PropertyCopier<Object, Object> {
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.crud.entity.factory;
|
||||
|
||||
|
||||
/**
|
||||
* 实体工厂接口,系统各个地方使用此接口来创建实体,在实际编码中也应该使用此接口来创建实体,而不是使用new方式来创建
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see MapperEntityFactory
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface EntityFactory {
|
||||
/**
|
||||
* 根据类型创建实例
|
||||
* <p>
|
||||
* e.g.
|
||||
* <pre>
|
||||
* entityFactory.newInstance(UserEntity.class);
|
||||
* </pre>
|
||||
*
|
||||
* @param entityClass 要创建的class
|
||||
* @param <T> 类型
|
||||
* @return 创建结果
|
||||
*/
|
||||
<T> T newInstance(Class<T> entityClass);
|
||||
|
||||
|
||||
/**
|
||||
* 根据类型创建实例,如果类型无法创建,则使用默认类型进行创建
|
||||
* <p>
|
||||
* e.g.
|
||||
* <pre>
|
||||
* entityFactory.newInstance(UserEntity.class,SimpleUserEntity.class);
|
||||
* </pre>
|
||||
*
|
||||
* @param entityClass 要创建的class
|
||||
* @param defaultClass 默认class,当{@code entityClass}无法创建时使用此类型进行创建
|
||||
* @param <T> 类型
|
||||
* @return 实例
|
||||
*/
|
||||
<T> T newInstance(Class<T> entityClass, Class<? extends T> defaultClass);
|
||||
|
||||
/**
|
||||
* 创建实体并设置默认的属性
|
||||
*
|
||||
* @param entityClass 实体类型
|
||||
* @param defaultProperties 默认属性
|
||||
* @param <S> 默认属性的类型
|
||||
* @param <T> 实体类型
|
||||
* @return 创建结果
|
||||
* @see EntityFactory#copyProperties(Object, Object)
|
||||
*/
|
||||
default <S, T> T newInstance(Class<T> entityClass, S defaultProperties) {
|
||||
return copyProperties(defaultProperties, newInstance(entityClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建实体并设置默认的属性
|
||||
*
|
||||
* @param entityClass 实体类型
|
||||
* @param defaultClass 默认class
|
||||
* @param defaultProperties 默认属性
|
||||
* @param <S> 默认属性的类型
|
||||
* @param <T> 实体类型
|
||||
* @return 创建结果
|
||||
* @see EntityFactory#copyProperties(Object, Object)
|
||||
*/
|
||||
default <S, T> T newInstance(Class<T> entityClass, Class<? extends T> defaultClass, S defaultProperties) {
|
||||
return copyProperties(defaultProperties, newInstance(entityClass, defaultClass));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据类型获取实体的真实的实体类型,
|
||||
* 可通过此方法获取获取已拓展的实体类型,如:<br>
|
||||
* <code>
|
||||
* factory.getInstanceType(MyBeanInterface.class);
|
||||
* </code>
|
||||
*
|
||||
* @param entityClass 类型
|
||||
* @param <T> 泛型
|
||||
* @return 实体类型
|
||||
*/
|
||||
<T> Class<T> getInstanceType(Class<T> entityClass);
|
||||
|
||||
/**
|
||||
* 拷贝对象的属性
|
||||
*
|
||||
* @param source 要拷贝到的对象
|
||||
* @param target 被拷贝的对象
|
||||
* @param <S> 要拷贝对象的类型
|
||||
* @param <T> 被拷贝对象的类型
|
||||
* @return 被拷贝的对象
|
||||
*/
|
||||
<S, T> T copyProperties(S source, T target);
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.crud.entity.factory;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.hswebframework.utils.ClassUtils;
|
||||
import org.hswebframework.web.exception.NotFoundException;
|
||||
import org.hswebframework.web.bean.BeanFactory;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class MapperEntityFactory implements EntityFactory, BeanFactory {
|
||||
private Map<Class, Mapper> realTypeMapper = new HashMap<>();
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
private Map<String, PropertyCopier> copierCache = new HashMap<>();
|
||||
|
||||
private static final DefaultMapperFactory DEFAULT_MAPPER_FACTORY = clazz -> {
|
||||
String simpleClassName = clazz.getPackage().getName().concat(".Simple").concat(clazz.getSimpleName());
|
||||
try {
|
||||
return defaultMapper(Class.forName(simpleClassName));
|
||||
} catch (ClassNotFoundException ignore) {
|
||||
// throw new NotFoundException(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 默认的属性复制器
|
||||
*/
|
||||
private static final DefaultPropertyCopier DEFAULT_PROPERTY_COPIER = FastBeanCopier::copy;
|
||||
|
||||
private DefaultMapperFactory defaultMapperFactory = DEFAULT_MAPPER_FACTORY;
|
||||
|
||||
private DefaultPropertyCopier defaultPropertyCopier = DEFAULT_PROPERTY_COPIER;
|
||||
|
||||
|
||||
public MapperEntityFactory() {
|
||||
}
|
||||
|
||||
public <T> MapperEntityFactory(Map<Class<T>, Mapper> realTypeMapper) {
|
||||
this.realTypeMapper.putAll(realTypeMapper);
|
||||
}
|
||||
|
||||
public <T> MapperEntityFactory addMapping(Class<T> target, Mapper<? extends T> mapper) {
|
||||
realTypeMapper.put(target, mapper);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapperEntityFactory addCopier(PropertyCopier copier) {
|
||||
Class source = ClassUtils.getGenericType(copier.getClass(), 0);
|
||||
Class target = ClassUtils.getGenericType(copier.getClass(), 1);
|
||||
if (source == null || source == Object.class) {
|
||||
throw new UnsupportedOperationException("generic type " + source + " not support");
|
||||
}
|
||||
if (target == null || target == Object.class) {
|
||||
throw new UnsupportedOperationException("generic type " + target + " not support");
|
||||
}
|
||||
addCopier(source, target, copier);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <S, T> MapperEntityFactory addCopier(Class<S> source, Class<T> target, PropertyCopier<S, T> copier) {
|
||||
copierCache.put(getCopierCacheKey(source, target), copier);
|
||||
return this;
|
||||
}
|
||||
|
||||
private String getCopierCacheKey(Class source, Class target) {
|
||||
return source.getName().concat("->").concat(target.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S, T> T copyProperties(S source, T target) {
|
||||
Objects.requireNonNull(source);
|
||||
Objects.requireNonNull(target);
|
||||
try {
|
||||
PropertyCopier<S, T> copier = copierCache.<S, T>get(getCopierCacheKey(source.getClass(), target.getClass()));
|
||||
if (null != copier) {
|
||||
return copier.copyProperties(source, target);
|
||||
}
|
||||
|
||||
return (T) defaultPropertyCopier.copyProperties(source, target);
|
||||
} catch (Exception e) {
|
||||
logger.warn("copy properties error", e);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
protected <T> Mapper<T> initCache(Class<T> beanClass) {
|
||||
Mapper<T> mapper = null;
|
||||
Class<T> realType = null;
|
||||
ServiceLoader<T> serviceLoader = ServiceLoader.load(beanClass, this.getClass().getClassLoader());
|
||||
Iterator<T> iterator = serviceLoader.iterator();
|
||||
if (iterator.hasNext()) {
|
||||
realType = (Class<T>) iterator.next().getClass();
|
||||
}
|
||||
//尝试使用 Simple类,如: package.SimpleUserBean
|
||||
if (realType == null) {
|
||||
mapper = defaultMapperFactory.apply(beanClass);
|
||||
}
|
||||
if (!Modifier.isInterface(beanClass.getModifiers()) && !Modifier.isAbstract(beanClass.getModifiers())) {
|
||||
realType = beanClass;
|
||||
}
|
||||
if (mapper == null && realType != null) {
|
||||
if (logger.isDebugEnabled() && realType != beanClass) {
|
||||
logger.debug("use instance {} for {}", realType, beanClass);
|
||||
}
|
||||
mapper = new Mapper<>(realType, new DefaultInstanceGetter(realType));
|
||||
}
|
||||
if (mapper != null) {
|
||||
realTypeMapper.put(beanClass, mapper);
|
||||
}
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T newInstance(Class<T> beanClass) {
|
||||
return newInstance(beanClass, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T newInstance(Class<T> beanClass, Class<? extends T> defaultClass) {
|
||||
if (beanClass == null) {
|
||||
return null;
|
||||
}
|
||||
Mapper<T> mapper = realTypeMapper.get(beanClass);
|
||||
if (mapper != null) {
|
||||
return mapper.getInstanceGetter().get();
|
||||
}
|
||||
mapper = initCache(beanClass);
|
||||
if (mapper != null) {
|
||||
return mapper.getInstanceGetter().get();
|
||||
}
|
||||
if (defaultClass != null) {
|
||||
return newInstance(defaultClass);
|
||||
}
|
||||
if (Map.class == beanClass) {
|
||||
return (T) new HashMap<>();
|
||||
}
|
||||
if (List.class == beanClass) {
|
||||
return (T) new ArrayList<>();
|
||||
}
|
||||
if (Set.class == beanClass) {
|
||||
return (T) new HashSet<>();
|
||||
}
|
||||
|
||||
throw new NotFoundException("can't create instance for " + beanClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Class<T> getInstanceType(Class<T> beanClass) {
|
||||
Mapper<T> mapper = realTypeMapper.get(beanClass);
|
||||
if (null != mapper) {
|
||||
return mapper.getTarget();
|
||||
}
|
||||
mapper = initCache(beanClass);
|
||||
if (mapper != null) {
|
||||
return mapper.getTarget();
|
||||
}
|
||||
|
||||
return Modifier.isAbstract(beanClass.getModifiers())
|
||||
|| Modifier.isInterface(beanClass.getModifiers())
|
||||
? null : beanClass;
|
||||
}
|
||||
|
||||
public void setDefaultMapperFactory(DefaultMapperFactory defaultMapperFactory) {
|
||||
Objects.requireNonNull(defaultMapperFactory);
|
||||
this.defaultMapperFactory = defaultMapperFactory;
|
||||
}
|
||||
|
||||
public void setDefaultPropertyCopier(DefaultPropertyCopier defaultPropertyCopier) {
|
||||
this.defaultPropertyCopier = defaultPropertyCopier;
|
||||
}
|
||||
|
||||
public static class Mapper<T> {
|
||||
Class<T> target;
|
||||
Supplier<T> instanceGetter;
|
||||
|
||||
public Mapper(Class<T> target, Supplier<T> instanceGetter) {
|
||||
this.target = target;
|
||||
this.instanceGetter = instanceGetter;
|
||||
}
|
||||
|
||||
public Class<T> getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public Supplier<T> getInstanceGetter() {
|
||||
return instanceGetter;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> Mapper<T> defaultMapper(Class<T> target) {
|
||||
return new Mapper<>(target, defaultInstanceGetter(target));
|
||||
}
|
||||
|
||||
public static <T> Supplier<T> defaultInstanceGetter(Class<T> clazz) {
|
||||
return new DefaultInstanceGetter<>(clazz);
|
||||
}
|
||||
|
||||
static class DefaultInstanceGetter<T> implements Supplier<T> {
|
||||
Class<T> type;
|
||||
|
||||
public DefaultInstanceGetter(Class<T> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public T get() {
|
||||
return type.newInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.hswebframework.web.crud.entity.factory;
|
||||
|
||||
/**
|
||||
* 属性复制接口,用于自定义属性复制
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface PropertyCopier<S, T> {
|
||||
T copyProperties(S source, T target);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.hswebframework.web.crud.events;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class EntityCreatedEvent<E> implements Serializable {
|
||||
|
||||
private E entity;
|
||||
|
||||
private Class<E> entityType;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.hswebframework.web.crud.events;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public class EntityModifyEvent<E> implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = -7158901204884303777L;
|
||||
|
||||
private E before;
|
||||
|
||||
private E after;
|
||||
|
||||
private Class<E> entityType;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.hswebframework.web.crud.service;
|
||||
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.ezorm.rdb.mapping.SyncDelete;
|
||||
import org.hswebframework.ezorm.rdb.mapping.SyncQuery;
|
||||
import org.hswebframework.ezorm.rdb.mapping.SyncRepository;
|
||||
import org.hswebframework.ezorm.rdb.mapping.SyncUpdate;
|
||||
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
|
||||
import org.hswebframework.web.crud.entity.PagerResult;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface CrudService<E, K> {
|
||||
SyncRepository<E, K> getRepository();
|
||||
|
||||
default SyncQuery<E> createQuery() {
|
||||
return getRepository().createQuery();
|
||||
}
|
||||
|
||||
default SyncUpdate<E> createUpdate() {
|
||||
return getRepository().createUpdate();
|
||||
}
|
||||
|
||||
default SyncDelete createDelete() {
|
||||
return getRepository().createDelete();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
default Optional<E> findById(K id) {
|
||||
return getRepository()
|
||||
.findById(id);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
default List<E> findById(Collection<K> id) {
|
||||
return getRepository()
|
||||
.findById(id);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
default SaveResult save(E... entityArr) {
|
||||
return getRepository()
|
||||
.save(entityArr);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
default SaveResult save(Collection<E> entityArr) {
|
||||
return getRepository()
|
||||
.save(entityArr);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
default int deleteById(K... idArr) {
|
||||
return getRepository().deleteById(idArr);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
default int deleteById(Collection<K> idArr) {
|
||||
return getRepository().deleteById(idArr);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
default List<E> query(QueryParam queryParam) {
|
||||
return createQuery().setParam(queryParam).fetch();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
default PagerResult<E> queryPager(QueryParam param) {
|
||||
|
||||
int count = count(param);
|
||||
if (count == 0) {
|
||||
return PagerResult.empty();
|
||||
}
|
||||
param.rePaging(count);
|
||||
|
||||
return PagerResult.of(count, query(param), param);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
default Integer count(QueryParam param) {
|
||||
return getRepository()
|
||||
.createQuery()
|
||||
.setParam(param)
|
||||
.count();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.hswebframework.web.crud.service;
|
||||
|
||||
import org.hswebframework.ezorm.rdb.mapping.SyncRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
public class DefaultCrudService<E,K> implements CrudService<E,K> {
|
||||
|
||||
@Autowired
|
||||
private SyncRepository<E, K> repository;
|
||||
|
||||
@Override
|
||||
public SyncRepository<E, K> getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package org.hswebframework.web.crud.service;
|
||||
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveDelete;
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveQuery;
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveUpdate;
|
||||
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
|
||||
import org.hswebframework.web.crud.entity.PagerResult;
|
||||
import org.reactivestreams.Publisher;
|
||||
@@ -15,6 +18,18 @@ public interface ReactiveCrudService<E, K> {
|
||||
|
||||
ReactiveRepository<E, K> getRepository();
|
||||
|
||||
default ReactiveQuery<E> createQuery() {
|
||||
return getRepository().createQuery();
|
||||
}
|
||||
|
||||
default ReactiveUpdate<E> createUpdate() {
|
||||
return getRepository().createUpdate();
|
||||
}
|
||||
|
||||
default ReactiveDelete createDelete() {
|
||||
return getRepository().createDelete();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
default Mono<E> findById(Mono<K> publisher) {
|
||||
return getRepository().findById(publisher);
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
package org.hswebframework.web.commons.entity.factory;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.hswebframework.web.NotFoundException;
|
||||
import org.hswebframework.web.exception.NotFoundException;
|
||||
import org.hswebframework.utils.ClassUtils;
|
||||
import org.hswebframework.web.bean.BeanFactory;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2019 http://www.hswebframework.org
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
~
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-commons</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<description>通用增删改查-通用model模块</description>
|
||||
|
||||
<artifactId>hsweb-commons-model</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-commons-bean</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 http://www.hswebframework.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.commons.model;
|
||||
|
||||
import org.hswebframework.web.commons.bean.ValidateBean;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface Model extends ValidateBean {
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ /*
|
||||
~ * Copyright 2019 http://www.hswebframework.org
|
||||
~ *
|
||||
~ * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ * you may not use this file except in compliance with the License.
|
||||
~ * You may obtain a copy of the License at
|
||||
~ *
|
||||
~ * http://www.apache.org/licenses/LICENSE-2.0
|
||||
~ *
|
||||
~ * Unless required by applicable law or agreed to in writing, software
|
||||
~ * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ * See the License for the specific language governing permissions and
|
||||
~ * limitations under the License.
|
||||
~ */
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-commons-service</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<description>通用增删改查-通用服务接口模块</description>
|
||||
|
||||
<artifactId>hsweb-commons-service-api</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-commons-dao-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
/**
|
||||
* 实体创建服务接口,通过此接口创建实体.在创建实体类时,建议使用此接口进行创建,而不是使用new
|
||||
* 如:
|
||||
* <code>
|
||||
* YourBean bean = service.createEntity();
|
||||
* </code>
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface CreateEntityService<E> extends Service {
|
||||
/**
|
||||
* 创建实体
|
||||
*
|
||||
* @return 实体
|
||||
*/
|
||||
E createEntity();
|
||||
|
||||
Class<E> getEntityInstanceType();
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
/**
|
||||
* 通用Service,实现增删改查
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface CrudService<E, PK> extends
|
||||
QueryByEntityService<E>,
|
||||
UpdateService<E,PK>,
|
||||
InsertService<E, PK>,
|
||||
DeleteService<E,PK>,
|
||||
CreateEntityService<E>,
|
||||
QueryService<E, PK> {
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface DeleteService<E,PK> extends Service {
|
||||
/**
|
||||
* 根据主键删除记录
|
||||
*
|
||||
* @param pk 主键
|
||||
* @return 影响记录数
|
||||
*/
|
||||
E deleteByPk(PK pk);
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface InsertService<E, PK> extends Service {
|
||||
|
||||
/**
|
||||
* 添加一条数据
|
||||
*
|
||||
* @param data 要添加的数据
|
||||
* @return 添加后生成的主键
|
||||
*/
|
||||
PK insert(E data);
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.web.commons.entity.Entity;
|
||||
import org.hswebframework.web.commons.entity.PagerResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 根据实体类参数执行各种查询的通用服务类
|
||||
*
|
||||
* @param <E> 实体类型
|
||||
* @author zhouhao
|
||||
* @see org.hswebframework.web.commons.entity.param.QueryParamEntity
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface QueryByEntityService<E> extends Service {
|
||||
|
||||
/**
|
||||
* 按分页查询
|
||||
*
|
||||
* @param param 参数
|
||||
* @return 分页查询结果
|
||||
*/
|
||||
PagerResult<E> selectPager(Entity param);
|
||||
|
||||
/**
|
||||
* 直接查询
|
||||
*
|
||||
* @param param 查询参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
List<E> select(Entity param);
|
||||
|
||||
/**
|
||||
* 查询总数
|
||||
*
|
||||
* @param param 查询参数
|
||||
* @return 总数
|
||||
*/
|
||||
int count(Entity param);
|
||||
|
||||
/**
|
||||
* 查询单条数据,如果存在多条数据,则返回第一条
|
||||
*
|
||||
* @param param 查询参数
|
||||
* @return 查询结果
|
||||
*/
|
||||
E selectSingle(Entity param);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 http://www.hswebframework.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 查询服务接口,提供基本的查询功能
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
* @see QueryByEntityService
|
||||
*/
|
||||
public interface QueryService<E, PK> extends Service {
|
||||
|
||||
/**
|
||||
* 根据主键查询
|
||||
* @param id 主键
|
||||
* @return 查询结果,无结果时返回{@code null}
|
||||
*/
|
||||
E selectByPk(PK id);
|
||||
|
||||
/**
|
||||
* 根据多个主键查询
|
||||
* @param id 主键集合
|
||||
* @return 查询结果,如果无结果返回空集合,而不是返回{@code null}
|
||||
*/
|
||||
List<E> selectByPk(List<PK> id);
|
||||
|
||||
/**
|
||||
* 查询所有结果
|
||||
* @return 所有结果,如果无结果则返回空集合,而不是返回{@code null}
|
||||
*/
|
||||
List<E> select();
|
||||
|
||||
/**
|
||||
* 查询结果总数
|
||||
* @return 结果总数
|
||||
*/
|
||||
int count();
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 http://www.hswebframework.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.web.commons.entity.TreeSupportEntity;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 树结构实体服务,提供对树结果实体的常用操作
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface TreeService<E extends TreeSupportEntity, PK> extends Service {
|
||||
|
||||
/**
|
||||
* 查询所有父节点
|
||||
* @param childId 子节点id
|
||||
* @return 父节点集合
|
||||
*/
|
||||
List<E> selectParentNode(PK childId);
|
||||
|
||||
/**
|
||||
* 根据父节点id获取子节点数据
|
||||
*
|
||||
* @param parentId 父节点ID
|
||||
* @return 子节点数据
|
||||
*/
|
||||
List<E> selectChildNode(PK parentId);
|
||||
|
||||
/**
|
||||
* 根据父节点id,获取所有子节点的数据,包含字节点的字节点
|
||||
*
|
||||
* @param parentId 父节点ID
|
||||
* @return 所有子节点的数据
|
||||
*/
|
||||
List<E> selectAllChildNode(PK parentId);
|
||||
|
||||
/**
|
||||
* 批量修改数据,如果集合中的数据不存在,则将会进行新增
|
||||
*
|
||||
* @param data 数据集合
|
||||
* @return 修改的数量
|
||||
*/
|
||||
int updateBatch(Collection<E> data);
|
||||
|
||||
/**
|
||||
* 批量添加数据
|
||||
*
|
||||
* @param data 数据集合
|
||||
* @return 被添加数据集合的主键
|
||||
*/
|
||||
List<PK> insertBatch(Collection<E> data);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UpdateService<E, PK> extends Service {
|
||||
/**
|
||||
* 修改记录信息
|
||||
*
|
||||
* @param data 要修改的对象
|
||||
* @return 影响记录数
|
||||
*/
|
||||
int updateByPk(PK id, E data);
|
||||
|
||||
/**
|
||||
* 批量修改记录
|
||||
*
|
||||
* @param data 要修改的记录集合
|
||||
* @return 影响记录数
|
||||
*/
|
||||
int updateByPk(List<E> data);
|
||||
|
||||
/**
|
||||
* 保存或修改
|
||||
*
|
||||
* @param e 要修改的数据
|
||||
* @return 影响记录数
|
||||
*/
|
||||
PK saveOrUpdate(E e);
|
||||
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface Validator<T> {
|
||||
boolean validate(T data);
|
||||
|
||||
default String getErrorMessage() {
|
||||
return "{validation_fail}";
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
# 通用服务类
|
||||
提供通用增删改查服务
|
||||
|
||||
## DSL查改删
|
||||
查询,实现`DefaultDSLQueryService`接口
|
||||
```java
|
||||
// select * from user where name = ? limit 0,1
|
||||
createQuery().where("name","张三").single();
|
||||
```
|
||||
```java
|
||||
// select * from user where name = ? or name = ?
|
||||
createQuery().where("name","张三").or().is("name","李四").list();
|
||||
```
|
||||
|
||||
```java
|
||||
//select * from user where name = ? and (age> ? and age <?)
|
||||
createQuery().where("name","张三").nest().gt("age",10).or().lt("age",20).end().list();
|
||||
```
|
||||
|
||||
修改,实现`DefaultDSLUpdateService`接口
|
||||
```java
|
||||
// update user set ... where name = ?
|
||||
createUpdate(data).where("name","张三").exec();
|
||||
//不会修改为null的属性,
|
||||
```
|
||||
|
||||
```java
|
||||
// update user set name=?,age=? where name = ?
|
||||
createUpdate(data).include("name","age").where("name","张三").exec();
|
||||
```
|
||||
|
||||
```java
|
||||
// update user set name=? where name = ?
|
||||
createUpdate().set("name","新张三").where("name","张三").exec();
|
||||
```
|
||||
|
||||
删除,实现`DefaultDSLDeleteService`接口
|
||||
```java
|
||||
//delete from user where name = ?
|
||||
createDelete().where("name","张三").exec();
|
||||
```
|
||||
|
||||
查改删,条件使用的方式都相同.
|
||||
@@ -1,81 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ /*
|
||||
~ * Copyright 2019 http://www.hswebframework.org
|
||||
~ *
|
||||
~ * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ * you may not use this file except in compliance with the License.
|
||||
~ * You may obtain a copy of the License at
|
||||
~ *
|
||||
~ * http://www.apache.org/licenses/LICENSE-2.0
|
||||
~ *
|
||||
~ * Unless required by applicable law or agreed to in writing, software
|
||||
~ * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ * See the License for the specific language governing permissions and
|
||||
~ * limitations under the License.
|
||||
~ */
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-commons-service</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hsweb-commons-service-simple</artifactId>
|
||||
|
||||
<description>通用增删改查-通用服务本地实现模块</description>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-commons-service-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-commons-dao-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aspects</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,147 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.utils.ClassUtils;
|
||||
import org.hswebframework.web.NotFoundException;
|
||||
import org.hswebframework.web.commons.entity.Entity;
|
||||
import org.hswebframework.web.commons.entity.factory.EntityFactory;
|
||||
import org.hswebframework.web.validate.SimpleValidateResults;
|
||||
import org.hswebframework.web.validate.ValidationException;
|
||||
import org.hswebframework.web.validator.LogicPrimaryKeyValidator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.Validator;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 抽象服务类,提供通用模板方法、类,如验证器,实体工厂等
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see CreateEntityService
|
||||
* @see Service
|
||||
*/
|
||||
public abstract class AbstractService<E extends Entity, PK> implements CreateEntityService<E>, Service {
|
||||
protected Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
protected Validator validator;
|
||||
|
||||
protected EntityFactory entityFactory;
|
||||
|
||||
protected LogicPrimaryKeyValidator logicPrimaryKeyValidator;
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setEntityFactory(EntityFactory entityFactory) {
|
||||
this.entityFactory = entityFactory;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setLogicPrimaryKeyValidator(LogicPrimaryKeyValidator logicPrimaryKeyValidator) {
|
||||
this.logicPrimaryKeyValidator = logicPrimaryKeyValidator;
|
||||
}
|
||||
|
||||
protected Class<E> entityType;
|
||||
|
||||
protected Class<PK> primaryKeyType;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public AbstractService() {
|
||||
primaryKeyType = (Class<PK>) ClassUtils.getGenericType(this.getClass(), 1);
|
||||
entityType = (Class<E>) ClassUtils.getGenericType(this.getClass(), 0);
|
||||
}
|
||||
|
||||
protected boolean entityFactoryIsEnabled() {
|
||||
if (entityFactory == null) {
|
||||
logger.warn("entityFactory is null!");
|
||||
}
|
||||
return null != entityFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<E> getEntityInstanceType() {
|
||||
return entityFactory.getInstanceType(getEntityType());
|
||||
}
|
||||
|
||||
public Class<E> getEntityType() {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
protected Class<PK> getPrimaryKeyType() {
|
||||
return primaryKeyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E createEntity() {
|
||||
if (!entityFactoryIsEnabled()) {
|
||||
throw new UnsupportedOperationException("{unsupported_operation}");
|
||||
}
|
||||
return entityFactory.newInstance(getEntityType());
|
||||
}
|
||||
|
||||
protected <T> void tryValidateProperty(org.hswebframework.web.service.Validator<T> validator, String property, T value) {
|
||||
if (validator != null) {
|
||||
if (!validator.validate(value)) {
|
||||
throw new ValidationException(validator.getErrorMessage(), property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected <T> void tryValidateProperty(org.hswebframework.web.service.Validator<T> validator, String property, T value, String message) {
|
||||
if (validator != null) {
|
||||
if (!validator.validate(value)) {
|
||||
throw new ValidationException(message, property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void tryValidateProperty(boolean success, String property, String message) {
|
||||
if (!success) {
|
||||
throw new ValidationException(message, property);
|
||||
}
|
||||
}
|
||||
|
||||
protected void tryValidate(Object data, String property, Class... groups) {
|
||||
validate(() -> validator.validateProperty(data, property, groups));
|
||||
}
|
||||
|
||||
protected <T> void tryValidate(Class<T> type, String property, Object value, Class... groups) {
|
||||
validate(() -> validator.validateValue(type, property, value, groups));
|
||||
}
|
||||
|
||||
protected void tryValidate(Object data, Class... groups) {
|
||||
validate(() -> validator.validate(data, groups));
|
||||
}
|
||||
|
||||
private <T> void validate(Supplier<Set<ConstraintViolation<T>>> validatorSetFunction) {
|
||||
if (validator == null) {
|
||||
logger.warn("validator is null!");
|
||||
return;
|
||||
}
|
||||
SimpleValidateResults results = new SimpleValidateResults();
|
||||
validatorSetFunction.get()
|
||||
.forEach(violation -> results.addResult(violation.getPropertyPath().toString(), violation.getMessage()));
|
||||
if (!results.isSuccess()) {
|
||||
throw new ValidationException(results);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertNotNull(Object data) {
|
||||
assertNotNull(data, "{data_not_found}");
|
||||
}
|
||||
|
||||
public static void assertNotNull(Object data, String message) {
|
||||
if (null == data) {
|
||||
throw new NotFoundException(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 http://www.hswebframework.org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.utils.RandomUtil;
|
||||
import org.hswebframework.web.commons.entity.TreeSortSupportEntity;
|
||||
import org.hswebframework.web.commons.entity.TreeSupportEntity;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 抽象树形结构服务类
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see TreeSortSupportEntity
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class AbstractTreeSortService<E extends TreeSortSupportEntity<PK>, PK>
|
||||
extends GenericEntityService<E, PK> implements TreeService<E, PK> {
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<E> selectParentNode(PK childId) {
|
||||
assertNotNull(childId);
|
||||
E old = selectByPk(childId);
|
||||
if (null == old) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return createQuery()
|
||||
.where()
|
||||
// where ? like concat(path,'%')
|
||||
.and("path$like$reverse$startWith", old.getPath())
|
||||
.listNoPaging();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<E> selectAllChildNode(PK parentId) {
|
||||
assertNotNull(parentId);
|
||||
E old = selectByPk(parentId);
|
||||
if (null == old) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return createQuery()
|
||||
.where()
|
||||
.like$(TreeSupportEntity.path, old.getPath())
|
||||
.listNoPaging();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<E> selectChildNode(PK parentId) {
|
||||
assertNotNull(parentId);
|
||||
return createQuery()
|
||||
.where(TreeSupportEntity.parentId, parentId)
|
||||
.listNoPaging();
|
||||
}
|
||||
|
||||
//当父节点不存在时,创建parentId
|
||||
@SuppressWarnings("unchecked")
|
||||
protected PK createParentIdOnExists() {
|
||||
if (getPrimaryKeyType() == String.class) {
|
||||
return (PK) "-1";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void applyPath(E entity) {
|
||||
if (StringUtils.isEmpty(entity.getParentId())) {
|
||||
if (entity.getSortIndex() == null) {
|
||||
entity.setSortIndex(0L);
|
||||
}
|
||||
entity.setParentId(createParentIdOnExists());
|
||||
entity.setLevel(0);
|
||||
entity.setPath(RandomUtil.randomChar(4));
|
||||
return;
|
||||
}
|
||||
if (!StringUtils.isEmpty(entity.getPath())) {
|
||||
return;
|
||||
}
|
||||
|
||||
TreeSortSupportEntity<PK> parent = selectByPk(entity.getParentId());
|
||||
if (null == parent) {
|
||||
if (entity.getSortIndex() == null) {
|
||||
entity.setSortIndex(0L);
|
||||
}
|
||||
entity.setParentId(createParentIdOnExists());
|
||||
entity.setPath(RandomUtil.randomChar(4));
|
||||
entity.setLevel(0);
|
||||
} else {
|
||||
if (entity.getSortIndex() == null && parent.getSortIndex() != null) {
|
||||
entity.setSortIndex(parent.getSortIndex() * 10);
|
||||
}
|
||||
entity.setPath(parent.getPath() + "-" + RandomUtil.randomChar(4));
|
||||
entity.setLevel(entity.getPath().split("[-]").length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PK insert(E entity) {
|
||||
if (StringUtils.isEmpty(entity.getId())) {
|
||||
entity.setId(getIDGenerator().generate());
|
||||
}
|
||||
applyPath(entity);
|
||||
List<E> childrenList = new ArrayList<>();
|
||||
TreeSupportEntity.expandTree2List(entity, childrenList, getIDGenerator());
|
||||
childrenList.forEach(this::saveOrUpdateForSingle);
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PK> insertBatch(Collection<E> data) {
|
||||
return data.stream()
|
||||
.map(this::insert)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateBatch(Collection<E> data) {
|
||||
assertNotNull(data);
|
||||
return data.stream()
|
||||
.mapToInt(this::updateByPk)
|
||||
.sum();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateByPk(E entity) {
|
||||
assertNotNull(entity);
|
||||
List<E> childrenList = new ArrayList<>();
|
||||
TreeSupportEntity.expandTree2List(entity, childrenList, getIDGenerator());
|
||||
childrenList.forEach(this::saveOrUpdateForSingle);
|
||||
return childrenList.size() + 1;
|
||||
}
|
||||
|
||||
protected PK saveOrUpdateForSingle(E entity) {
|
||||
assertNotNull(entity);
|
||||
PK id = entity.getId();
|
||||
if (StringUtils.isEmpty(id) || this.selectByPk(id) == null) {
|
||||
if (StringUtils.isEmpty(id)) {
|
||||
entity.setId(getIDGenerator().generate());
|
||||
}
|
||||
applyPath(entity);
|
||||
return super.insert(entity);
|
||||
}
|
||||
super.updateByPk(entity);
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E deleteByPk(PK id) {
|
||||
E old = selectByPk(id);
|
||||
assertNotNull(old);
|
||||
if (StringUtils.isEmpty(old.getPath())) {
|
||||
getDao().deleteByPk(id);
|
||||
} else {
|
||||
DefaultDSLDeleteService.createDelete(getDao())
|
||||
// where path like 'path%'
|
||||
.where().like$(TreeSupportEntity.path, old.getPath())
|
||||
.exec();
|
||||
}
|
||||
return old;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.ezorm.core.dsl.Delete;
|
||||
import org.hswebframework.web.commons.entity.param.DeleteParamEntity;
|
||||
import org.hswebframework.web.dao.dynamic.DeleteByEntityDao;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface DefaultDSLDeleteService<E, PK> extends DefaultDeleteService<E, PK> {
|
||||
DeleteByEntityDao getDao();
|
||||
|
||||
default Delete<DeleteParamEntity> createDelete() {
|
||||
Delete<DeleteParamEntity> delete = new Delete<>(new DeleteParamEntity());
|
||||
delete.setExecutor(getDao()::delete);
|
||||
return delete;
|
||||
}
|
||||
|
||||
static Delete<DeleteParamEntity> createDelete(DeleteByEntityDao deleteDao) {
|
||||
Delete<DeleteParamEntity> update = new Delete<>(new DeleteParamEntity());
|
||||
update.setExecutor(deleteDao::delete);
|
||||
return update;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义一个删除执行器。创建dsl数据删除操作对象
|
||||
*
|
||||
* @param executor 执行器
|
||||
* @return {@link Delete}
|
||||
* @since 3.0
|
||||
*/
|
||||
static Delete<DeleteParamEntity> createDelete(Delete.Executor<DeleteParamEntity> executor) {
|
||||
Delete<DeleteParamEntity> update = new Delete<>(new DeleteParamEntity());
|
||||
update.setExecutor(executor);
|
||||
return update;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.ezorm.core.dsl.Query;
|
||||
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
|
||||
import org.hswebframework.web.dao.dynamic.QueryByEntityDao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface DefaultDSLQueryService<E, PK>
|
||||
extends DefaultQueryByEntityService<E>, QueryService<E, PK> {
|
||||
|
||||
@Override
|
||||
default List<E> select() {
|
||||
return createQuery().noPaging().list();
|
||||
}
|
||||
|
||||
@Override
|
||||
default int count() {
|
||||
return createQuery().total();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建本服务的dsl查询操作对象
|
||||
* 可通过返回的Query对象进行dsl方式操作如:<br>
|
||||
* <code>
|
||||
* createQuery().where("id",1).single();
|
||||
* </code>
|
||||
*
|
||||
* @return {@link Query}
|
||||
* @see Query
|
||||
* @see org.hswebframework.ezorm.core.Conditional
|
||||
* @since 3.0
|
||||
*/
|
||||
default Query<E, QueryParamEntity> createQuery() {
|
||||
Query<E, QueryParamEntity> query = Query.empty(new QueryParamEntity());
|
||||
query.setListExecutor(this::select);
|
||||
query.setTotalExecutor(this::count);
|
||||
query.setSingleExecutor(this::selectSingle);
|
||||
query.noPaging();
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定一个dao映射接口,接口需继承{@link QueryByEntityDao}创建dsl数据查询对象<br>
|
||||
* 可通过返回的Query对象进行dsl方式操作如:<br>
|
||||
* <code>
|
||||
* createQuery(userMapper).where("id",1).single();
|
||||
* </code>
|
||||
*
|
||||
* @param dao dao接口
|
||||
* @param <PO> PO泛型
|
||||
* @return {@link Query}
|
||||
* @see Query
|
||||
* @see org.hswebframework.ezorm.core.Conditional
|
||||
* @since 3.0
|
||||
*/
|
||||
static <PO> Query<PO, QueryParamEntity> createQuery(QueryByEntityDao<PO> dao) {
|
||||
Query<PO, QueryParamEntity> query = new Query<>(new QueryParamEntity());
|
||||
query.setListExecutor(dao::query);
|
||||
query.setTotalExecutor(dao::count);
|
||||
query.setSingleExecutor((param) -> {
|
||||
param.doPaging(0, 1);
|
||||
List<PO> list = dao.query(param);
|
||||
if (null == list || list.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return list.get(0);
|
||||
}
|
||||
});
|
||||
query.noPaging();
|
||||
return query;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.ezorm.core.dsl.Update;
|
||||
import org.hswebframework.web.dao.dynamic.UpdateByEntityDao;
|
||||
import org.hswebframework.web.commons.entity.param.UpdateParamEntity;
|
||||
|
||||
/**
|
||||
* 默认的DSL方式更新服务
|
||||
*
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface DefaultDSLUpdateService<E, PK> extends UpdateService<E, PK> {
|
||||
|
||||
UpdateByEntityDao getDao();
|
||||
|
||||
default Update<E, UpdateParamEntity<E>> createUpdate(E data) {
|
||||
return createUpdate(getDao(), data);
|
||||
}
|
||||
|
||||
default Update<E, UpdateParamEntity<E>> createUpdate() {
|
||||
return createUpdate(getDao());
|
||||
}
|
||||
|
||||
static <E> Update<E, UpdateParamEntity<E>> createUpdate(UpdateByEntityDao dao) {
|
||||
return Update.build(dao::update, new UpdateParamEntity<>());
|
||||
}
|
||||
|
||||
static <E> Update<E, UpdateParamEntity<E>> createUpdate(UpdateByEntityDao dao, E data) {
|
||||
return Update.build(dao::update, new UpdateParamEntity<>(data));
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface DefaultDeleteService<E,PK> extends DeleteService<E,PK> {
|
||||
/**
|
||||
* 根据主键删除记录
|
||||
*
|
||||
* @param pk 主键
|
||||
* @return 影响记录数
|
||||
*/
|
||||
@Override
|
||||
E deleteByPk(PK pk);
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.hswebframework.ezorm.core.dsl.Query;
|
||||
import org.hswebframework.ezorm.core.param.TermType;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
|
||||
import org.hswebframework.web.validator.LogicPrimaryKey;
|
||||
import org.hswebframework.web.validator.LogicPrimaryKeyValidator;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@Slf4j
|
||||
public class DefaultLogicPrimaryKeyValidator implements LogicPrimaryKeyValidator {
|
||||
|
||||
private static final Map<Class, Map<Class, Validator>> validatorCache = new HashMap<>();
|
||||
|
||||
|
||||
private static final DefaultLogicPrimaryKeyValidator instrance = new DefaultLogicPrimaryKeyValidator();
|
||||
|
||||
protected DefaultLogicPrimaryKeyValidator() {
|
||||
}
|
||||
|
||||
public static DefaultLogicPrimaryKeyValidator getInstrance() {
|
||||
return instrance;
|
||||
}
|
||||
|
||||
private static final Validator ALWAYS_PASSED_VALIDATOR = bean -> {
|
||||
return Result.passed();
|
||||
};
|
||||
|
||||
public static <T> void registerQuerySuppiler(Class<T> type, Function<T, Query<T, QueryParamEntity>> querySupplier) {
|
||||
validatorCache.computeIfAbsent(type, instrance::createValidator)
|
||||
.values()
|
||||
.stream()
|
||||
.filter(DefaultValidator.class::isInstance)
|
||||
.map(DefaultValidator.class::cast)
|
||||
.forEach(validator -> validator.querySupplier = querySupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result validate(Object bean, Class... groups) {
|
||||
|
||||
Class target = ClassUtils.getUserClass(bean);
|
||||
Result result;
|
||||
if (null != groups && groups.length > 0) {
|
||||
result = Arrays.stream(groups)
|
||||
.map(group ->
|
||||
validatorCache.computeIfAbsent(target, this::createValidator)
|
||||
.getOrDefault(group, ALWAYS_PASSED_VALIDATOR)
|
||||
.doValidate(bean))
|
||||
.filter(Result::isError)
|
||||
.findFirst()
|
||||
.orElseGet(Result::passed);
|
||||
|
||||
} else {
|
||||
result = validatorCache.computeIfAbsent(target, this::createValidator)
|
||||
.getOrDefault(Void.class, ALWAYS_PASSED_VALIDATOR)
|
||||
.doValidate(bean);
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
protected Map<Class, Validator> createValidator(Class target) {
|
||||
//属性名:注解
|
||||
Map<String, LogicPrimaryKey> keys = new HashMap<>();
|
||||
|
||||
ReflectionUtils.doWithFields(target, field -> {
|
||||
LogicPrimaryKey primaryKey = field.getAnnotation(LogicPrimaryKey.class);
|
||||
if (primaryKey != null) {
|
||||
keys.put(field.getName(), primaryKey);
|
||||
}
|
||||
});
|
||||
|
||||
//获取类上的注解
|
||||
Class[] tempClass = new Class[]{target};
|
||||
LogicPrimaryKey classAnn = null;
|
||||
Class[] empty = new Class[0];
|
||||
|
||||
while (tempClass.length != 0) {
|
||||
|
||||
for (Class group : tempClass) {
|
||||
classAnn = AnnotationUtils.findAnnotation(group, LogicPrimaryKey.class);
|
||||
if (null != classAnn) {
|
||||
if (classAnn.value().length > 0) {
|
||||
for (String field : classAnn.value()) {
|
||||
keys.put(field, classAnn);
|
||||
}
|
||||
tempClass = empty;
|
||||
continue;
|
||||
} else {
|
||||
//如果注解没有指定字段则从group中获取
|
||||
tempClass = classAnn.groups();
|
||||
if (tempClass.length == 1 && tempClass[0] == Void.class) {
|
||||
log.warn("类{}的注解{}无效,请设置value属性或者group属性", classAnn, tempClass);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tempClass = empty;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (keys.isEmpty()) {
|
||||
return new java.util.HashMap<>();
|
||||
}
|
||||
return keys.entrySet()
|
||||
.stream()
|
||||
.flatMap(entry -> Stream.of(entry.getValue().groups())
|
||||
.flatMap(group -> Optional.ofNullable(entry.getValue().value())
|
||||
.map(Arrays::asList)
|
||||
.filter(CollectionUtils::isNotEmpty)
|
||||
.orElse(Arrays.asList(entry.getKey()))
|
||||
.stream()
|
||||
.map(field -> LogicPrimaryKeyField.builder()
|
||||
.field(field)
|
||||
.termType(entry.getValue().termType())
|
||||
.condition(entry.getValue().condition())
|
||||
.matchNullOrEmpty(entry.getValue().matchNullOrEmpty())
|
||||
.group(group)
|
||||
.build())
|
||||
))
|
||||
.collect(Collectors.groupingBy(
|
||||
//按group分组
|
||||
LogicPrimaryKeyField::getGroup,
|
||||
//将每一组的集合构造为验证器对象
|
||||
Collectors.collectingAndThen(
|
||||
Collectors.mapping(Function.identity(), Collectors.toSet())
|
||||
, list -> DefaultValidator.builder()
|
||||
.infos(list)
|
||||
.targetType(target)
|
||||
.build())
|
||||
)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
interface Validator<T> {
|
||||
Result doValidate(T bean);
|
||||
}
|
||||
|
||||
@Builder
|
||||
static class DefaultValidator<T> implements Validator<T> {
|
||||
private Set<LogicPrimaryKeyField> infos = new HashSet<>();
|
||||
|
||||
private Class<T> targetType;
|
||||
|
||||
private volatile Function<T, Query<T, QueryParamEntity>> querySupplier;
|
||||
|
||||
public Result doValidate(T bean) {
|
||||
if (querySupplier == null) {
|
||||
log.warn("未设置查询函数," +
|
||||
"你可以在服务初始化的时候通过调用" +
|
||||
"DefaultLogicPrimaryKeyValidator" +
|
||||
".registerQuerySuppiler({},bean -> this.createQuery().not(\"id\", bean.getId()))" +
|
||||
"进行设置"
|
||||
, targetType);
|
||||
return Result.passed();
|
||||
}
|
||||
|
||||
Query<T, QueryParamEntity> query = querySupplier.apply(bean);
|
||||
|
||||
//转为map
|
||||
Map<String, Object> mapBean = FastBeanCopier.copy(bean, new HashMap<>());
|
||||
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
for (LogicPrimaryKeyField info : infos) {
|
||||
String field = info.getField();
|
||||
Object value = mapBean.get(field);
|
||||
//为空,可能是有字段嵌套,也有可能是真的null
|
||||
if (value == null) {
|
||||
String tmpField = field;
|
||||
Object tmpValue = null;
|
||||
Map<String, Object> tempMapBean = mapBean;
|
||||
//嵌套的场景
|
||||
while (tmpValue == null && tmpField.contains(".")) {
|
||||
String[] nest = tmpField.split("[.]", 2);
|
||||
Object nestObject = tempMapBean.get(nest[0]);
|
||||
if (nestObject == null) {
|
||||
break;
|
||||
}
|
||||
if (nestObject instanceof Map) {
|
||||
tempMapBean = ((Map) nestObject);
|
||||
} else {
|
||||
tempMapBean = FastBeanCopier.copy(nestObject, new HashMap<>());
|
||||
}
|
||||
tmpField = nest[1];
|
||||
tmpValue = tempMapBean.get(tmpField);
|
||||
}
|
||||
value = tmpValue;
|
||||
}
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
if (info.matchNullOrEmpty) {
|
||||
if (value == null) {
|
||||
query.isNull(info.getField());
|
||||
} else {
|
||||
query.isEmpty(info.getField());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String termType = StringUtils.isEmpty(info.termType) ? TermType.eq : info.termType;
|
||||
query.and(info.getField(), termType, value);
|
||||
}
|
||||
properties.put(info.getField(), value);
|
||||
}
|
||||
|
||||
T result = query.single();
|
||||
|
||||
if (result != null) {
|
||||
Result validateResult = new Result();
|
||||
validateResult.setError(true);
|
||||
validateResult.setData(result);
|
||||
validateResult.setProperties(properties);
|
||||
return validateResult;
|
||||
}
|
||||
return Result.passed();
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
private static class LogicPrimaryKeyField {
|
||||
private String field;
|
||||
|
||||
private String condition;
|
||||
|
||||
private boolean matchNullOrEmpty;
|
||||
|
||||
private String termType;
|
||||
|
||||
private Class group;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return field.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof LogicPrimaryKeyField) {
|
||||
return hashCode() == obj.hashCode();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.ezorm.core.dsl.Query;
|
||||
import org.hswebframework.web.commons.entity.Entity;
|
||||
import org.hswebframework.web.commons.entity.PagerResult;
|
||||
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
|
||||
import org.hswebframework.web.dao.dynamic.QueryByEntityDao;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface DefaultQueryByEntityService<E>
|
||||
extends QueryByEntityService<E> {
|
||||
|
||||
QueryByEntityDao<E> getDao();
|
||||
|
||||
/**
|
||||
* 分页进行查询数据,查询条件同 {@link DefaultQueryByEntityService#select}
|
||||
*
|
||||
* @param param 查询参数
|
||||
* @return 分页查询结果
|
||||
* @see QueryParamEntity
|
||||
* @see QueryParamEntity#newQuery()
|
||||
*/
|
||||
@Override
|
||||
default PagerResult<E> selectPager(Entity param) {
|
||||
PagerResult<E> pagerResult = new PagerResult<>();
|
||||
|
||||
if (param instanceof QueryParamEntity) {
|
||||
QueryParamEntity entity = ((QueryParamEntity) param);
|
||||
//不分页,不进行count
|
||||
if (!entity.isPaging()) {
|
||||
pagerResult.setData(getDao().query(param));
|
||||
pagerResult.setTotal(pagerResult.getData().size());
|
||||
pagerResult.setPageIndex(entity.getThinkPageIndex());
|
||||
pagerResult.setPageSize(pagerResult.getData().size());
|
||||
return pagerResult;
|
||||
}
|
||||
}
|
||||
int total = getDao().count(param);
|
||||
pagerResult.setTotal(total);
|
||||
|
||||
//根据实际记录数量重新指定分页参数
|
||||
if (param instanceof QueryParamEntity) {
|
||||
QueryParamEntity paramEntity = (QueryParamEntity) param;
|
||||
paramEntity.rePaging(total);
|
||||
pagerResult.setPageSize(paramEntity.getPageSize());
|
||||
pagerResult.setPageIndex(paramEntity.getThinkPageIndex());
|
||||
}
|
||||
|
||||
if (total == 0) {
|
||||
pagerResult.setData(new java.util.ArrayList<>());
|
||||
} else {
|
||||
pagerResult.setData(select(param));
|
||||
}
|
||||
return pagerResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据查询参数进行查询,参数可使用 {@link Query}进行构建
|
||||
*
|
||||
* @param param 查询参数
|
||||
* @return 查询结果
|
||||
* @see QueryParamEntity
|
||||
* @see QueryParamEntity#newQuery()
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
default List<E> select(Entity param) {
|
||||
if (param == null) {
|
||||
param = QueryParamEntity.empty();
|
||||
}
|
||||
return getDao().query(param);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询记录总数,用于分页等操作。查询条件同 {@link DefaultQueryByEntityService#select}
|
||||
*
|
||||
* @param param 查询参数
|
||||
* @return 查询结果,实现mapper中的sql应指定默认值,否则可能抛出异常
|
||||
* @see QueryParamEntity
|
||||
* @see QueryParamEntity#newQuery()
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
default int count(Entity param) {
|
||||
if (param == null) {
|
||||
param = QueryParamEntity.empty();
|
||||
}
|
||||
return getDao().count(param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询只返回单个结果,如果有多个结果,只返回第一个
|
||||
*
|
||||
* @param param 查询条件
|
||||
* @return 单个查询结果
|
||||
* @see QueryParamEntity
|
||||
* @see QueryParamEntity#newQuery()
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
default E selectSingle(Entity param) {
|
||||
if (param instanceof QueryParamEntity) {
|
||||
((QueryParamEntity) param).doPaging(0, 1);
|
||||
}
|
||||
List<E> list = this.select(param);
|
||||
if (list.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return list.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.web.commons.entity.GenericEntity;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @see org.springframework.cache.annotation.CacheConfig
|
||||
* @see Cacheable
|
||||
* @see CacheEvict
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class EnableCacheAllEvictGenericEntityService<E extends GenericEntity<PK>, PK> extends GenericEntityService<E, PK> {
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'id:'+#pk")
|
||||
public E selectByPk(PK pk) {
|
||||
return super.selectByPk(pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public int updateByPk(List<E> data) {
|
||||
return super.updateByPk(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public int updateByPk(PK pk, E entity) {
|
||||
return super.updateByPk(pk, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public PK insert(E entity) {
|
||||
return super.insert(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public E deleteByPk(PK pk) {
|
||||
return super.deleteByPk(pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public PK saveOrUpdate(E entity) {
|
||||
return super.saveOrUpdate(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'all'")
|
||||
public List<E> select() {
|
||||
return super.select();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'id-in:'+#id.hashCode()")
|
||||
public List<E> selectByPk(List<PK> id) {
|
||||
return super.selectByPk(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'count'")
|
||||
public int count() {
|
||||
return super.count();
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.web.commons.entity.TreeSortSupportEntity;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class EnableCacheAllEvictTreeSortService<E extends TreeSortSupportEntity<PK>, PK>
|
||||
extends AbstractTreeSortService<E, PK> {
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'parent:'+#parentId")
|
||||
public List<E> selectParentNode(PK parentId) {
|
||||
return super.selectParentNode(parentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'chidlren:'+#parentId")
|
||||
public List<E> selectChildNode(PK parentId) {
|
||||
return super.selectChildNode(parentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'all-chidlren:'+#parentId")
|
||||
public List<E> selectAllChildNode(PK parentId) {
|
||||
return super.selectAllChildNode(parentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public int updateBatch(Collection<E> data) {
|
||||
return super.updateBatch(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'id:'+#pk")
|
||||
public E selectByPk(PK pk) {
|
||||
return super.selectByPk(pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public int updateByPk(List<E> data) {
|
||||
return super.updateByPk(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public int updateByPk(PK pk, E entity) {
|
||||
return super.updateByPk(pk, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public PK insert(E entity) {
|
||||
return super.insert(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public E deleteByPk(PK pk) {
|
||||
return super.deleteByPk(pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public PK saveOrUpdate(E entity) {
|
||||
return super.saveOrUpdate(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'id-in:'+#id.hashCode()")
|
||||
public List<E> selectByPk(List<PK> id) {
|
||||
return super.selectByPk(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'all'")
|
||||
public List<E> select() {
|
||||
return super.select();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'count'")
|
||||
public int count() {
|
||||
return super.count();
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.web.commons.entity.GenericEntity;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 启用缓冲的通用实体曾删改查服务,继承此类
|
||||
* 在类上注解{@link org.springframework.cache.annotation.CacheConfig}即可
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see org.springframework.cache.annotation.CacheConfig
|
||||
* @see Cacheable
|
||||
* @see CacheEvict
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class EnableCacheGenericEntityService<E extends GenericEntity<PK>, PK> extends GenericEntityService<E, PK> {
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'id:'+#pk")
|
||||
public E selectByPk(PK pk) {
|
||||
return super.selectByPk(pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public int updateByPk(List<E> data) {
|
||||
return super.updateByPk(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(
|
||||
evict = {
|
||||
@CacheEvict(key = "'id:'+#pk"),
|
||||
@CacheEvict(key = "'all'"),
|
||||
@CacheEvict(key = "'count'")
|
||||
}
|
||||
)
|
||||
public int updateByPk(PK pk, E entity) {
|
||||
return super.updateByPk(pk, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(
|
||||
evict = {
|
||||
@CacheEvict(key = "'id:'+#result"),
|
||||
@CacheEvict(key = "'all'"),
|
||||
@CacheEvict(key = "'count'")
|
||||
}
|
||||
)
|
||||
public PK insert(E entity) {
|
||||
return super.insert(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(
|
||||
evict = {
|
||||
@CacheEvict(key = "'id:'+#pk"),
|
||||
@CacheEvict(key = "'all'"),
|
||||
@CacheEvict(key = "'count'")
|
||||
}
|
||||
)
|
||||
public E deleteByPk(PK pk) {
|
||||
return super.deleteByPk(pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(
|
||||
evict = {
|
||||
@CacheEvict(key = "'id:'+#result"),
|
||||
@CacheEvict(key = "'all'"),
|
||||
@CacheEvict(key = "'count'")
|
||||
}
|
||||
)
|
||||
public PK saveOrUpdate(E entity) {
|
||||
return super.saveOrUpdate(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'all'")
|
||||
public List<E> select() {
|
||||
return super.select();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'count'")
|
||||
public int count() {
|
||||
return super.count();
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.web.commons.entity.TreeSortSupportEntity;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class EnableCacheTreeSortService<E extends TreeSortSupportEntity<PK>, PK>
|
||||
extends AbstractTreeSortService<E, PK> {
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public int updateBatch(Collection<E> data) {
|
||||
return super.updateBatch(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'id:'+#pk")
|
||||
public E selectByPk(PK pk) {
|
||||
return super.selectByPk(pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(allEntries = true)
|
||||
public int updateByPk(List<E> data) {
|
||||
return super.updateByPk(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(
|
||||
evict = {
|
||||
@CacheEvict(key = "'id:'+#pk"),
|
||||
@CacheEvict(key = "'all'"),
|
||||
@CacheEvict(key = "'count'")
|
||||
}
|
||||
)
|
||||
public int updateByPk(PK pk, E entity) {
|
||||
return super.updateByPk(pk, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(
|
||||
evict = {
|
||||
@CacheEvict(key = "'id:'+#result"),
|
||||
@CacheEvict(key = "'all'"),
|
||||
@CacheEvict(key = "'count'")
|
||||
}
|
||||
)
|
||||
public PK insert(E entity) {
|
||||
return super.insert(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(
|
||||
evict = {
|
||||
@CacheEvict(key = "'id:'+#pk"),
|
||||
@CacheEvict(key = "'all'"),
|
||||
@CacheEvict(key = "'count'")
|
||||
}
|
||||
)
|
||||
public E deleteByPk(PK pk) {
|
||||
return super.deleteByPk(pk);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Caching(
|
||||
evict = {
|
||||
@CacheEvict(key = "'id:'+#result"),
|
||||
@CacheEvict(key = "'all'"),
|
||||
@CacheEvict(key = "'count'")
|
||||
}
|
||||
)
|
||||
public PK saveOrUpdate(E entity) {
|
||||
return super.saveOrUpdate(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'all'")
|
||||
public List<E> select() {
|
||||
return super.select();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(key = "'count'")
|
||||
public int count() {
|
||||
return super.count();
|
||||
}
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.web.commons.entity.GenericEntity;
|
||||
import org.hswebframework.web.commons.entity.LogicalDeleteEntity;
|
||||
import org.hswebframework.web.commons.entity.RecordCreationEntity;
|
||||
import org.hswebframework.web.commons.entity.RecordModifierEntity;
|
||||
import org.hswebframework.web.commons.entity.events.EntityCreatedEvent;
|
||||
import org.hswebframework.web.commons.entity.events.EntityModifyEvent;
|
||||
import org.hswebframework.web.id.IDGenerator;
|
||||
import org.hswebframework.web.validator.group.CreateGroup;
|
||||
import org.hswebframework.web.validator.group.UpdateGroup;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通用实体服务类,提供增删改查的默认实现
|
||||
*
|
||||
* @author zhouhao
|
||||
*/
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
public abstract class GenericEntityService<E extends GenericEntity<PK>, PK>
|
||||
extends AbstractService<E, PK>
|
||||
implements GenericService<E, PK> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public GenericEntityService() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取ID生成器,在insert的时候,如果ID为空,则调用生成器进行生成
|
||||
*
|
||||
* @return IDGenerator
|
||||
* @see IDGenerator
|
||||
*/
|
||||
protected abstract IDGenerator<PK> getIDGenerator();
|
||||
|
||||
protected ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setEventPublisher(ApplicationEventPublisher eventPublisher) {
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (logicPrimaryKeyValidator instanceof DefaultLogicPrimaryKeyValidator) {
|
||||
DefaultLogicPrimaryKeyValidator.registerQuerySuppiler(getEntityInstanceType(), bean -> this.createQuery().not("id", bean.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E deleteByPk(PK pk) {
|
||||
E old = selectByPk(pk);
|
||||
if (old == null) {
|
||||
return null;
|
||||
}
|
||||
if (old instanceof LogicalDeleteEntity) {
|
||||
LogicalDeleteEntity deleteEntity = (LogicalDeleteEntity) old;
|
||||
deleteEntity.setDeleted(true);
|
||||
deleteEntity.setDeleteTime(System.currentTimeMillis());
|
||||
createUpdate()
|
||||
.set(deleteEntity::getDeleted)
|
||||
.set(deleteEntity::getDeleteTime)
|
||||
.where(GenericEntity.id, pk)
|
||||
.exec();
|
||||
} else {
|
||||
if (!physicalDeleteByPk(pk)) {
|
||||
logger.warn("物理删除数据失败,主键:{}", pk);
|
||||
}
|
||||
}
|
||||
return old;
|
||||
}
|
||||
|
||||
protected boolean physicalDeleteByPk(PK pk) {
|
||||
//createDelete().where(GenericEntity.id,pk).exec()>0;
|
||||
|
||||
return getDao().deleteByPk(pk) > 0;
|
||||
}
|
||||
|
||||
protected boolean pushModifyEvent() {
|
||||
return RecordModifierEntity.class.isAssignableFrom(entityType);
|
||||
}
|
||||
|
||||
protected boolean pushCreatedEvent() {
|
||||
return RecordCreationEntity.class.isAssignableFrom(entityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateByPk(PK pk, E entity) {
|
||||
Assert.notNull(pk, "primary key can not be null");
|
||||
Assert.hasText(String.valueOf(pk), "primary key can not be null");
|
||||
Assert.notNull(entity, "entity can not be null");
|
||||
entity.setId(pk);
|
||||
|
||||
tryValidate(entity, UpdateGroup.class);
|
||||
//尝试推送 EntityModifyEvent 事件.
|
||||
if (eventPublisher != null && pushModifyEvent()) {
|
||||
E old = selectByPk(pk);
|
||||
eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, new EntityModifyEvent<>(old, entity, getEntityType()), getEntityType()));
|
||||
}
|
||||
return createUpdate(entity)
|
||||
//如果是RecordCreationEntity则不修改creator_id和creator_time
|
||||
.when(entity instanceof RecordCreationEntity,
|
||||
update -> update.and().excludes(((RecordCreationEntity) entity).getCreatorIdProperty(), RecordCreationEntity.createTime))
|
||||
.where(GenericEntity.id, pk)
|
||||
.exec();
|
||||
}
|
||||
|
||||
protected int updateByPk(E entity) {
|
||||
return updateByPk(entity.getId(), entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateByPk(List<E> data) {
|
||||
return data.stream()
|
||||
.map(this::updateByPk)
|
||||
.reduce(Math::addExact)
|
||||
.orElse(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PK saveOrUpdate(E entity) {
|
||||
if (dataExisted(entity)) {
|
||||
updateByPk(entity);
|
||||
} else {
|
||||
insert(entity);
|
||||
}
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean dataExisted(E entity) {
|
||||
if (null != logicPrimaryKeyValidator) {
|
||||
logicPrimaryKeyValidator
|
||||
.validate(entity)
|
||||
.ifError(result -> entity.setId(result.getData().getId()));
|
||||
}
|
||||
return null != entity.getId() && null != selectByPk(entity.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PK insert(E entity) {
|
||||
if (!StringUtils.isEmpty(entity.getId())) {
|
||||
if ((entity.getId() instanceof String) && !StringUtils.isEmpty(entity.getId())) {
|
||||
tryValidateProperty(entity.getId().toString().matches("[a-zA-Z0-9_\\-]+"), "id", "只能由数字,字母,下划线,和-组成");
|
||||
}
|
||||
tryValidateProperty(selectByPk(entity.getId()) == null, "id", entity.getId() + "已存在");
|
||||
}
|
||||
if (StringUtils.isEmpty(entity.getId()) && getIDGenerator() != null) {
|
||||
entity.setId(getIDGenerator().generate());
|
||||
}
|
||||
if (entity instanceof RecordCreationEntity) {
|
||||
((RecordCreationEntity) entity).setCreateTimeNow();
|
||||
}
|
||||
tryValidate(entity, CreateGroup.class);
|
||||
getDao().insert(entity);
|
||||
|
||||
if (eventPublisher != null && pushCreatedEvent()) {
|
||||
eventPublisher.publishEvent(new GenericsPayloadApplicationEvent<>(this, new EntityCreatedEvent<>(entity, getEntityType()), getEntityType()));
|
||||
}
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public E selectByPk(PK pk) {
|
||||
if (StringUtils.isEmpty(pk)) {
|
||||
return null;
|
||||
}
|
||||
return createQuery().where(GenericEntity.id, pk).single();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<E> selectByPk(List<PK> id) {
|
||||
if (CollectionUtils.isEmpty(id)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return createQuery().where().in(GenericEntity.id, id).listNoPaging();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* *
|
||||
* * http://www.apache.org/licenses/LICENSE-2.0
|
||||
* *
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.web.dao.CrudDao;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see DefaultDSLQueryService
|
||||
* @see DefaultDSLUpdateService
|
||||
* @see DefaultDSLDeleteService
|
||||
* @see CrudService
|
||||
* @see CrudDao
|
||||
*/
|
||||
public interface GenericService<E, PK> extends
|
||||
DefaultDSLQueryService<E, PK>,
|
||||
DefaultDSLUpdateService<E,PK>,
|
||||
DefaultDSLDeleteService<E,PK>,
|
||||
CrudService<E, PK> {
|
||||
@Override
|
||||
CrudDao<E, PK> getDao();
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.springframework.context.PayloadApplicationEvent;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
||||
/**
|
||||
* 动态泛型事件,用于动态发布支持泛型的事件
|
||||
* <pre>
|
||||
* //相当于发布事件: EntityModifyEvent<UserEntity>
|
||||
* eventPublisher
|
||||
* .publishEvent(new GenericsPayloadApplicationEvent<>(this, new EntityModifyEvent<>(oldEntity, newEntity), UserEntity.class));
|
||||
*
|
||||
* //只监听相同泛型事件
|
||||
* @EventListener
|
||||
* public handleEvent(EntityModifyEvent<UserEntity> event){
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author zhouhao
|
||||
* @since 3.0.7
|
||||
*/
|
||||
public class GenericsPayloadApplicationEvent<E> extends PayloadApplicationEvent<E> {
|
||||
|
||||
private static final long serialVersionUID = 3745888943307798710L;
|
||||
|
||||
//泛型列表
|
||||
private transient Class[] generics;
|
||||
|
||||
//事件类型
|
||||
private transient Class eventType;
|
||||
|
||||
/**
|
||||
* @param source 事件源
|
||||
* @param payload 事件,不能使用匿名内部类
|
||||
* @param generics 泛型列表
|
||||
*/
|
||||
public GenericsPayloadApplicationEvent(Object source, E payload, Class... generics) {
|
||||
super(source, payload);
|
||||
this.generics = generics;
|
||||
this.eventType = payload.getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResolvableType getResolvableType() {
|
||||
return ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class
|
||||
, ResolvableType.forClassWithGenerics(eventType, generics));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"groupId": "${project.groupId}",
|
||||
"artifactId": "${project.artifactId}",
|
||||
"path": "hsweb-commons/hsweb-commons-service/hsweb-commons-service-simple",
|
||||
"gitCommitHash": "${git.commit.hash}",
|
||||
"gitRepository": "http://github.com/hs-web/hsweb-framework",
|
||||
"version": "${project.version}"
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.hswebframework.web.commons.entity.factory.MapperEntityFactory;
|
||||
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
|
||||
import org.hswebframework.web.dao.CrudDao;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AbstractTreeSortServiceTests {
|
||||
|
||||
@InjectMocks
|
||||
private TestTreeEntityService entityService = new TestTreeEntityService();
|
||||
|
||||
@Mock
|
||||
private CrudDao<TestEntity, String> dao;
|
||||
|
||||
private List<TestEntity> entities = new ArrayList<>();
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
entityService.setEntityFactory(new MapperEntityFactory());
|
||||
|
||||
TestEntity entity = TestEntity.builder()
|
||||
.age((byte) 10)
|
||||
.enabled(true)
|
||||
.name("test")
|
||||
.build();
|
||||
entity.setId("testId");
|
||||
|
||||
when(dao.query(any()))
|
||||
.thenReturn(new ArrayList<>());
|
||||
|
||||
when(dao.count(any())).thenReturn(1);
|
||||
|
||||
// when(dao.update(any())).thenReturn(1);
|
||||
|
||||
when(dao.delete(any())).thenReturn(1);
|
||||
when(dao.deleteByPk("test")).thenReturn(1);
|
||||
|
||||
// doNothing().when(dao).insert(anyObject());
|
||||
doAnswer(invocationOnMock -> {
|
||||
TestEntity t = invocationOnMock.getArgumentAt(0, TestEntity.class);
|
||||
entities.add(t);
|
||||
return t.getId();
|
||||
}).when(dao).insert(anyObject());
|
||||
|
||||
doAnswer(invocationOnMock -> {
|
||||
TestEntity t = invocationOnMock.getArgumentAt(0, TestEntity.class);
|
||||
entities.add(t);
|
||||
return t.getId();
|
||||
}).when(dao).update(anyObject());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBatchInsert() {
|
||||
String treeJson = "{'id':'1','parentId':'-1','name':'父节点','children':[" +
|
||||
"{'id':'101','parentId':'1','name':'子节点1'}," +
|
||||
"{'id':'102','parentId':'1','name':'子节点2'}" +
|
||||
"]}";
|
||||
TestEntity entity = JSON.parseObject(treeJson, TestEntity.class);
|
||||
|
||||
entityService.insert(entity);
|
||||
|
||||
Assert.assertEquals(entities.size(), 3);
|
||||
entities.clear();
|
||||
|
||||
entityService.updateByPk(entity);
|
||||
Assert.assertEquals(entities.size(), 3);
|
||||
entities.clear();
|
||||
|
||||
entityService.updateBatch(Arrays.asList(entity));
|
||||
Assert.assertEquals(entities.size(), 3);
|
||||
}
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hswebframework.ezorm.core.dsl.Query;
|
||||
import org.hswebframework.web.commons.entity.param.QueryParamEntity;
|
||||
import org.hswebframework.web.validator.LogicPrimaryKey;
|
||||
import org.hswebframework.web.validator.group.CreateGroup;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.0-RC
|
||||
*/
|
||||
public class DefaultLogicPrimaryKeyValidatorTest {
|
||||
DefaultLogicPrimaryKeyValidator validator = new DefaultLogicPrimaryKeyValidator();
|
||||
|
||||
@Test
|
||||
public void testSimple() {
|
||||
DefaultLogicPrimaryKeyValidator.registerQuerySuppiler(TestBean.class, bean ->
|
||||
Query.<TestBean, QueryParamEntity>empty(QueryParamEntity.empty())
|
||||
.setSingleExecutor(param -> {
|
||||
Assert.assertNotNull(param.getTerms());
|
||||
Assert.assertEquals(param.getTerms().size(), 2);
|
||||
return new TestBean("test", "1");
|
||||
}));
|
||||
|
||||
TestBean bean = new TestBean("test", "1");
|
||||
Assert.assertTrue(validator.validate(bean).isError());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testClassAnn() {
|
||||
DefaultLogicPrimaryKeyValidator.registerQuerySuppiler(ClassAnnTestBean.class, bean ->
|
||||
Query.<ClassAnnTestBean, QueryParamEntity>empty(QueryParamEntity.empty())
|
||||
.setSingleExecutor(param -> {
|
||||
Assert.assertNotNull(param.getTerms());
|
||||
Assert.assertEquals(param.getTerms().size(), 2);
|
||||
return new ClassAnnTestBean("test", "1");
|
||||
}));
|
||||
|
||||
ClassAnnTestBean bean = new ClassAnnTestBean("test", "1");
|
||||
Assert.assertTrue(validator.validate(bean).isError());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGroupAnn() {
|
||||
DefaultLogicPrimaryKeyValidator.registerQuerySuppiler(GroupAnnTestBean.class, bean ->
|
||||
Query.<GroupAnnTestBean, QueryParamEntity>empty(QueryParamEntity.empty())
|
||||
.setSingleExecutor(param -> {
|
||||
Assert.assertNotNull(param.getTerms());
|
||||
Assert.assertEquals(param.getTerms().size(), 2);
|
||||
return new GroupAnnTestBean("test", "1");
|
||||
}));
|
||||
|
||||
GroupAnnTestBean bean = new GroupAnnTestBean("test", "1");
|
||||
Assert.assertTrue(validator.validate(bean).isPassed());
|
||||
|
||||
Assert.assertTrue(validator.validate(bean, TestGroup.class).isError());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNestProperty() {
|
||||
NestTestBean nestTestBean=new NestTestBean(new TestBean("test","1"),"test");
|
||||
|
||||
DefaultLogicPrimaryKeyValidator.registerQuerySuppiler(NestTestBean.class, bean ->
|
||||
Query.<NestTestBean, QueryParamEntity>empty(QueryParamEntity.empty())
|
||||
.setSingleExecutor(param -> {
|
||||
Assert.assertNotNull(param.getTerms());
|
||||
Assert.assertEquals(param.getTerms().size(), 2);
|
||||
return nestTestBean;
|
||||
}));
|
||||
|
||||
Assert.assertTrue(validator.validate(nestTestBean).isError());
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@LogicPrimaryKey(groups = TestGroup.class)
|
||||
public class GroupAnnTestBean {
|
||||
|
||||
private String name;
|
||||
|
||||
private String org;
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@LogicPrimaryKey({"name", "org"})
|
||||
public class ClassAnnTestBean {
|
||||
|
||||
private String name;
|
||||
|
||||
private String org;
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
public class GroupTestBean {
|
||||
|
||||
@LogicPrimaryKey(groups = CreateGroup.class)
|
||||
private String name;
|
||||
|
||||
@LogicPrimaryKey
|
||||
private String org;
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
public class NestTestBean {
|
||||
@LogicPrimaryKey("nest.name")
|
||||
private TestBean nest;
|
||||
|
||||
@LogicPrimaryKey
|
||||
private String org;
|
||||
}
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
public class TestBean {
|
||||
@LogicPrimaryKey
|
||||
private String name;
|
||||
|
||||
@LogicPrimaryKey
|
||||
private String org;
|
||||
}
|
||||
|
||||
@LogicPrimaryKey(value = {"name", "org"}, groups = TestGroup.class)
|
||||
public interface TestGroup {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.web.dao.CrudDao;
|
||||
import org.hswebframework.web.id.IDGenerator;
|
||||
import org.springframework.cache.annotation.CacheConfig;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 1.0
|
||||
*/
|
||||
@CacheConfig(cacheNames = "test-2-entity")
|
||||
public class EnableCacheAllEvictTestService extends EnableCacheAllEvictGenericEntityService<TestEntity, String> {
|
||||
private CrudDao<TestEntity, String> dao;
|
||||
|
||||
@Override
|
||||
protected IDGenerator<String> getIDGenerator() {
|
||||
return IDGenerator.MD5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrudDao<TestEntity, String> getDao() {
|
||||
return dao;
|
||||
}
|
||||
|
||||
public void setDao(CrudDao<TestEntity, String> dao) {
|
||||
this.dao = dao;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.web.dao.CrudDao;
|
||||
import org.hswebframework.web.id.IDGenerator;
|
||||
import org.springframework.cache.annotation.CacheConfig;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 1.0
|
||||
*/
|
||||
@CacheConfig(cacheNames = "test-2-tree-entity")
|
||||
public class EnableCacheAllEvictTreeTestService extends EnableCacheAllEvictTreeSortService<TestEntity, String> {
|
||||
private CrudDao<TestEntity, String> dao;
|
||||
|
||||
@Override
|
||||
protected IDGenerator<String> getIDGenerator() {
|
||||
return IDGenerator.MD5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrudDao<TestEntity, String> getDao() {
|
||||
return dao;
|
||||
}
|
||||
|
||||
public void setDao(CrudDao<TestEntity, String> dao) {
|
||||
this.dao = dao;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.web.dao.CrudDao;
|
||||
import org.hswebframework.web.id.IDGenerator;
|
||||
import org.springframework.cache.annotation.CacheConfig;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 1.0
|
||||
*/
|
||||
@CacheConfig(cacheNames = "test-entity")
|
||||
public class EnableCacheTestService extends EnableCacheGenericEntityService<TestEntity, String> {
|
||||
private CrudDao<TestEntity, String> dao;
|
||||
|
||||
@Override
|
||||
protected IDGenerator<String> getIDGenerator() {
|
||||
return IDGenerator.MD5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrudDao<TestEntity, String> getDao() {
|
||||
return dao;
|
||||
}
|
||||
|
||||
public void setDao(CrudDao<TestEntity, String> dao) {
|
||||
this.dao = dao;
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
package org.hswebframework.web.service;
|
||||
|
||||
import org.hswebframework.web.commons.entity.factory.MapperEntityFactory;
|
||||
import org.hswebframework.web.dao.CrudDao;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.theories.suppliers.TestedOn;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
@SpringBootTest(classes = SpringTestApplication.class)
|
||||
@RunWith(value = SpringRunner.class)
|
||||
public class EnableCacheTests {
|
||||
|
||||
@Autowired
|
||||
private EnableCacheTestService enableCacheTestService;
|
||||
|
||||
@Autowired
|
||||
private EnableCacheAllEvictTestService enableCacheAllEvictTestService;
|
||||
@Autowired
|
||||
private EnableCacheTreeTestService enableCacheTreeTestService;
|
||||
@Autowired
|
||||
private EnableCacheAllEvictTreeTestService enableCacheAllEvictTreeTestService;
|
||||
|
||||
private AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
CrudDao<TestEntity, String> dao = Mockito.mock(CrudDao.class);
|
||||
enableCacheTestService.setEntityFactory(new MapperEntityFactory());
|
||||
|
||||
TestEntity entity = TestEntity.builder()
|
||||
.age((byte) 10)
|
||||
.enabled(true)
|
||||
.name("test")
|
||||
.build();
|
||||
entity.setId("testId");
|
||||
|
||||
when(dao.query(any()))
|
||||
.then((Answer<List<TestEntity>>) invocationOnMock -> {
|
||||
//模拟命中数据库
|
||||
counter.incrementAndGet();
|
||||
return new ArrayList<>(Arrays.asList(entity));
|
||||
});
|
||||
|
||||
when(dao.count(any())).then((Answer<Integer>) invocationOnMock -> {
|
||||
//模拟命中数据库
|
||||
counter.incrementAndGet();
|
||||
return 1;
|
||||
});
|
||||
|
||||
when(dao.update(any())).thenReturn(1);
|
||||
|
||||
when(dao.delete(any())).thenReturn(1);
|
||||
when(dao.deleteByPk(anyString())).thenReturn(1);
|
||||
|
||||
doNothing().when(dao).insert(anyObject());
|
||||
|
||||
enableCacheTestService.setDao(dao);
|
||||
|
||||
enableCacheAllEvictTestService.setDao(dao);
|
||||
|
||||
enableCacheTreeTestService.setDao(dao);
|
||||
|
||||
enableCacheAllEvictTreeTestService.setDao(dao);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleCacheEnableService() {
|
||||
doTest(enableCacheTestService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnableCacheAllEvictTestService() {
|
||||
doTest(enableCacheAllEvictTestService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnableCacheTreeTestService() {
|
||||
doTest(enableCacheTreeTestService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnableCacheAllEvictTreeTestService() {
|
||||
doTest(enableCacheAllEvictTreeTestService);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void doTest(CrudService<TestEntity, String> service) {
|
||||
service.selectByPk("testId"); //db 1
|
||||
service.selectByPk("testId");//cache
|
||||
Assert.assertEquals(counter.get(), 1);
|
||||
|
||||
service.select(); //db 2
|
||||
service.select();//cache
|
||||
Assert.assertEquals(counter.get(), 2);
|
||||
|
||||
service.count(); //db 3
|
||||
service.count(); //cache
|
||||
Assert.assertEquals(counter.get(), 3);
|
||||
|
||||
service.updateByPk("testId", new TestEntity()); //evict cache
|
||||
|
||||
service.select(); //db 4
|
||||
service.selectByPk("testId");//db 5
|
||||
service.count();//db 6
|
||||
|
||||
service.select(); //cache
|
||||
service.selectByPk("testId");//cache
|
||||
service.count();//cache
|
||||
Assert.assertEquals(counter.get(), 6);
|
||||
|
||||
service.deleteByPk("testId"); //evict cache
|
||||
//删除前会查询
|
||||
counter.decrementAndGet();
|
||||
service.select(); //db 7
|
||||
service.selectByPk("testId");//db 8
|
||||
service.count();//db 9
|
||||
|
||||
service.select(); //cache
|
||||
service.selectByPk("testId");//cache
|
||||
service.count();//cache
|
||||
Assert.assertEquals(counter.get(), 9);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user