Merge pull request #44 from dibo-software/2.0.3

2.0.3
This commit is contained in:
Mazc 2019-12-12 09:58:54 +08:00 committed by GitHub
commit e48923f301
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
207 changed files with 3149 additions and 9115 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@
*.iml
application.properties
application-*.properties
.DS_Store
# Ignore Gradle build output directory
build/

View File

@ -27,7 +27,7 @@ diboot 2.0版本,实现: diboot-core全新内核 + diboot-devtools代码生成
## 二、 diboot-devtools 代码生成工具 [(我要试试)](https://www.diboot.com/guide/diboot-devtools/%E4%BB%8B%E7%BB%8D.html)
## 二、 diboot-devtools 自动化开发助理 [(我要试试)](https://www.diboot.com/guide/diboot-devtools/%E4%BB%8B%E7%BB%8D.html)
##### 1. 支持多数据库MySQL、MariaDB、ORACLE、SQLServer、PostgreSQL
##### 2. 使用很简单引入依赖jar配置参数后即可随SpringBoot启动运行
@ -37,7 +37,7 @@ diboot 2.0版本,实现: diboot-core全新内核 + diboot-devtools代码生成
## 三、技术交流群
如果Diboot对您有用欢迎您为Diboot的发展提供捐助。我们不喝咖啡所有捐助费用都将用于Diboot服务器支出。
如果Diboot对您有用欢迎您为Diboot的发展提供捐助。
<p align="center">
<img src="https://www.diboot.com/donate.jpg" width = "500" alt="支持Diboot发展">
</p>
@ -45,4 +45,4 @@ diboot 2.0版本,实现: diboot-core全新内核 + diboot-devtools代码生成
> QQ群: [731690096]()
> 微信群加: [wx20201024]()
> 微信群备注diboot加: [wx20201024]()

View File

@ -1,10 +1,9 @@
buildscript {
ext {
springBootVersion = '2.1.8.RELEASE'
springBootVersion = '2.2.1.RELEASE'
}
repositories {
mavenLocal() //maven库
maven{ url 'http://maven.diboot.com/repository/diboot'}
maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
}
dependencies {
@ -31,17 +30,15 @@ subprojects {
[compileJava,compileTestJava,javadoc]*.options*.encoding = 'UTF-8'
repositories {
mavenLocal() //maven库
maven{ url 'http://maven.diboot.com/repository/diboot'}
maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'}
}
ext {//
springBootVersion = "2.1.8.RELEASE"
springBootVersion = "2.2.1.RELEASE"
mysqlConnectorVersion = "8.0.16"
mybatisStarterVersion = "2.1.0"
mybatisPlusVersion = "3.2.0"
fastjsonVersion = "1.2.60"
lombokVersion = "1.18.8"
validatorVersion = "6.0.17.Final"
lombokVersion = "1.18.10"
validatorVersion = "6.0.18.Final"
}
dependencies {
// Gradle 5.0使
@ -51,8 +48,6 @@ subprojects {
compile("javax.servlet:javax.servlet-api:4.0.1")
compile("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
// Mybatis
compile("org.mybatis.spring.boot:mybatis-spring-boot-starter:$mybatisStarterVersion")
// Mybatis-Plus
compile("com.baomidou:mybatis-plus-boot-starter:$mybatisPlusVersion")
// Log4j2
@ -65,7 +60,7 @@ subprojects {
compile("org.hibernate.validator:hibernate-validator:$validatorVersion")
// Apache Commons
compile("org.apache.commons:commons-lang3:3.8.1",
"commons-fileupload:commons-fileupload:1.3.3",
// "commons-fileupload:commons-fileupload:1.3.3",
"commons-io:commons-io:2.6")
//

View File

@ -1,14 +0,0 @@
dependencies {
compile project(":diboot-core")
compile project(":diboot-component-file")
def poiversion = "3.17"
// office文件相关依赖
compile("org.apache.poi:poi:$poiversion",
"org.apache.poi:poi-ooxml:$poiversion")
//easyexcel
compile("com.alibaba:easyexcel:2.0.5")
testCompile group: 'junit', name: 'junit', version: '4.12'
}

View File

@ -1,273 +0,0 @@
package com.diboot.component.excel.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.diboot.component.excel.entity.BaseExcelDataEntity;
import com.diboot.component.excel.entity.ExcelColumn;
import com.diboot.component.excel.listener.BaseExcelDataListener;
import com.diboot.component.excel.service.ExcelColumnService;
import com.diboot.component.excel.service.ExcelImportRecordService;
import com.diboot.component.excel.utils.EasyExcelHelper;
import com.diboot.component.file.entity.BaseFile;
import com.diboot.component.file.file.FileHelper;
import com.diboot.component.file.service.BaseFileService;
import com.diboot.core.entity.BaseEntity;
import com.diboot.core.service.BaseService;
import com.diboot.core.util.S;
import com.diboot.core.util.V;
import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.Pagination;
import com.diboot.core.vo.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Excel导入基类
* @author Mazc
* @version 2017/9/18
* Copyright @ www.com.ltd
*/
public abstract class BaseExcelImportController <T extends BaseExcelDataEntity> {
private static final Logger logger = LoggerFactory.getLogger(BaseExcelImportController.class);
@Autowired
protected BaseFileService baseFileService;
@Autowired
protected ExcelColumnService excelColumnService;
@Autowired
protected ExcelImportRecordService excelImportRecordService;
protected static final String PARAM_IMPORT_UUID = "importUid";
/**
* 获取Excel列的定义
*/
private static Map<String, List<ExcelColumn>> excelColumnMap = null;
/***
* 获取Model类
* @return
*/
protected abstract Class<?> getModelClass();
/***
* 获取ExcelData模板类
* @return
*/
protected abstract Class<T> getExcelDataClass();
/***
* 获取业务的service
* @return
*/
protected abstract BaseService getBusinessService();
/***
* 获取对应的ExcelDataListener
* @return
*/
protected abstract BaseExcelDataListener getExcelDataListener();
protected BaseService getService() {
return baseFileService;
}
/***
* 列表页处理
* @param request
* @return
* @throws Exception
*/
public JsonResult listPaging(LambdaQueryWrapper<? extends BaseFile> queryWrapper, Pagination pagination, HttpServletRequest request) throws Exception{
queryWrapper.eq(BaseFile::getRelObjType, getModelClass().getSimpleName());
// 查询当前页的数据
List entityList = getService().getEntityList(queryWrapper, pagination);
// 返回结果
return new JsonResult(Status.OK, entityList).bindPagination(pagination);
}
/***
* 预览处理
* @param request
* @return
* @throws Exception
*/
public JsonResult preview(HttpServletRequest request) throws Exception {
Map dataMap = new HashMap();
try{
BaseEntity model = (BaseEntity) getModelClass().newInstance();
List<T> modelList = saveExcelFile(request, model, true);
//获取预览时的表头
List<Map> header = getPreviewDataHeader();
dataMap.put("header", header);
//最多返回前端十条数据
dataMap.put("modelList", modelList.size()>10?modelList.subList(0,10):modelList.subList(0,modelList.size()));
// 上传文件的id
dataMap.put(PARAM_IMPORT_UUID, request.getAttribute(PARAM_IMPORT_UUID));
return new JsonResult(Status.OK, dataMap);
}
catch(Exception e){
logger.warn("预览数据失败 -->" + e);
return new JsonResult(Status.FAIL_OPERATION, e.getMessage());
}
}
/***
* 预览保存
* @param request
* @param
* @return
* @throws Exception
*/
public JsonResult previewSave(HttpServletRequest request) throws Exception {
String importUid = request.getParameter(PARAM_IMPORT_UUID);
if(V.isEmpty(importUid)){
return new JsonResult(Status.FAIL_OPERATION, "预览保存失败,无法获取上传文件编号 importUid");
}
try{
LambdaQueryWrapper<BaseFile> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(BaseFile::getUuid, importUid);
BaseFile importFile = baseFileService.getModel(wrapper);
boolean success = false;
success = EasyExcelHelper.simpleReadAndSave(FileHelper.getFileStorageDirectory() + importFile.getPath(),getExcelDataClass(),getExcelDataListener());
if(success){
// 更新上传文件信息
importFile.setDeleted(false);
importFile.setDataCount(getExcelDataListener().getEntityList().size());
baseFileService.updateModel(importFile);
// 批量保存导入记录明细
success = excelImportRecordService.batchCreateRecords(importUid, getExcelDataListener().getEntityList());
if(!success){
logger.warn("数据导入成功但保存导入历史记录信息失败fileUuid="+importUid);
}
}else{
logger.error("数据上传失败:"+ S.join(getExcelDataListener().getErrorMsgs(), "<br/>"));
return new JsonResult(Status.FAIL_OPERATION, S.join(getExcelDataListener().getErrorMsgs(), "<br/>"));
}
}
catch(Exception e){
logger.error("上传数据错误: "+ e.getMessage(), e);
}
return new JsonResult(Status.OK,"上传成功");
}
/***
* 直接上传
* @param request
* @param
* @return
* @throws Exception
*/
public JsonResult upload(HttpServletRequest request) throws Exception {
try{
BaseEntity model = (BaseEntity) getModelClass().newInstance();
saveExcelFile(request, model, false);
String importUid = (String) request.getAttribute(PARAM_IMPORT_UUID);
// 批量保存导入记录明细
if(importUid != null){
boolean success = excelImportRecordService.batchCreateRecords(importUid, getExcelDataListener().getEntityList());
if(!success){
logger.warn("数据导入成功但保存导入历史记录信息失败fileUuid="+importUid);
}
}
else{
logger.warn("数据导入成功,但无法导入历史记录信息: importUuid不存在");
}
}
catch(Exception e){
logger.error("上传数据错误: "+ e.getMessage(), e);
return new JsonResult(Status.FAIL_OPERATION, e.getMessage());
}
return new JsonResult(Status.OK);
}
/**
* 保存上传文件
* @param request
* @param model
* @param fileInputName
* @return
* @throws Exception
*/
protected List<T> saveExcelFile(HttpServletRequest request, BaseEntity model, boolean isPreview, String... fileInputName) throws Exception{
MultipartFile file = FileHelper.getFileFromRequest(request, fileInputName);
if(V.isEmpty(file)) {
throw new Exception("未获取待处理的excel文件");
}
List<T> dataList = null;
String fileName = file.getOriginalFilename();
if (V.isEmpty(fileName) || !FileHelper.isExcel(fileName)) {
logger.info("非Excel类型: " + fileName);
throw new Exception("上传的文件为非Excel文件类型");
}
// 文件后缀
String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
// 先保存文件
String uuid = S.newUuid();
String newFileName = uuid + "." + ext;
String path = FileHelper.saveFile(file, newFileName);
if(V.isEmpty(path)){
logger.info("文件保存失败");
throw new Exception("文件保存失败");
}
BaseFile fileObj = new BaseFile();
// fileObj.setUuid(uuid);
fileObj.setName(fileName);
fileObj.setRelObjType(model.getClass().getSimpleName());
fileObj.setRelObjId(model.getId());
fileObj.setFileType(ext);
fileObj.setName(fileName);
fileObj.setPath(path);
fileObj.setLink("/file/download/" + uuid);
fileObj.setSize(file.getSize());
fileObj.setComment(request.getParameter("comment"));
try {
dataList = EasyExcelHelper.simpleRead(FileHelper.getFileStorageDirectory() + path, getExcelDataClass(), getExcelDataListener(), isPreview);
} catch (Exception e) {
throw new Exception("解析excel文件失败");
}
if(V.notEmpty(getExcelDataListener().getErrorMsgs())){
throw new Exception(S.join(getExcelDataListener().getErrorMsgs(), "<br/>"));
}
// 初始设置为0批量保存数据后更新
if(isPreview){
fileObj.setDeleted(true);
fileObj.setDataCount(0);
}else{
fileObj.setDeleted(false);
fileObj.setDataCount(getExcelDataListener().getEntityList().size());
}
baseFileService.createEntity(fileObj);
// 绑定属性到model
request.setAttribute(PARAM_IMPORT_UUID, uuid);
logger.info("成功保存附件: uid=" + uuid + ", name=" + fileName);
// 返回结果
return dataList;
}
/**
* 获取预览数据时显示在页面上的数据表头
* @return
* @throws Exception
*/
private List<Map> getPreviewDataHeader() throws Exception{
List<Map> list = new ArrayList<>();
List<ExcelColumn> excelColumns = getExcelDataListener().getExcelColumnList(getModelClass().getSimpleName());
if(V.notEmpty(excelColumns)){
for(ExcelColumn excelColumn : excelColumns){
Map map = new HashMap();
map.put("title",excelColumn.getColName());
map.put("dataIndex", excelColumn.getModelField());
list.add(map);
}
}
return list;
}
}

View File

@ -1,38 +0,0 @@
package com.diboot.component.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import java.math.BigDecimal;
public class BigDecimalConverter implements Converter<BigDecimal> {
@Override
public Class supportJavaTypeKey() {
return BigDecimal.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.NUMBER;
}
@Override
public BigDecimal convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
BigDecimal value = null;
String colName = contentProperty.getHead().getHeadNameList().get(0);//当前列
try {
value = cellData.getNumberValue();
} catch (Exception e) {
throw new Exception("["+colName+"]列数据格式有误,请填写正确的浮点型数据");
}
return value;
}
@Override
public CellData convertToExcelData(BigDecimal value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(value);
}
}

View File

@ -1,39 +0,0 @@
package com.diboot.component.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
public class BooleanConverter implements Converter<Boolean> {
@Override
public Class supportJavaTypeKey() {
return Boolean.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.BOOLEAN;
}
@Override
public Boolean convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
Boolean value = null;
String colName = contentProperty.getHead().getHeadNameList().get(0);//当前列
if(!CellDataTypeEnum.BOOLEAN.equals(cellData.getType())){
throw new Exception("["+colName+"]列数据格式有误,请填写正确的逻辑类型数据");
}
try {
value = cellData.getBooleanValue();
} catch (Exception e) {
throw new Exception("["+colName+"]列数据格式有误,请填写正确的逻辑类型数据");
}
return value;
}
@Override
public CellData convertToExcelData(Boolean value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(value);
}
}

View File

@ -1,45 +0,0 @@
package com.diboot.component.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.DateUtils;
import org.apache.poi.ss.usermodel.DateUtil;
import java.util.Date;
public class DateConverter implements Converter<Date> {
@Override
public Class supportJavaTypeKey() {
return Date.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.NUMBER;
}
@Override
public Date convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
Date value = null;
String colName = contentProperty.getHead().getHeadNameList().get(0);//当前列
try {
value = DateUtil.getJavaDate(cellData.getNumberValue().doubleValue(),
globalConfiguration.getUse1904windowing(), null);
} catch (Exception e) {
throw new Exception("["+colName+"]列数据格式有误,请填写正确的时间类型数据");
}
return value;
}
@Override
public CellData convertToExcelData(Date value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
return new CellData(DateUtils.format(value, null));
} else {
return new CellData(DateUtils.format(value, contentProperty.getDateTimeFormatProperty().getFormat()));
}
}
}

View File

@ -1,38 +0,0 @@
package com.diboot.component.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import java.math.BigDecimal;
public class DoubleConverter implements Converter<Double> {
@Override
public Class supportJavaTypeKey() {
return Double.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.NUMBER;
}
@Override
public Double convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
Double value = null;
String colName = contentProperty.getHead().getHeadNameList().get(0);//当前列
try {
value = cellData.getNumberValue().doubleValue();
} catch (Exception e) {
throw new Exception("["+colName+"]列数据格式有误,请填写正确的浮点型数据");
}
return value;
}
@Override
public CellData convertToExcelData(Double value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(BigDecimal.valueOf(value));
}
}

View File

@ -1,36 +0,0 @@
package com.diboot.component.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
public class IntegerConverter implements Converter<Integer> {
@Override
public Class supportJavaTypeKey() {
return Integer.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.NUMBER;
}
@Override
public Integer convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
Integer value = null;
String colName = contentProperty.getHead().getHeadNameList().get(0);//当前列
try {
value = cellData.getNumberValue().intValue();
} catch (Exception e) {
throw new Exception("["+colName+"]列数据格式有误,请填写正确的整型数据");
}
return value;
}
@Override
public CellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(value.toString());
}
}

View File

@ -1,29 +0,0 @@
package com.diboot.component.excel.converter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
public class StringConverter implements Converter<String> {
@Override
public Class supportJavaTypeKey() {
return String.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return cellData.getStringValue();
}
@Override
public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(value);
}
}

View File

@ -1,13 +0,0 @@
package com.diboot.component.excel.entity;
import lombok.Data;
/***
* excel数据导入导出实体基类
* @auther wangyl
* @date 2019-10-9
*/
@Data
public class BaseExcelDataEntity {
}

View File

@ -1,34 +0,0 @@
package com.diboot.component.excel.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.diboot.core.entity.BaseEntity;
import lombok.Data;
/**
* Excel列定义
* @author Mazc
* @version 2017-09-18
* Copyright @ www.com.ltd
*/
@Data
public class ExcelColumn extends BaseEntity {
private static final long serialVersionUID = -1539079350889067812L;
@TableField
private String modelClass; // Java对象类
@TableField
private String modelField; // Java对象属性
@TableField
private String colName; // 列标题
@TableField
private Integer colIndex; // 列索引
@TableField
private String dataType; // 数据类型
@TableField
private String validation; // 校验
}

View File

@ -1,29 +0,0 @@
package com.diboot.component.excel.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.diboot.core.entity.BaseEntity;
import lombok.Data;
/**
* Excel导入记录
* @author Mazc
* @version 2017-09-18
* Copyright @ www.com.ltd
*/
@Data
public class ExcelImportRecord extends BaseEntity {
private static final long serialVersionUID = 7628990901469833759L;
@TableField
private String fileUuid; // 文件ID
@TableField
private String relObjType; // 关联类型
@TableField
private Long relObjId; // 关联ID
@TableField
private String relObjUid; // 关联UUID
}

View File

@ -1,246 +0,0 @@
package com.diboot.component.excel.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.diboot.component.excel.entity.BaseExcelDataEntity;
import com.diboot.component.excel.entity.ExcelColumn;
import com.diboot.component.excel.service.ExcelColumnService;
import com.diboot.core.entity.BaseEntity;
import com.diboot.core.service.BaseService;
import com.diboot.core.util.BeanUtils;
import com.diboot.core.util.JSON;
import com.diboot.core.util.V;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/***
* excel数据导入导出listener基类
* @auther wangyl
* @date 2019-10-9
*/
@Component
public abstract class BaseExcelDataListener <T extends BaseExcelDataEntity, E extends BaseEntity> extends AnalysisEventListener<T> {
private static final Logger logger = LoggerFactory.getLogger(BaseExcelDataListener.class);
@Autowired
protected ExcelColumnService excelColumnService;
private Map<Integer, String> headMap;//解析出的excel表头
private List<T> dataList = new ArrayList<>();//解析后的数据实体list
private List<E> entityList = new ArrayList<>();//可存入数据库的数据实体list
private List<String> errorMsgs = new ArrayList<>();//错误日志
private boolean isPreview = false;//是否预览模式默认否
private static Map<String, List<ExcelColumn>> excelColumnMap = null;//Excel列定义缓存
/*
* 当前一行数据解析成功后的操作
* */
@Override
public void invoke(T data, AnalysisContext context) {
dataList.add(data);
}
/*
* 所有数据解析成功后的操作
* */
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
if(isPreview){
return;
}
checkHeader(headMap);//表头校验
checkDataList(dataList);//数据校验
List<String> errors = customVerify(dataList);//自定义数据校验
if(V.notEmpty(errors)){
errorMsgs.addAll(errors);
}
try {
convertToEntityList(dataList);
} catch (Exception e) {
logger.error("excel数据转化失败",e);
errorMsgs.add("excel数据解析失败");
}
if(V.notEmpty(errorMsgs)){
return;
}
try {
saveData();
} catch (Exception e) {
logger.error("保存excel数据失败", e);
errorMsgs.add("保存excel数据失败");
}
}
/*
* 在转换异常获取其他异常下会调用本接口
* 抛出异常则停止读取如果这里不抛出异常则 继续读取下一行
* */
@Override
public void onException(Exception exception, AnalysisContext context) throws Exception {
int currentRowNum = context.readRowHolder().getRowIndex();
//数据类型转化异常
if (exception instanceof ExcelDataConvertException) {
logger.error("数据转化异常", exception);
errorMsgs.add(""+currentRowNum+""+exception.getCause().getMessage());
}else{//其他异常
logger.error("出现暂未处理的异常:",exception);
errorMsgs.add(exception.getMessage());
}
}
/*
* excel表头数据
* */
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
logger.error("解析到一条表头数据:",JSON.toJSONString(headMap));
this.headMap = headMap;
}
/*
* 校验表头
* */
private void checkHeader(Map<Integer, String> headMap) {
List<ExcelColumn> excelColumnList = null;
try {
excelColumnList = getExcelColumnList(getModelClass().getSimpleName());
} catch (Exception e) {
logger.error("获取表格列定义失败");
errorMsgs.add("获取表格列定义失败");
return;
}
if(V.isEmpty(headMap) || V.isEmpty(excelColumnList)){
logger.error("请设置excel表头");
errorMsgs.add("请设置excel表头");
return;
}
if(headMap.size() != excelColumnList.size()){
logger.error("Excel文件中的标题列数与期望不符");
errorMsgs.add("Excel文件中的标题列数与预期不符,期望为"+excelColumnList.size()+"");
return;
}
for(ExcelColumn excelColumn : excelColumnList){
if(V.notEquals(excelColumn.getColName(), headMap.get(excelColumn.getColIndex()-1))){
errorMsgs.add("标题名:["+headMap.get(excelColumn.getColIndex()-1)+"]与预期不符,请改为["+excelColumn.getColName()+"]");
}
}
}
/*
* 校验数据实体list
* */
private void checkDataList(List<T> dataList) {
List<ExcelColumn> excelColumnList = null;
try {
excelColumnList = getExcelColumnList(getModelClass().getSimpleName());
} catch (Exception e) {
logger.error("获取表格列定义失败", e);
errorMsgs.add("获取表格列定义失败");
return;
}
if(V.isEmpty(excelColumnList)){
logger.error("获取表格列定义为空");
errorMsgs.add("获取表格列定义为空");
return;
}
if(V.notEmpty(dataList)){
for(int i=0;i<dataList.size();i++){
checkData(dataList.get(i), i+1,excelColumnList);
}
}
}
/*
* 校验数据实体
* */
private void checkData(T data, Integer currentRowNum, List<ExcelColumn> excelColumnList) {
if(V.notEmpty(excelColumnList)){
for(ExcelColumn excelColumn : excelColumnList){
String value = BeanUtils.getStringProperty(data,excelColumn.getModelField());
String error = V.validate(value, excelColumn.getValidation());
if(V.notEmpty(error)){
errorMsgs.add(""+currentRowNum+"行["+excelColumn.getColName()+"]"+error);
}
}
}
}
/*
* 将解析后的数据实体list转化为可存入数据库的实体list
* */
private void convertToEntityList(List<T> dataList) throws Exception{
entityList = BeanUtils.convertList(dataList, getModelClass());
}
/*
* 自定义数据检验方式数据重复性校验等,返回校验日志信息
* */
protected abstract List<String> customVerify(List<T> dataList) ;
/*
* 保存解析的数据到数据库
* */
private boolean saveData() throws Exception{
return getBusinessService().createEntities(entityList);
}
/***
* 获取业务的service
* @return
*/
protected abstract BaseService getBusinessService();
/***
* 获取Model类
* @return
*/
protected abstract Class<E> getModelClass();
/**
* 加载表格列定义
*/
public List<ExcelColumn> getExcelColumnList(String modelClass) throws Exception{
if(excelColumnMap == null){
excelColumnMap = new ConcurrentHashMap<>();
}
List<ExcelColumn> list = excelColumnMap.get(modelClass);
if(list == null){
// 构建查询时的排序定义根据列序号进行升序排列
LambdaQueryWrapper<ExcelColumn> wrapper = new LambdaQueryWrapper();
wrapper.eq(ExcelColumn::getModelClass, modelClass)
.orderByAsc(ExcelColumn::getColIndex);
list = excelColumnService.getEntityList(wrapper);
excelColumnMap.put(modelClass, list);
}
return list;
}
public List<T> getDataList(){
return dataList;
}
public List<E> getEntityList(){
return entityList;
}
public List<String> getErrorMsgs(){
return errorMsgs;
}
public boolean isPreview() {
return isPreview;
}
public void setPreview(boolean preview) {
isPreview = preview;
}
}

View File

@ -1,13 +0,0 @@
package com.diboot.component.excel.mapper;
import com.diboot.component.excel.entity.ExcelColumn;
import com.diboot.core.mapper.BaseCrudMapper;
/**
* @author Lishuaifei
* @description
* @creatime 2019-07-11 17:03
*/
public interface ExcelColumnMapper extends BaseCrudMapper<ExcelColumn> {
}

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"./mybatis-3-mapper.dtd">
<mapper namespace="com.diboot.component.excel.mapper.ExcelColumnMapper">
</mapper>

View File

@ -1,13 +0,0 @@
package com.diboot.component.excel.mapper;
import com.diboot.component.excel.entity.ExcelImportRecord;
import com.diboot.core.mapper.BaseCrudMapper;
/**
* @author Lishuaifei
* @description
* @creatime 2019-07-11 17:03
*/
public interface ExcelImportRecordMapper extends BaseCrudMapper<ExcelImportRecord> {
}

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"./mybatis-3-mapper.dtd">
<mapper namespace="com.diboot.component.excel.mapper.ExcelImportRecordMapper">
</mapper>

View File

@ -1,13 +0,0 @@
package com.diboot.component.excel.service;
import com.diboot.component.excel.entity.ExcelColumn;
import com.diboot.core.service.BaseService;
/**
* @author Lishuaifei
* @description
* @creatime 2019-07-11 17:05
*/
public interface ExcelColumnService extends BaseService<ExcelColumn> {
}

View File

@ -1,18 +0,0 @@
package com.diboot.component.excel.service;
import com.diboot.component.excel.entity.ExcelImportRecord;
import com.diboot.core.entity.BaseEntity;
import com.diboot.core.service.BaseService;
import java.util.List;
/**
* @author Lishuaifei
* @description
* @creatime 2019-07-11 17:05
*/
public interface ExcelImportRecordService extends BaseService<ExcelImportRecord> {
boolean batchCreateRecords(String fileUid, List<? extends BaseEntity> modelList);
}

View File

@ -1,17 +0,0 @@
package com.diboot.component.excel.service.impl;
import com.diboot.component.excel.entity.ExcelColumn;
import com.diboot.component.excel.mapper.ExcelColumnMapper;
import com.diboot.component.excel.service.ExcelColumnService;
import com.diboot.core.service.impl.BaseServiceImpl;
import org.springframework.stereotype.Service;
/**
* @author Lishuaifei
* @description
* @creatime 2019-07-11 17:06
*/
@Service
public class ExcelColumnServiceImpl extends BaseServiceImpl<ExcelColumnMapper, ExcelColumn> implements ExcelColumnService {
}

View File

@ -1,36 +0,0 @@
package com.diboot.component.excel.service.impl;
import com.diboot.component.excel.entity.ExcelImportRecord;
import com.diboot.component.excel.mapper.ExcelImportRecordMapper;
import com.diboot.component.excel.service.ExcelImportRecordService;
import com.diboot.core.entity.BaseEntity;
import com.diboot.core.service.impl.BaseServiceImpl;
import com.diboot.core.util.V;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @author Lishuaifei
* @description
* @creatime 2019-07-11 17:06
*/
@Service
public class ExcelImportRecordServiceImpl extends BaseServiceImpl<ExcelImportRecordMapper, ExcelImportRecord> implements ExcelImportRecordService {
@Override
public boolean batchCreateRecords(String fileUid, List<? extends BaseEntity> modelList) {
List<ExcelImportRecord> recordList = new ArrayList<>();
if(V.notEmpty(modelList)){
for(BaseEntity entity : modelList){
ExcelImportRecord record = new ExcelImportRecord();
record.setFileUuid(fileUid);
record.setRelObjType(entity.getClass().getSimpleName());
record.setRelObjId(entity.getId());
recordList.add(record);
}
}
return createEntities(recordList);
}
}

View File

@ -1,103 +0,0 @@
package com.diboot.component.excel.utils;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.diboot.component.excel.entity.BaseExcelDataEntity;
import com.diboot.component.excel.listener.BaseExcelDataListener;
import com.diboot.core.util.V;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.List;
/***
* excel数据导入导出工具类
* @auther wangyl
* @date 2019-10-9
*/
public class EasyExcelHelper {
private static final Logger logger = LoggerFactory.getLogger(EasyExcelHelper.class);
/**
* 简单读取excel文件数据,当isPreview=false时保存到数据库
* @param filePath
* @param clazz
* @param listener
* @return
*/
public static <T extends BaseExcelDataEntity> List<T> simpleRead(String filePath, Class<T> clazz, BaseExcelDataListener listener, boolean isPreview) throws Exception{
File file = new File(filePath);
if(!file.exists()){
logger.error("找不到指定文件,路径:"+filePath);
throw new Exception("找不到指定文件,导入excel失败");
}
listener.setPreview(isPreview);
EasyExcel.read(filePath, clazz, listener).sheet().doRead();
return listener.getDataList();
}
public static <T extends BaseExcelDataEntity> boolean simpleReadAndSave(String filePath, Class<T> clazz, BaseExcelDataListener listener) throws Exception{
File file = new File(filePath);
if(!file.exists()){
logger.error("找不到指定文件,路径:"+filePath);
throw new Exception("找不到指定文件,导入excel失败");
}
listener.setPreview(false);
EasyExcel.read(filePath, clazz, listener).sheet().doRead();
if(V.notEmpty(listener.getErrorMsgs())){
return false;
}
return true;
}
/**
* 简单将数据写入excel文件
* @param filePath
* @param clazz
* @param dataList
* @param <T>
* @return
*/
public static <T extends BaseExcelDataEntity> boolean simpleWrite(String filePath, Class<T> clazz, List dataList) throws Exception{
return simpleWrite(filePath, clazz, null, dataList);
}
/**
* 简单将数据写入excel文件
* @param filePath
* @param clazz
* @param sheetName
* @param dataList
* @param <T>
* @return
*/
public static <T extends BaseExcelDataEntity> boolean simpleWrite(String filePath, Class<T> clazz, String sheetName, List dataList) throws Exception{
try {
EasyExcel.write(filePath, clazz).sheet(sheetName).doWrite(dataList);
} catch (Exception e) {
logger.error("数据写入excel文件失败",e);
return false;
}
return true;
}
/**
* 简单将数据写入excel文件,列宽自适应数据长度
* @param filePath
* @param clazz
* @param dataList
* @param <T>
* @return
*/
public static <T extends BaseExcelDataEntity> boolean simpleWriteWithAdaptColumnWidth(String filePath, Class<T> clazz, List dataList) throws Exception{
try {
EasyExcel.write(filePath, clazz).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet().doWrite(dataList);
} catch (Exception e) {
logger.error("数据写入excel文件失败",e);
return false;
}
return true;
}
}

View File

@ -1,16 +0,0 @@
dependencies {
// core
compile project(":diboot-core")
//
compile ("net.coobird:thumbnailator:0.4.8")
//
compile("org.apache.httpcomponents:httpclient:4.5.9")
}
jar{
manifest {
attributes('Implementation-Title': 'Diboot component - file',
'Implementation-Version': '2.0.3'
)
}
}

View File

@ -1,175 +0,0 @@
package com.diboot.component.file.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.diboot.core.entity.BaseEntity;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
/**
* file基础父类
* @author wangyl
* @version v2.0
* @date 2019/07/18
*/
@TableName("file")
public class BaseFile extends BaseEntity {
private static final long serialVersionUID = 201L;
public enum FILE_STATUS {
S,
F
}
// 废弃默认主键
@TableField(exist = false)
private Long id;
// 声明新主键uuid
@NotNull(message = "编号不能为空!")
@Length(max = 32, message = "编号长度超出了最大限制!")
@TableId(type = IdType.UUID)
private String uuid = null;
@NotNull(message = "关联对象类型不能为空!")
@Length(max = 50, message = "关联对象类型长度超出了最大限制!")
@TableField
private String relObjType = null;
@TableField
@NotNull(message = "关联对象ID不能为空")
private Long relObjId;
@TableField
@NotNull(message = "文件名不能为空!")
@Length(max = 100, message = "文件名长度超出了最大限制!")
private String name;
@TableField
@NotNull(message = "文件链接不能为空!")
@Length(max = 255, message = "文件链接长度超出了最大限制!")
private String link;
@TableField
@NotNull(message = "文件路径不能为空!")
@Length(max = 255, message = "文件路径长度超出了最大限制!")
private String path;
@TableField
@Length(max = 20, message = "文件类型长度超出了最大限制!")
private String fileType;
@TableField
private int dataCount = 0;
@TableField
private Long size;
@TableField
@Length(max = 1, message = "状态长度超出了最大限制!")
private String status;
@TableField
@Length(max = 255, message = "备注长度超出了最大限制!")
private String comment;
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getRelObjType() {
return this.relObjType;
}
public void setRelObjType(String relObjType) {
this.relObjType = relObjType;
}
public Long getRelObjId() {
return this.relObjId;
}
public void setRelObjId(Long relObjId) {
this.relObjId = relObjId;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getLink() {
return this.link;
}
public void setLink(String link) {
this.link = link;
}
public String getPath() {
return this.path;
}
public void setPath(String path) {
this.path = path;
}
public String getFileType() {
return this.fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public Long getSize() {
return this.size;
}
public void setSize(Long size) {
this.size = size;
}
public String getComment() {
return this.comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public int getDataCount() {
return this.dataCount;
}
public void setDataCount(int dataCount) {
this.dataCount = dataCount;
}
public String getStatus() {
return this.status;
}
public String getStatusLabel(){
if(FILE_STATUS.S.name().equals(this.status)){
return "成功";
}else if(FILE_STATUS.F.name().equals(this.status)){
return "失败";
}else{
return null;
}
}
public void setStatus(String status) {
this.status = status;
}
}

View File

@ -1,640 +0,0 @@
package com.diboot.component.file.file;
import com.diboot.component.file.file.http.CustomSSLSocketFactory;
import com.diboot.core.util.PropertiesUtils;
import com.diboot.core.util.S;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/***
* 文件操作辅助类
* @author Mazc
*/
public class FileHelper{
private static final Logger logger = LoggerFactory.getLogger(FileHelper.class);
/**
* image验证
*/
public static final String VALID_IMAGE_SUFFIX = "|png|jpg|jpeg|gif|bmp|";
/**
* file验证
*/
public static final String DANGER_FILE_SUFFIX = "|exe|bat|bin|dll|sh";
/**
* excel格式
*/
public static final String EXCEL_SUFFIX = "|xls|xlsx|xlsm|";
/**
* 文件存储路径参数名
*/
public static final String FILE_STORAGE_DIRECTORY = "files.storage.directory";
/**
* 文件存储路径
*/
private static final String PATH_FILE = "/upload/file";
private static final String PATH_IMG = "/upload/img";
private static final String PATH_AUDIO = "/upload/audio";
/**
* 日期格式-文件夹名称
*/
public static final String FORMAT_DATE_Y2M = "yyMM";
public static final String FORMAT_DATE_Y2MD = "yyMMdd";
/***
* 默认contextType
*/
private static final String DEFAULT_CONTEXT_TYPE = "application/octet-stream";
/***
* 文件扩展名-ContentType的对应关系
*/
private static Map<String, String> EXT_CONTENT_TYPE_MAP = new HashMap(){{
put("xls", "application/x-msdownload");
put("xlsx", "application/x-msdownload");
put("doc", "application/x-msdownload");
put("docx", "application/x-msdownload");
put("dot", "application/x-msdownload");
put("ppt", "application/x-msdownload");
put("pptx", "application/x-msdownload");
put("pdf", "application/pdf");
put("avi", "video/avi");
put("bmp", "application/x-bmp");
put("css", "text/css");
put("dll", "application/x-msdownload");
put("dtd", "text/xml");
put("exe", "application/x-msdownload");
put("gif", "image/gif");
put("htm", "text/html");
put("html", "text/html");
put("ico", "image/x-icon");
put("jpeg", "image/jpeg");
put("jpg", "image/jpeg");
put("js", "application/x-javascript");
put("mp3", "audio/mp3");
put("mp4", "video/mpeg4");
put("png", "image/png");
put("svg", "text/xml");
put("swf", "application/x-shockwave-flash");
put("tif", "image/tiff");
put("tiff", "image/tiff");
put("tld", "text/xml");
put("torrent", "application/x-bittorrent");
put("tsd", "text/xml");
put("txt", "text/plain");
put("wav", "audio/wav");
put("wma", "audio/x-ms-wma");
put("wmf", "application/x-wmf");
put("wsdl", "text/xml");
put("xhtml", "text/html");
put("xml", "text/xml");
put("xsd", "text/xml");
put("xsl", "text/xml");
put("xslt", "text/xml");
put("apk", "application/vnd.android.package-archive");
}};
/**
* 文件和图片的后台存储路径
*/
private static String fileStorageDirectory = null;
/***
* 是否为合法的文件类型
* @param ext
* @return
*/
public static boolean isValidFileExt(String ext){
return !DANGER_FILE_SUFFIX.contains("|"+ext.toLowerCase());
}
/**
* 是否是图片类型
* @param ext
* @return
*/
public static boolean isImage(String ext){
return VALID_IMAGE_SUFFIX.contains("|"+ext.toLowerCase()+"|");
}
/**
* 是否是Excel文件
* @param fileName
* @return
*/
public static boolean isExcel(String fileName){
String ext = getFileExtByName(fileName);
return EXCEL_SUFFIX.contains("|"+ext.toLowerCase()+"|");
}
/***
* 获取系统临时目录
* @return
*/
public static String getSystemTempDir(){
return System.getProperty("java.io.tmpdir");
}
/***
* 将文件保存到系统临时目录
* @param file
* @param fileName
* @return
*/
public static String saveFile2TempDir(MultipartFile file, String fileName){
String fullPath = getSystemTempDir() + fileName;
try {
FileUtils.writeByteArrayToFile(new File(fullPath), file.getBytes());
return fileName;
}
catch (IOException e1) {
logger.error("保存原图片失败(image=" + fileName + "): ", e1);
return null;
}
}
/**
* 保存图片
* @param file 上传文件
* @param imgName 图片名称
*/
public static String saveImage(MultipartFile file, String imgName){
// 生成图片路径
String accessPath = getImageStoragePath(imgName);
String fullPath = getFileStorageDirectory() + accessPath;
try {
// 创建文件夹
makeDirectory(fullPath);
FileUtils.writeByteArrayToFile(new File(fullPath), file.getBytes());
if(logger.isDebugEnabled()){
logger.debug("保存图片成功!路径为: " + accessPath);
}
return accessPath;
}
catch (IOException e1) {
logger.error("保存原图片失败(image=" + accessPath + "): ", e1);
return null;
}
}
/**
* 保存图片
* @param file 上传文件
*/
public static String saveImage(MultipartFile file){
String fileName = file.getOriginalFilename();
String ext = fileName.substring(fileName.lastIndexOf(".")+1);
String newFileName = S.newUuid() + "." + ext;
return saveImage(file, newFileName);
}
/**
* 保存图片
* @param file 上传文件
* @param imgName 图片名称
* @param reserve 是否保留原图片
* @return
*/
public static String saveImage(File file, String imgName, boolean reserve){
// 生成图片路径
String accessPath = getImageStoragePath(imgName);
String fullPath = getFileStorageDirectory() + accessPath;
try{
// 创建文件夹
makeDirectory(fullPath);
FileUtils.copyFile(new File(fullPath), file);
if(logger.isDebugEnabled()){
logger.debug("保存图片成功!路径为: " + accessPath);
}
// 如果原文件与目标文件不相等且不保留原文件则删除原文件
if (!reserve && !StringUtils.equals(file.getAbsolutePath(), fullPath)){
FileUtils.forceDelete(file);
}
return accessPath;
} catch (Exception e){
logger.error("保存原图片失败(image=" + accessPath + "): ", e);
return null;
}
}
/***
* 上传文件
* @param file 上传文件
* @param fileName 文件名
* @return
*/
public static String saveFile(MultipartFile file, String fileName) {
// 生成文件路径
String accessPath = getFileStoragePath(fileName);
String fullPath = getFileStorageDirectory() + accessPath;
try {
// 创建文件夹
makeDirectory(fullPath);
FileUtils.writeByteArrayToFile(new File(fullPath), file.getBytes());
if(logger.isDebugEnabled()){
logger.debug("保存文件成功!路径为: " + fullPath);
}
return accessPath;
}
catch (IOException e1) {
logger.error("保存文件失败(file=" + fullPath + "): ", e1);
return null;
}
}
/***
* 上传文件
* @param file 上传文件
* @return
*/
public static String saveFile(MultipartFile file){
String fileName = file.getOriginalFilename();
String ext = fileName.substring(fileName.lastIndexOf(".")+1);
String newFileName = S.newUuid() + "." + ext;
return saveFile(file, newFileName);
}
/**
* 上传文件
* @param file 上传文件
* @param fileName 文件名
* @param reserve 保留原文件
* @return
*/
public static String saveFile(File file, String fileName, boolean reserve){
// 生成文件路径
String accessPath = getFileStoragePath(fileName);
String fullPath = getFileStorageDirectory() + accessPath;
try{
// 创建文件夹
makeDirectory(fullPath);
FileUtils.copyFile(new File(fullPath), file);
if(logger.isDebugEnabled()){
logger.debug("保存文件成功!路径为: " + fullPath);
}
// 如果原文件与目标文件不相等且不保留原文件则删除原文件
if (!reserve && !StringUtils.equals(file.getAbsolutePath(), fullPath)){
FileUtils.forceDelete(file);
}
return accessPath;
} catch (Exception e){
logger.error("保存文件失败(file=" + fullPath + "): ", e);
return null;
}
}
/***
* 根据名称取得后缀
* @param fileName
* @return
*/
public static String getFileExtByName(String fileName){
if(fileName.startsWith("http") && fileName.contains("/")){
fileName = getFileName(fileName);
}
if(fileName.lastIndexOf(".") > 0){
return fileName.substring(fileName.lastIndexOf(".")+1).toLowerCase();
}
logger.warn("检测到没有后缀的文件名:" + fileName);
return "";
}
/**
* 得到图片存储的全路径
* @param fileName
* @return
*/
public static String getImageStoragePath(String fileName){
StringBuilder sb = new StringBuilder();
sb.append(PATH_IMG).append("/").append(getYearMonth()).append("/").append(fileName);
return sb.toString();
}
/***
* 获取文件的存储路径
* @param name
* @return
*/
public static String getFileStoragePath(String name) {
StringBuilder sb = new StringBuilder();
sb.append(PATH_FILE).append("/").append(getYearMonth()).append("/").append(name);
return sb.toString();
}
/***
* 获取录音文件的存储路径
* @param category
* @return
*/
public static String getAudioStoragePath(String category, String name) {
StringBuilder sb = new StringBuilder();
sb.append(PATH_AUDIO);
if(StringUtils.isNotBlank(category)){
sb.append("/").append(category);
}
sb.append("/").append(getYearMonthDay()).append("/").append(name);
return sb.toString();
}
/***
* 获取文件的全路径
* @return
*/
public static String getFullFilePath(String filePath){
return getFileStorageDirectory()+filePath;
}
/**
* 根据文件URL解析出其文件名
* @param fileURL
* @return
*/
public static String getFileName(String fileURL){
String temp = StringUtils.substring(fileURL, fileURL.lastIndexOf("/")+1);
if(StringUtils.contains(fileURL, "?")){
temp = StringUtils.substring(temp, 0, temp.lastIndexOf("?"));
}
return temp;
}
/**
* 文件的存储路径
* @return
*/
public static String getFileStorageDirectory(){
if(fileStorageDirectory == null){
fileStorageDirectory = PropertiesUtils.get(FILE_STORAGE_DIRECTORY);
}
return fileStorageDirectory;
}
/***
* 创建文件夹
* @param dirPath
* @return
*/
public static boolean makeDirectory(String dirPath){
String imageDirectory = StringUtils.substringBeforeLast(dirPath, "/");
File dir = new File(imageDirectory);
if(!dir.exists()){
try {
FileUtils.forceMkdir(dir);
return true;
}
catch (IOException e) {
return false;
}
}
return false;
}
/**
* 压缩图片
* @param imgUrl
* @return
*/
public static String generateThumbnail(String imgUrl, int width, int height){
String file = imgUrl.substring(imgUrl.indexOf("/img/"));
String imageFileDirectory = getFileStorageDirectory() + file;
try {
// 压缩图片
String targetFile = imgUrl.replace(".", "_tn.");
Thumbnails.of(imageFileDirectory).size(width, height).outputQuality(0.7f).toFile(getFileStorageDirectory() + targetFile);
return targetFile;
}
catch (IOException e1) {
logger.error("压缩图片异常(image=" + imageFileDirectory + "): ", e1);
}
return imgUrl;
}
/**
* 根据文件路径与请求信息下载文件
* @param path
* @param request
* @param response
* @throws Exception
*/
public static void downloadLocalFile(String path, HttpServletRequest request, HttpServletResponse response) throws Exception{
request.setCharacterEncoding("UTF-8");
String downloadPath = path;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try{
String outFileName = getFileName(path);
String fileName = new String(outFileName.getBytes("utf-8"), "ISO8859-1");
long fileLength = new File(downloadPath).length();
response.setContentType(getContextType(fileName));
response.setHeader("Content-disposition", "attachment; filename="+ fileName);
response.setHeader("Content-Length", String.valueOf(fileLength));
bis = new BufferedInputStream(new FileInputStream(downloadPath));
bos = new BufferedOutputStream(response.getOutputStream());
byte[] buff = new byte[2048];
int bytesRead;
while(-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
}
catch (Exception e) {
logger.error("下载导出文件失败:"+path, e);
}
finally {
if (bis != null) {
bis.close();
}
if (bos != null) {
bos.close();
}
}
}
/****
* HTTP下载文件
* @param fileUrl
* @param targetFilePath
* @return
*/
public static boolean downloadRemoteFile(String fileUrl, String targetFilePath) {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(fileUrl);
try {
HttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity entity = httpResponse.getEntity();
long length = entity.getContentLength();
if(length <= 0){
logger.warn("下载文件不存在!");
return false;
}
FileUtils.copyInputStreamToFile(entity.getContent(), new File(targetFilePath));
return true;
}
catch (MalformedURLException e) {
logger.error("下载文件URL异常(url=" + fileUrl + "): ", e);
return false;
}
catch (IOException e) {
logger.error("下载文件IO异常(url=" + fileUrl + "): ", e);
return false;
}
finally{
try {
httpClient.close();
}
catch (IOException e) {
logger.error("关闭httpClient下载连接异常:", e);
}
}
}
/****
* HTTP下载文件
* @param fileUrl
* @param
* @return
*/
public static boolean downloadRemoteFileViaHttps(String fileUrl, String targetFilePath) {
HttpClient httpClient = CustomSSLSocketFactory.newHttpClient();
try {
HttpGet httpGet = new HttpGet();
httpGet.setURI(new URI(fileUrl));
HttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity entity = httpResponse.getEntity();
long length = entity.getContentLength();
if(length <= 0){
logger.warn("下载文件不存在!");
return false;
}
FileUtils.copyInputStreamToFile(entity.getContent(), new File(targetFilePath));
return true;
}
catch (MalformedURLException e) {
logger.error("下载文件URL异常(url=" + fileUrl + "): ", e);
return false;
}
catch (IOException e) {
logger.error("下载文件IO异常(url=" + fileUrl + "): ", e);
return false;
}
catch (Exception e) {
logger.error("下载文件异常(url=" + fileUrl + "): ", e);
return false;
}
}
/****
* 删除文件
* @param fileStoragePath
*/
public static void deleteFile(String fileStoragePath) {
File wavFile = new File(fileStoragePath);
if(wavFile.exists()){
wavFile.delete();
}
}
/**
* 根据文件类型获取ContentType
* @param fileName
* @return
*/
public static String getContextType(String fileName){
String ext = S.substringAfterLast(fileName, ".");
String contentType = EXT_CONTENT_TYPE_MAP.get(ext);
if(contentType == null){
contentType = DEFAULT_CONTEXT_TYPE;
}
return contentType + ";charset=utf-8";
}
/**
* 得到当前的年月YYMM用于生成文件夹名称
* @return
*/
private static String getYearMonth(){
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_DATE_Y2M);
return sdf.format(cal.getTime());
}
/**
* 得到当前的年月YYMM用于生成文件夹
* @return
*/
private static String getYearMonthDay(){
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_DATE_Y2MD);
return sdf.format(cal.getTime());
}
/***
* 获取请求中的多个文件数据
* @param request
* @param fileInputName
* @return
*/
public static List<MultipartFile> getFilesFromRequest(HttpServletRequest request, String... fileInputName){
// 解析上传文件
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if(!isMultipart){
logger.warn("请上传文件!");
return null;
}
// 获取附件文件名
String inputName = "attachedFiles";
if(fileInputName != null && fileInputName.length > 0){
inputName = fileInputName[0];
}
// 解析上传文件
List<MultipartFile> files = ((MultipartHttpServletRequest)request).getFiles(inputName);
return files;
}
/***
* 获取请求中的单个文件数据
* @param request
* @param fileInputName
* @return
*/
public static MultipartFile getFileFromRequest(HttpServletRequest request, String... fileInputName){
// 解析上传文件
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if(!isMultipart){
logger.warn("请上传文件!");
return null;
}
// 获取附件文件名
String inputName = "attachedFiles";
if(fileInputName != null && fileInputName.length > 0){
inputName = fileInputName[0];
}
// 解析上传文件
MultipartFile file = ((MultipartHttpServletRequest)request).getFile(inputName);
return file;
}
}

View File

@ -1,32 +0,0 @@
package com.diboot.component.file.file.audio;
import com.diboot.component.file.utils.RuntimeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/***
* Dibo 音频处理类
* @author Mazc
* @version 2016年11月7日
* Copyright @ www.com.ltd
*/
public class AudioHelper {
private static final Logger logger = LoggerFactory.getLogger(AudioHelper.class);
/***
* 将wav音频转换为MP3格式
* @param sourceFilePath
* @param targetFilePath
* @return
*/
public static String convertWavToMp3(String sourceFilePath, String targetFilePath){
boolean success = RuntimeHelper.run("ffmpeg -i "+ sourceFilePath +" "+ targetFilePath); // -ar 8000 -ac 1 -y
if(success){
return targetFilePath;
}
else{
return sourceFilePath;
}
}
}

View File

@ -1,94 +0,0 @@
package com.diboot.component.file.file.http;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/****
* HttpClient工厂类 解决HTTPS验证问题
* @author Mazc
* @version 2017年8月8日
*
*/
public class CustomSSLSocketFactory extends SSLSocketFactory {
private static final Logger logger = LoggerFactory.getLogger(CustomSSLSocketFactory.class);
SSLContext sslContext = SSLContext.getInstance("TLS");
public CustomSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
/**
* 返回httpclient实例避开https验证
* @return
*/
public static HttpClient newHttpClient() {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new CustomSSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(ccm, params);
}
catch (Exception e) {
return null;
}
}
}

View File

@ -1,81 +0,0 @@
package com.diboot.component.file.file.image;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.util.Base64;
/***
* 图片操作辅助类
* @author Mazc
*/
public class ImageHandler {
private static final Logger logger = LoggerFactory.getLogger(ImageHandler.class);
private static final String DATA_IMAGE_FLAG = "data:image/";
private static final String BASE_64_FLAG = "base64,";
/**
* 将Base64转换为图片
* @param base64Str
* @param fullFilePath
* @return
* @throws IOException
*/
private static boolean convertBase64ToImage(String base64Str, String fullFilePath){
if(base64Str == null){
return false;
}
if(base64Str.contains(BASE_64_FLAG)){
int suffixStart = StringUtils.indexOf(base64Str, DATA_IMAGE_FLAG)+ DATA_IMAGE_FLAG.length();
String suffix = StringUtils.substring(base64Str, suffixStart, StringUtils.indexOf(base64Str, ";", suffixStart));
base64Str = base64Str.substring(base64Str.indexOf(BASE_64_FLAG)+ BASE_64_FLAG.length());
}
try{
byte[] data = Base64.getDecoder().decode(base64Str);
File file = new File(fullFilePath);
FileUtils.writeByteArrayToFile(file, data);
data = null;
return true;
}
catch(Exception e){
logger.warn("base", e);
return false;
}
}
/**
* 生成缩略图
* @return
* @throws Exception
*/
public static String generateThumbnail(String sourcePath, String targetPath, int width, int height) throws Exception{
// 创建文件
File file = new File(sourcePath);
if(!file.exists()){
boolean result = file.mkdir();
if(!result){
logger.warn("创建文件夹"+sourcePath+"失败。");
}
}
// 生成缩略图
Thumbnails.of(sourcePath).size(width, height).toFile(targetPath);
return targetPath;
}
/**
* 给图片添加水印
* @param filePath
*/
private static void addWatermark(String filePath, String watermark) throws Exception{
Thumbnails.of(filePath).watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File(watermark)), 0.8f).toFile(filePath);
}
}

View File

@ -1,167 +0,0 @@
package com.diboot.component.file.file.zip;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/***
* 文件压缩操作辅助类
* @author Mazc
*/
public class ZipHelper {
private static final Logger logger = LoggerFactory.getLogger(ZipHelper.class);
/**
* 递归压缩文件
* @param srcRootDir
* @param file
* @param zos
* @return
* @throws Exception
*/
private static void zipFile(String srcRootDir, File file, ZipOutputStream zos, String... matchKeyword) throws Exception {
if (file == null) {
logger.error("[Zip]file对象为空,压缩失败!");
return;
}
//如果是文件则直接压缩该文件
if (file.isFile()) {
int count, bufferLen = 1024;
byte[] data = new byte[bufferLen];
String fileName = file.getName();
//包含在指定文件名范围内的文件进行压缩
if (matchKeyword == null || matchKeyword.length == 0 || fileName.indexOf(matchKeyword[0]) >= 0) {
//获取文件相对于压缩文件夹根目录的子路径
String subPath = file.getAbsolutePath();
subPath = subPath.replace("\\", "/"); //解决文件路径分割符Unix和Windows不兼容的问题
if (subPath.indexOf(srcRootDir) != -1)
{
subPath = subPath.substring(srcRootDir.length() + File.separator.length());
}
ZipEntry entry = new ZipEntry(subPath);
zos.putNextEntry(entry);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
while ((count = bis.read(data, 0, bufferLen)) != -1)
{
zos.write(data, 0, count);
}
logger.info("[Zip]压缩成功:" + file.getName());
bis.close();
zos.closeEntry();
}
}
else {
//压缩目录中的文件或子目录
File[] childFileList = file.listFiles();
String filePath = "";
if(childFileList != null) {
for (int n=0; n<childFileList.length; n++)
{
if (!childFileList[n].isFile()) {
filePath = childFileList[n].getPath();
//不在指定目录名范围内的目录不进行压缩
if (matchKeyword != null && matchKeyword.length > 0 && filePath.indexOf(matchKeyword[0]) == -1) {
continue;
}
}
childFileList[n].getAbsolutePath().indexOf(file.getAbsolutePath());
zipFile(srcRootDir, childFileList[n], zos, matchKeyword);
}
}
}
}
/**
* 对文件或文件目录进行压缩
* @param srcPath 要压缩的源文件路径如果压缩一个文件则为该文件的全路径如果压缩一个目录则为该目录的顶层目录路径
* @param zipPath 压缩文件保存的路径注意zipPath不能是srcPath路径下的子文件夹
* @param zipFileName 压缩文件名
* @param excludeKeyword 需要剔除
* @throws Exception
*/
public static boolean zip(String srcPath, String zipPath, String zipFileName, String... excludeKeyword) throws Exception {
if (StringUtils.isEmpty(srcPath))
{
logger.error("[Zip]源文件路径为空,压缩失败!");
return false;
}
if (StringUtils.isEmpty(zipPath))
{
logger.error("[Zip]压缩文件保存的路径为空,压缩失败!");
return false;
}
if (StringUtils.isEmpty(zipFileName))
{
logger.error("[Zip]压缩文件名为空,压缩失败!");
return false;
}
CheckedOutputStream cos = null;
ZipOutputStream zos = null;
File srcFile = new File(srcPath);
if (srcFile.exists()) {
//判断压缩文件保存的路径是否为源文件路径的子文件夹如果是则终止压缩
if (srcFile.isDirectory() && zipPath.indexOf(srcPath)!=-1)
{
logger.error("[Zip]压缩文件保存的路径是源文件的字文件夹,压缩失败!");
return false;
}
//判断压缩文件保存的路径是否存在如果不存在则创建目录
File zipDir = new File(zipPath);
if (!zipDir.exists() || !zipDir.isDirectory())
{
zipDir.mkdirs();
}
//创建压缩文件保存的文件对象
String zipFilePath = zipPath + "/" + zipFileName;
File zipFile = new File(zipFilePath);
if (zipFile.exists())
{
//删除已存在的目标文件
zipFile.delete();
}
cos = new CheckedOutputStream(new FileOutputStream(zipFile), new CRC32());
zos = new ZipOutputStream(cos);
//如果只是压缩一个文件则需要截取该文件的父目录
String srcRootDir = srcPath;
if (srcFile.isFile())
{
int index = srcPath.lastIndexOf("/");
if (index != -1)
{
srcRootDir = srcPath.substring(0, index);
}
}
//调用递归压缩方法进行目录或文件压缩
zipFile(srcRootDir, srcFile, zos, excludeKeyword);
zos.flush();
if (zos != null)
{
zos.close();
}
return true;
} else {
logger.error("[Zip]当前源目录不存在,压缩失败!" + srcPath);
return false;
}
}
}

View File

@ -1,22 +0,0 @@
package com.diboot.component.file.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.diboot.component.file.entity.BaseFile;
import com.diboot.core.mapper.BaseCrudMapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* 文件相关Mapper
* @author Mazc
* @version 2017/4/18
*/
public interface BaseFileMapper extends BaseCrudMapper<BaseFile> {
@Select("SELECT * FROM file ${ew.customSqlSegment}")
BaseFile getModel(@Param(Constants.WRAPPER) Wrapper<BaseFile> wrapper);
int updateModel(@Param("m") BaseFile model);
}

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"./mybatis-3-mapper.dtd">
<mapper namespace="com.diboot.component.file.mapper.BaseFileMapper">
<sql id="setValues">
<set>
<if test="m.relObjType != null">rel_obj_type = #{m.relObjType},</if>
<if test="m.relObjId != null">rel_obj_id = #{m.relObjId},</if>
<if test="m.name != null">name = #{m.name},</if>
<if test="m.link != null">link = #{m.link},</if>
<if test="m.path != null">path = #{m.path},</if>
<if test="m.fileType != null">file_type = #{m.fileType},</if>
<if test="m.dataCount != null">data_count = #{m.dataCount},</if>
<if test="m.size != null">size = #{m.size},</if>
<if test="m.status != null">status = #{m.status},</if>
<if test="m.comment != null">comment = #{m.comment},</if>
<if test="m.deleted != null">is_deleted = #{m.deleted},</if>
</set>
</sql>
<update id="updateModel" parameterType="String">
UPDATE file <include refid="setValues"/>
WHERE uuid = #{m.uuid, jdbcType=VARCHAR}
</update>
</mapper>

View File

@ -1,291 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Copyright 2009-2013 the original author or authors.
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 joinOn 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.
-->
<!ELEMENT mapper (cache-ref | cache | resultMap* | parameterMap* | sql* | insert* | update* | delete* | select* )+>
<!ATTLIST mapper
xmlns:fo CDATA #IMPLIED
namespace CDATA #IMPLIED
>
<!ELEMENT cache-ref EMPTY>
<!ATTLIST cache-ref
namespace CDATA #REQUIRED
>
<!ELEMENT cache (property*)>
<!ATTLIST cache
type CDATA #IMPLIED
eviction CDATA #IMPLIED
flushInterval CDATA #IMPLIED
size CDATA #IMPLIED
readOnly CDATA #IMPLIED
blocking CDATA #IMPLIED
>
<!ELEMENT parameterMap (parameter+)?>
<!ATTLIST parameterMap
id CDATA #REQUIRED
type CDATA #REQUIRED
>
<!ELEMENT parameter EMPTY>
<!ATTLIST parameter
property CDATA #REQUIRED
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
mode (IN | OUT | INOUT) #IMPLIED
resultMap CDATA #IMPLIED
scale CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT resultMap (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST resultMap
id CDATA #REQUIRED
type CDATA #REQUIRED
extends CDATA #IMPLIED
autoMapping (true|false) #IMPLIED
>
<!ELEMENT constructor (idArg*,arg*)>
<!ELEMENT id EMPTY>
<!ATTLIST id
property CDATA #IMPLIED
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT result EMPTY>
<!ATTLIST result
property CDATA #IMPLIED
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT idArg EMPTY>
<!ATTLIST idArg
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
>
<!ELEMENT arg EMPTY>
<!ATTLIST arg
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
>
<!ELEMENT collection (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST collection
property CDATA #REQUIRED
column CDATA #IMPLIED
javaType CDATA #IMPLIED
ofType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
typeHandler CDATA #IMPLIED
notNullColumn CDATA #IMPLIED
columnPrefix CDATA #IMPLIED
resultSet CDATA #IMPLIED
foreignColumn CDATA #IMPLIED
autoMapping (true|false) #IMPLIED
fetchType (lazy|eager) #IMPLIED
>
<!ELEMENT association (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST association
property CDATA #REQUIRED
column CDATA #IMPLIED
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
typeHandler CDATA #IMPLIED
notNullColumn CDATA #IMPLIED
columnPrefix CDATA #IMPLIED
resultSet CDATA #IMPLIED
foreignColumn CDATA #IMPLIED
autoMapping (true|false) #IMPLIED
fetchType (lazy|eager) #IMPLIED
>
<!ELEMENT discriminator (case+)>
<!ATTLIST discriminator
column CDATA #IMPLIED
javaType CDATA #REQUIRED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT case (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST case
value CDATA #REQUIRED
resultMap CDATA #IMPLIED
resultType CDATA #IMPLIED
>
<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>
<!ELEMENT typeAlias EMPTY>
<!ATTLIST typeAlias
alias CDATA #REQUIRED
type CDATA #REQUIRED
>
<!ELEMENT select (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST select
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
resultMap CDATA #IMPLIED
resultType CDATA #IMPLIED
resultSetType (FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
fetchSize CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
useCache (true|false) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
resultOrdered (true|false) #IMPLIED
resultSets CDATA #IMPLIED
>
<!ELEMENT insert (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST insert
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
useGeneratedKeys (true|false) #IMPLIED
keyColumn CDATA #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>
<!ELEMENT selectKey (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST selectKey
resultType CDATA #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
keyColumn CDATA #IMPLIED
order (BEFORE|AFTER) #IMPLIED
databaseId CDATA #IMPLIED
>
<!ELEMENT update (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST update
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
useGeneratedKeys (true|false) #IMPLIED
keyColumn CDATA #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>
<!ELEMENT delete (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST delete
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>
<!-- Dynamic -->
<!ELEMENT include (property+)?>
<!ATTLIST include
refid CDATA #REQUIRED
>
<!ELEMENT bind EMPTY>
<!ATTLIST bind
name CDATA #REQUIRED
value CDATA #REQUIRED
>
<!ELEMENT sql (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST sql
id CDATA #REQUIRED
lang CDATA #IMPLIED
databaseId CDATA #IMPLIED
>
<!ELEMENT trim (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST trim
prefix CDATA #IMPLIED
prefixOverrides CDATA #IMPLIED
suffix CDATA #IMPLIED
suffixOverrides CDATA #IMPLIED
>
<!ELEMENT where (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ELEMENT set (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ELEMENT foreach (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST foreach
collection CDATA #REQUIRED
item CDATA #IMPLIED
index CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
separator CDATA #IMPLIED
>
<!ELEMENT choose (when* , otherwise?)>
<!ELEMENT when (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST when
test CDATA #REQUIRED
>
<!ELEMENT otherwise (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ELEMENT if (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST if
test CDATA #REQUIRED
>

View File

@ -1,26 +0,0 @@
package com.diboot.component.file.service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.diboot.component.file.entity.BaseFile;
import com.diboot.core.service.BaseService;
import org.springframework.stereotype.Component;
/**
* @author Lishuaifei
* @description 基础文件Service
* @creatime 2019-07-18 15:20
*/
@Component
public interface BaseFileService extends BaseService<BaseFile> {
/*
* 自定义获取数据方法解决查询不出is_deleted=1的数据的问题
* */
BaseFile getModel(Wrapper<BaseFile> wrapper);
/*
* 自定义更新数据方法解决无法更新is_deleted=1的数据的问题
* */
boolean updateModel(BaseFile model);
}

View File

@ -1,31 +0,0 @@
package com.diboot.component.file.service.impl;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.diboot.component.file.entity.BaseFile;
import com.diboot.component.file.mapper.BaseFileMapper;
import com.diboot.component.file.service.BaseFileService;
import com.diboot.core.service.impl.BaseServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author Lishuaifei
* @description 文件下载实现类
* @creatime 2019-07-18 15:29
*/
@Service
public class BaseFileServiceImpl extends BaseServiceImpl<BaseFileMapper, BaseFile> implements BaseFileService {
@Autowired
private BaseFileMapper baseFileMapper;
@Override
public BaseFile getModel(Wrapper<BaseFile> wrapper) {
return baseFileMapper.getModel(wrapper);
}
@Override
public boolean updateModel(BaseFile model) {
return baseFileMapper.updateModel(model)>0? true:false;
}
}

View File

@ -1,47 +0,0 @@
package com.diboot.component.file.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/***
* Java执行命令行辅助类
* @author Mazc
*/
public class RuntimeHelper {
private static final Logger logger = LoggerFactory.getLogger(RuntimeHelper.class);
public static boolean run(String command){
Runtime runtime = null;
try{
runtime = Runtime.getRuntime();
Process process = runtime.exec(command); // -ar 8000 -ac 1 -y -ab 12.4k
// 转换成功
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line;
while((line=reader.readLine())!=null){
logger.debug(line);
}
int result = process.waitFor();
if(result == 0){
process.destroy();
return true;
}
else{
process.destroy();
throw new RuntimeException("运行命令失败: "+command);
}
}
catch(Exception e){
logger.error("运行命令失败: ", e);
return false;
}
finally{
if(runtime!=null){
runtime.freeMemory();
}
}
}
}

View File

@ -1,10 +0,0 @@
dependencies {
compile project(":diboot-component-msg")
// java mail
compile("javax.mail:mail:1.4.7",
"org.apache.commons:commons-email:1.5")
testCompile group: 'junit', name: 'junit', version: '4.12'
}

View File

@ -1,7 +0,0 @@
package com.diboot.component.msg.email.service;
import com.diboot.component.msg.service.BaseSendService;
public interface EmailSendService extends BaseSendService {
}

View File

@ -1,26 +0,0 @@
package com.diboot.component.msg.email.service;
import com.diboot.component.msg.email.utils.EmailUtil;
import com.diboot.component.msg.entity.Message;
import com.diboot.component.msg.service.impl.BaseSendServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class EmailSendServiceImpl extends BaseSendServiceImpl implements EmailSendService {
private static final Logger logger = LoggerFactory.getLogger(EmailSendServiceImpl.class);
@Override
public boolean sendMsg(Message message) throws Exception {
String to = message.getReceiver();
String toName = String.valueOf(message.getFromExt("toName"));
String title = message.getTitle();
String content = message.getContent();
String[] ccEmails = (String[])message.getFromExt("ccEmails");
String[] filePaths = (String[])message.getFromExt("filePaths");
return EmailUtil.send(to, toName, title, content, ccEmails, filePaths);
}
}

View File

@ -1,73 +0,0 @@
package com.diboot.component.msg.email.utils;
import com.diboot.core.config.Cons;
import com.diboot.core.util.PropertiesUtils;
import com.diboot.core.util.V;
import org.apache.commons.mail.EmailAttachment;
import org.apache.commons.mail.HtmlEmail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
public class EmailUtil {
private static final Logger logger = LoggerFactory.getLogger(EmailUtil.class);
private static final String EMAIL_NAME = PropertiesUtils.get("email.name");// 发送方称呼
private static final String EMAIL_ADDRESS = PropertiesUtils.get("email.address");// 发送方email地址
private static final String EMAIL_PASSWORD = PropertiesUtils.get("email.password");// 发送方授权码
private static final String EMAIL_HOST = PropertiesUtils.get("email.host");//发送方smptHost
private static final String EMAIL_SSLPORT = PropertiesUtils.get("email.sslport");// 发送端口
/*
* 邮件发送
* */
public static boolean send(String to, String toName, String title, String content, String[] ccEmails, String... filePaths) throws Exception{
if(logger.isDebugEnabled()){
logger.debug(">>>发送邮件开始, 收件人:" + to);
}
// Create the email message
HtmlEmail email = new HtmlEmail();
email.setHostName(EMAIL_HOST);
email.setCharset(Cons.CHARSET_UTF8);
email.setSSLOnConnect(true);
if(V.notEmpty(EMAIL_SSLPORT)){
email.setSslSmtpPort(EMAIL_SSLPORT);
}
email.setAuthentication(EMAIL_ADDRESS, EMAIL_PASSWORD);
email.addTo(to, toName);
if(V.notEmpty(ccEmails)){
for(String cc : ccEmails){
email.addCc(cc);
}
}
email.setFrom(EMAIL_ADDRESS, EMAIL_NAME);
email.setSubject(title);
// set the html message
email.setHtmlMsg(content);
// 发送附件
if(V.notEmpty(filePaths)){
for(String path : filePaths){
File file = new File(path);
if(!file.exists()){
logger.warn("附件文件不存在无法发送path="+path);
return false;
}
EmailAttachment attachment = new EmailAttachment();
attachment.setName(file.getName());
attachment.setPath(path);
attachment.setDisposition(EmailAttachment.ATTACHMENT);
email.attach(attachment);
}
}
// send the email
email.send();
if(logger.isDebugEnabled()){
logger.debug("<<<发送邮件结束");
}
return true;
}
}

View File

@ -1,6 +0,0 @@
dependencies {
compile project(":diboot-core")
testCompile group: 'junit', name: 'junit', version: '4.12'
}

View File

@ -1,13 +0,0 @@
package com.diboot.component.msg.config;
public class MsgCons {
/*
* 消息类型
* */
public enum MSG_TYPE{
EMAIL, //邮件消息
SMS, //短信消息
WECHAT //微信消息
}
}

View File

@ -1,91 +0,0 @@
package com.diboot.component.msg.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.diboot.component.msg.entity.Message;
import com.diboot.component.msg.service.MessageService;
import com.diboot.component.msg.vo.MessageVO;
import com.diboot.core.binding.RelationsBinder;
import com.diboot.core.controller.BaseCrudRestController;
import com.diboot.core.service.BaseService;
import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.Pagination;
import com.diboot.core.vo.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@RestController
@RequestMapping("/message")
public class MessageController extends BaseCrudRestController {
@Autowired
private MessageService messageService;
@Override
protected BaseService getService() {
return messageService;
}
@GetMapping("/list")
public JsonResult list(Message message, Pagination pagination, HttpServletRequest request) throws Exception {
//构建查询条件
QueryWrapper<Message> queryWrapper = super.buildQueryWrapper(message);
// 查询当前页的Entity主表数据
List<Message> entityList = getService().getEntityList(queryWrapper, pagination);
// 自动转换VO中注解绑定的关联
List<MessageVO> voList = RelationsBinder.convertAndBind(entityList, MessageVO.class);
//返回结果
return new JsonResult(Status.OK, voList).bindPagination(pagination);
}
/***
* 查询Entity
* @param id
* @return
* @throws Exception
*/
@GetMapping("/{id}")
public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request)
throws Exception{
MessageVO entityVO = messageService.getViewObject(id, MessageVO.class);
return new JsonResult(entityVO);
}
/***
* 创建Entity
* @return
* @throws Exception
*/
@PostMapping("/")
public JsonResult createEntity(@ModelAttribute Message entity, BindingResult result, HttpServletRequest request)
throws Exception{
return super.createEntity(entity, result);
}
/***
* 更新Entity
* @param id
* @return
* @throws Exception
*/
@PutMapping("/{id}")
public JsonResult updateModel(@PathVariable("id")Long id, @ModelAttribute Message entity, BindingResult result,
HttpServletRequest request) throws Exception{
return super.updateEntity(entity, result);
}
/***
* 删除
* @param id
* @return
* @throws Exception
*/
@DeleteMapping("/{id}")
public JsonResult deleteModel(@PathVariable("id")Long id, HttpServletRequest request) throws Exception{
return super.deleteEntity(id);
}
}

View File

@ -1,124 +0,0 @@
package com.diboot.component.msg.controller;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.diboot.component.msg.entity.MessageTemplate;
import com.diboot.component.msg.service.MessageTemplateService;
import com.diboot.core.controller.BaseCrudRestController;
import com.diboot.core.service.BaseService;
import com.diboot.core.service.DictionaryService;
import com.diboot.core.util.V;
import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.KeyValue;
import com.diboot.core.vo.Pagination;
import com.diboot.core.vo.Status;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/*
* 消息模板相关service
* @author:wangyl
* */
@RestController
@RequestMapping("/messageTemplate")
public class MessageTemplateController extends BaseCrudRestController {
@Autowired
private MessageTemplateService messageTemplateService;
@Autowired
private DictionaryService dictionaryService;
@Override
protected BaseService getService() {
return messageTemplateService;
}
@GetMapping("/list")
public JsonResult list(MessageTemplate messageTemplate, Pagination pagination, HttpServletRequest request) throws Exception {
//构建查询条件
QueryWrapper<MessageTemplate> queryWrapper = super.buildQueryWrapper(messageTemplate);
// 查询当前页的Entity主表数据
List<MessageTemplate> entityList = getService().getEntityList(queryWrapper, pagination);
//返回结果
return new JsonResult(Status.OK, entityList).bindPagination(pagination);
}
/***
* 查询Entity
* @param id
* @return
* @throws Exception
*/
@GetMapping("/{id}")
public JsonResult getModel(@PathVariable("id")Long id, HttpServletRequest request)
throws Exception{
MessageTemplate entity = messageTemplateService.getEntity(id);
return new JsonResult(entity);
}
/***
* 创建Entity
* @return
* @throws Exception
*/
@PostMapping("/")
public JsonResult createEntity(@ModelAttribute MessageTemplate entity, BindingResult result, HttpServletRequest request)
throws Exception{
return super.createEntity(entity, result);
}
/***
* 更新Entity
* @param id
* @return
* @throws Exception
*/
@PutMapping("/{id}")
public JsonResult updateModel(@PathVariable("id")Long id, @ModelAttribute MessageTemplate entity, BindingResult result,
HttpServletRequest request) throws Exception{
return super.updateEntity(entity, result);
}
/***
* 删除
* @param id
* @return
* @throws Exception
*/
@DeleteMapping("/{id}")
public JsonResult deleteModel(@PathVariable("id")Long id, HttpServletRequest request) throws Exception{
return super.deleteEntity(id);
}
/***
* 加载更多数据
* @param request
* @param modelMap
* @return
*/
@GetMapping("/attachMore")
public JsonResult attachMore(HttpServletRequest request, ModelMap modelMap){
Wrapper wrapper = null;
//消息模板元数据
List<KeyValue> msgTempCodeKvList = dictionaryService.getKeyValueList(MessageTemplate.METADATA_TYPE.MSG_TEMP_CODE.name());
modelMap.put("msgTempCodeKvList", msgTempCodeKvList);
return new JsonResult(modelMap);
}
@GetMapping("/getTemplateVaribles/{code}")
public JsonResult getTemplateVaribles(@PathVariable("code")String code, HttpServletRequest request) throws Exception{
String[] varibles = messageTemplateService.getTemplateVaribles(code);
if(V.isEmpty(varibles)){
return new JsonResult(Status.FAIL_OPERATION, "获取变量列表为空");
}
return new JsonResult(Status.OK, varibles,"获取变量列表成功");
}
}

View File

@ -1,51 +0,0 @@
package com.diboot.component.msg.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.diboot.core.entity.BaseExtEntity;
import lombok.Data;
import java.util.Date;
@Data
public class Message extends BaseExtEntity {
@TableField
private String type;
@TableField
private Long templateId;
@TableField
private String businessType;
@TableField
private Long businessId;
@TableField
private String sender;
@TableField
private String receiver;
@TableField
private String title;
@TableField
private String content;
@TableField
private String url;
@TableField
private String status;
@TableField
private Date scheduleTime;
@TableField
private String response;
@TableField
private Long createBy;
}

View File

@ -1,39 +0,0 @@
package com.diboot.component.msg.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.diboot.core.entity.BaseExtEntity;
import lombok.Data;
@Data
public class MessageTemplate extends BaseExtEntity {
public static enum METADATA_TYPE{
MSG_TEMP_CODE, // 消息模板
MSG_TEMP_VARIBLES // 消息模板变量
}
@TableField
private String type;
@TableField
private String code;
@TableField
private String msgType;
@TableField
private String businessType;
@TableField
private Long businessId;
@TableField
private String title;
@TableField
private String msgTitle;
@TableField
private String content;
}

View File

@ -1,7 +0,0 @@
package com.diboot.component.msg.mapper;
import com.diboot.component.msg.entity.Message;
import com.diboot.core.mapper.BaseCrudMapper;
public interface MessageMapper extends BaseCrudMapper<Message> {
}

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "./mybatis-3-mapper.dtd">
<mapper namespace="com.diboot.component.msg.mapper.MessageMapper">
</mapper>

View File

@ -1,7 +0,0 @@
package com.diboot.component.msg.mapper;
import com.diboot.component.msg.entity.MessageTemplate;
import com.diboot.core.mapper.BaseCrudMapper;
public interface MessageTemplateMapper extends BaseCrudMapper<MessageTemplate> {
}

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "./mybatis-3-mapper.dtd">
<mapper namespace="com.diboot.component.msg.mapper.MessageTemplateMapper">
</mapper>

View File

@ -1,25 +0,0 @@
package com.diboot.component.msg.service;
import com.diboot.component.msg.entity.Message;
/*
* 消息发送基础service
* @author:wangyl
* */
public interface BaseSendService {
/***
* 保存并发送消息
* @param msg
* @return
*/
boolean createAndSendMsg(Message msg) throws Exception;
/***
* 发送消息
* @param msg
* @return
*/
boolean sendMsg(Message msg) throws Exception;
}

View File

@ -1,12 +0,0 @@
package com.diboot.component.msg.service;
import com.diboot.component.msg.entity.Message;
import com.diboot.core.service.BaseService;
/*
* 消息相关service
* @author:wangyl
* */
public interface MessageService extends BaseService<Message> {
}

View File

@ -1,26 +0,0 @@
package com.diboot.component.msg.service;
import com.diboot.component.msg.entity.MessageTemplate;
import com.diboot.core.service.BaseService;
/*
* 消息模板相关service
* @author:wangyl
* */
public interface MessageTemplateService extends BaseService<MessageTemplate> {
/**
* 根据code获取唯一的消息模板
* @param code
* @return
*/
MessageTemplate getMessageTemplate(String code);
/**
* 根据code获取消息模板变量
* @param code
* @return
*/
String[] getTemplateVaribles(String code);
}

View File

@ -1,27 +0,0 @@
package com.diboot.component.msg.service.impl;
import com.diboot.component.msg.entity.Message;
import com.diboot.component.msg.mapper.MessageMapper;
import com.diboot.component.msg.service.BaseSendService;
import com.diboot.core.service.impl.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/*
* 消息发送基础service
* @author:wangyl
* */
@Service
@Slf4j
public abstract class BaseSendServiceImpl extends BaseServiceImpl<MessageMapper, Message> implements BaseSendService {
@Override
public boolean createAndSendMsg(Message msg) throws Exception {
boolean success = super.createEntity(msg);
if(success){
success = sendMsg(msg);
}
return success;
}
}

View File

@ -1,18 +0,0 @@
package com.diboot.component.msg.service.impl;
import com.diboot.component.msg.entity.Message;
import com.diboot.component.msg.mapper.MessageMapper;
import com.diboot.component.msg.service.MessageService;
import com.diboot.core.service.impl.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/*
* 消息相关service实现
* @author:wangyl
* */
@Service
@Slf4j
public class MessageServiceImpl extends BaseServiceImpl<MessageMapper, Message> implements MessageService {
}

View File

@ -1,52 +0,0 @@
package com.diboot.component.msg.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.diboot.component.msg.entity.MessageTemplate;
import com.diboot.component.msg.mapper.MessageTemplateMapper;
import com.diboot.component.msg.service.MessageTemplateService;
import com.diboot.core.entity.Dictionary;
import com.diboot.core.service.DictionaryService;
import com.diboot.core.service.impl.BaseServiceImpl;
import com.diboot.core.util.S;
import com.diboot.core.util.V;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/*
* 消息模板相关service
* @author:wangyl
* */
@Service
@Slf4j
public class MessageTemplateServiceImpl extends BaseServiceImpl<MessageTemplateMapper, MessageTemplate> implements MessageTemplateService {
private static final Logger logger = LoggerFactory.getLogger(MessageTemplateServiceImpl.class);
@Autowired
private DictionaryService dictionaryService;
@Override
public MessageTemplate getMessageTemplate(String code) {
LambdaQueryWrapper<MessageTemplate> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(MessageTemplate::getCode, code);
List<MessageTemplate> list = this.getEntityList(wrapper);
return V.notEmpty(list)? list.get(0) : null;
}
@Override
public String[] getTemplateVaribles(String code) {
LambdaQueryWrapper<Dictionary> wrapper = new LambdaQueryWrapper<>();
wrapper.ne(Dictionary::getParentId, 0)
.eq(Dictionary::getItemName, code)
.eq(Dictionary::getType, MessageTemplate.METADATA_TYPE.MSG_TEMP_VARIBLES.name());
List<Dictionary> list = dictionaryService.getEntityList(wrapper);
if (V.isEmpty(list)){
return null;
}
return S.split(list.get(0).getItemValue());
}
}

View File

@ -1,21 +0,0 @@
package com.diboot.component.msg.vo;
import com.diboot.component.msg.entity.Message;
import com.diboot.component.msg.entity.MessageTemplate;
import com.diboot.core.binding.annotation.BindDict;
import com.diboot.core.binding.annotation.BindEntity;
import lombok.Data;
@Data
public class MessageVO extends Message {
private static final long serialVersionUID = 141470955506938430L;
@BindDict(type="MSG_TYPE", field="type")
private String typeLabel;
@BindDict(type="MSG_STATUS", field="status")
private String statusLabel;
@BindEntity(entity=MessageTemplate.class, condition="this.template_id=id")
private MessageTemplate messageTemplate;
}

View File

@ -48,14 +48,14 @@ private List<Role> roleList;
### 1. 引入依赖
Gradle:
~~~gradle
compile("com.diboot:diboot-core-spring-boot-starter:2.0.3-RC2")
compile("com.diboot:diboot-core-spring-boot-starter:2.0.3")
~~~
或Maven
~~~xml
<dependency>
<groupId>com.diboot</groupId>
<artifactId>diboot-core-spring-boot-starter</artifactId>
<version>2.0.3-RC2</version>
<version>2.0.3</version>
</dependency>
~~~
> * 使用diboot-devtools会自动引入diboot-core无需配置此依赖。
@ -99,9 +99,9 @@ public class UserDTO{
* 将映射为 queryWrapper.eq("gender", "M").like("realname", "张")
*/
@GetMapping("/list")
public JsonResult getVOList(UserDto userDto) throws Exception{
//调用super.buildQueryWrapper(entityOrDto) 或者直接调用 QueryBuilder.toQueryWrapper(entityOrDto) 进行转换
QueryWrapper<User> queryWrapper = super.buildQueryWrapper(userDto);
public JsonResult getVOList(UserDto userDto, HttpServletRequest request) throws Exception{
//调用super.buildQueryWrapper(entityOrDto, request) 或者直接调用 QueryBuilder.toQueryWrapper(entityOrDto) 进行转换
QueryWrapper<User> queryWrapper = super.buildQueryWrapper(userDto, request);
//... 查询list
return new JsonResult(Status.OK, list);
}

View File

@ -148,7 +148,7 @@ public class RelationsBinder {
EntityBinder binder = buildEntityBinder(annotation, voList);
if(binder != null){
// 构建binder
binder.set(fieldAnnotation.getFieldName());
binder.set(fieldAnnotation.getFieldName(), fieldAnnotation.getFieldClass());
// 解析条件并且执行绑定
parseConditionsAndBinding(binder, annotation.condition());
}
@ -165,7 +165,7 @@ public class RelationsBinder {
// 构建binder
EntityListBinder binder = buildEntityListBinder(bindAnnotation, voList);
if(binder != null){
binder.set(fieldAnnotation.getFieldName());
binder.set(fieldAnnotation.getFieldName(), fieldAnnotation.getFieldClass());
// 解析条件并且执行绑定
parseConditionsAndBinding(binder, bindAnnotation.condition());
}

View File

@ -27,6 +27,10 @@ public class EntityBinder<T> extends BaseBinder<T> {
* 给待绑定list中VO对象赋值的setter属性名
*/
protected String annoObjectField;
/***
* 给待绑定list中VO对象赋值的setter属性class类型
*/
protected Class<?> annoObjectFieldClass;
public EntityBinder(){}
/***
@ -47,8 +51,8 @@ public class EntityBinder<T> extends BaseBinder<T> {
* @param <R> set方法参数类型
* @return
*/
public <T1,R> BaseBinder<T> set(ISetter<T1, R> voSetter){
return set(BeanUtils.convertToFieldName(voSetter));
public <T1,R> BaseBinder<T> set(ISetter<T1, R> voSetter, Class annoObjectFieldClass){
return set(BeanUtils.convertToFieldName(voSetter), annoObjectFieldClass);
}
/***
@ -56,8 +60,9 @@ public class EntityBinder<T> extends BaseBinder<T> {
* @param annoObjectField VO中调用赋值的setter属性
* @return
*/
public BaseBinder<T> set(String annoObjectField){
public BaseBinder<T> set(String annoObjectField, Class annoObjectFieldClass){
this.annoObjectField = annoObjectField;
this.annoObjectFieldClass = annoObjectFieldClass;
return this;
}
@ -76,7 +81,7 @@ public class EntityBinder<T> extends BaseBinder<T> {
return;
}
// 结果转换Map
Map<String, T> valueEntityMap = new HashMap<>();
Map<String, Object> valueEntityMap = new HashMap<>();
// 通过中间表关联Entity
// @BindEntity(entity = Organization.class, condition = "this.department_id=department.id AND department.org_id=id AND department.is_deleted=0")
// Organization organization;
@ -98,8 +103,8 @@ public class EntityBinder<T> extends BaseBinder<T> {
continue;
}
String key = entry.getKey();
T value = listMap.get(String.valueOf(fetchValueId));
valueEntityMap.put(key, BeanUtils.cloneBean(value));
T entity = listMap.get(String.valueOf(fetchValueId));
valueEntityMap.put(key, cloneOrConvertBean(entity));
}
}
}
@ -116,11 +121,24 @@ public class EntityBinder<T> extends BaseBinder<T> {
String refEntityPKFieldName = S.toLowerCaseCamel(referencedEntityPrimaryKey);
for(T entity : list){
String pkValue = BeanUtils.getStringProperty(entity, refEntityPKFieldName);
valueEntityMap.put(pkValue, BeanUtils.cloneBean(entity));
valueEntityMap.put(pkValue, cloneOrConvertBean(entity));
}
}
}
// 绑定结果
BeanUtils.bindPropValueOfList(annoObjectField, annoObjectList, annoObjectForeignKey, valueEntityMap);
}
/**
* 克隆Entity/VO对象如果与Entity类型不一致如VO则先转型
* @param value
*/
protected Object cloneOrConvertBean(T value){
if(value.getClass().getName().equals(annoObjectFieldClass.getName())){
return BeanUtils.cloneBean(value);
}
else{
return BeanUtils.convert(value, annoObjectFieldClass);
}
}
}

View File

@ -47,7 +47,7 @@ public class EntityListBinder<T> extends EntityBinder<T> {
if(V.isEmpty(annoObjectForeignKeyList)){
return;
}
Map<String, List<T>> valueEntityListMap = new HashMap<>();
Map<String, List> valueEntityListMap = new HashMap<>();
// 解析中间表查询 1-N关联
//User.class @BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id")
if(middleTable != null){
@ -67,11 +67,11 @@ public class EntityListBinder<T> extends EntityBinder<T> {
if(V.isEmpty(annoObjFKList)){
continue;
}
List<T> valueList = new ArrayList();
List valueList = new ArrayList();
for(Object obj : annoObjFKList){
T ent = entityMap.get(String.valueOf(obj));
if(ent != null){
valueList.add(BeanUtils.cloneBean(ent));
valueList.add(cloneOrConvertBean(ent));
}
}
valueEntityListMap.put(entry.getKey(), valueList);
@ -86,12 +86,12 @@ public class EntityListBinder<T> extends EntityBinder<T> {
if(V.notEmpty(list)){
for(T entity : list){
String keyValue = BeanUtils.getStringProperty(entity, S.toLowerCaseCamel(referencedEntityPrimaryKey));
List<T> entityList = valueEntityListMap.get(keyValue);
List entityList = valueEntityListMap.get(keyValue);
if(entityList == null){
entityList = new ArrayList<>();
valueEntityListMap.put(keyValue, entityList);
}
entityList.add(BeanUtils.cloneBean(entity));
entityList.add(cloneOrConvertBean(entity));
}
}
}

View File

@ -38,30 +38,30 @@ public class BindAnnotationGroup {
* @param fieldName
* @param annotation
*/
public void addBindAnnotation(String fieldName, Annotation annotation){
public void addBindAnnotation(String fieldName, Class<?> fieldClass, Annotation annotation){
if(annotation instanceof BindDict){
if(bindDictAnnotations == null){
bindDictAnnotations = new ArrayList<>();
}
bindDictAnnotations.add(new FieldAnnotation(fieldName, annotation));
bindDictAnnotations.add(new FieldAnnotation(fieldName, fieldClass, annotation));
}
else if(annotation instanceof BindField){
if(bindFieldAnnotations == null){
bindFieldAnnotations = new ArrayList<>();
}
bindFieldAnnotations.add(new FieldAnnotation(fieldName, annotation));
bindFieldAnnotations.add(new FieldAnnotation(fieldName, fieldClass, annotation));
}
else if(annotation instanceof BindEntity){
if(bindEntityAnnotations == null){
bindEntityAnnotations = new ArrayList<>();
}
bindEntityAnnotations.add(new FieldAnnotation(fieldName, annotation));
bindEntityAnnotations.add(new FieldAnnotation(fieldName, fieldClass, annotation));
}
else if(annotation instanceof BindEntityList){
if(bindEntityListAnnotations == null){
bindEntityListAnnotations = new ArrayList<>();
}
bindEntityListAnnotations.add(new FieldAnnotation(fieldName, annotation));
bindEntityListAnnotations.add(new FieldAnnotation(fieldName, fieldClass, annotation));
}
}

View File

@ -1,9 +1,13 @@
package com.diboot.core.binding.parser;
import com.diboot.core.util.BeanUtils;
import com.diboot.core.util.V;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -30,7 +34,7 @@ public class BindAnnotationGroupCache {
// 获取注解并缓存
group = new BindAnnotationGroup();
// 获取当前VO的注解
Field[] fields = voClass.getDeclaredFields();
List<Field> fields = BeanUtils.extractAllFields(voClass);
if(fields != null){
for (Field field : fields) {
//遍历属性
@ -39,7 +43,16 @@ public class BindAnnotationGroupCache {
continue;
}
for (Annotation annotation : annotations) {
group.addBindAnnotation(field.getName(), annotation);
Class<?> setterObjClazz = field.getType();
if(setterObjClazz.equals(java.util.List.class) || setterObjClazz.equals(java.util.Collections.class)){
// 如果是集合获取其泛型参数class
Type genericType = field.getGenericType();
if(genericType instanceof ParameterizedType){
ParameterizedType pt = (ParameterizedType) genericType;
setterObjClazz = (Class<?>)pt.getActualTypeArguments()[0];
}
}
group.addBindAnnotation(field.getName(), setterObjClazz, annotation);
}
}
}

View File

@ -14,13 +14,18 @@ public class FieldAnnotation{
* 字段名
*/
private String fieldName;
/**
* 字段类型
*/
private Class<?> fieldClass;
/**
* 注解
*/
private Annotation annotation;
public FieldAnnotation(String fieldName, Annotation annotation){
public FieldAnnotation(String fieldName, Class fieldClass, Annotation annotation){
this.fieldName = fieldName;
this.fieldClass = fieldClass;
this.annotation = annotation;
}
@ -31,4 +36,8 @@ public class FieldAnnotation{
public Annotation getAnnotation() {
return annotation;
}
public Class getFieldClass(){
return fieldClass;
}
}

View File

@ -12,11 +12,17 @@ public class Cons {
*/
public static final String CHARSET_UTF8 = "UTF-8";
/**
* 分隔符 ,
* 逗号分隔符 ,
*/
public static final String SEPARATOR_COMMA = ",";
/**
* 下划线分隔符_
*/
public static final String SEPARATOR_UNDERSCORE = "_";
/**
* 排序 - 降序标记
*/
public static final String ORDER_DESC = "DESC";
/***
* 默认字段名定义
*/
@ -36,7 +42,11 @@ public class Cons {
/**
* 逻辑删除标记字段
*/
deleted
deleted,
/**
* 创建时间字段
*/
createTime
}
}

View File

@ -9,8 +9,6 @@ import com.diboot.core.util.S;
import com.diboot.core.util.V;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
@ -24,33 +22,11 @@ import java.util.*;
public class BaseController {
private static final Logger log = LoggerFactory.getLogger(BaseController.class);
/***
* 字段
*/
protected static final String PARAM_FIELDS = "_fields";
/**
* ID参数名
*/
protected static final String PARAM_ID = Cons.FieldName.id.name();
/**
* 解析所有的验证错误信息转换为JSON
* @param result
* @return
*/
protected String getBindingError(BindingResult result){
if(result == null || !result.hasErrors()){
return null;
}
List<ObjectError> errors = result.getAllErrors();
List<String> allErrors = new ArrayList<>(errors.size());
for(ObjectError error : errors){
allErrors.add(error.getDefaultMessage().replaceAll("\"", "'"));
}
return S.join(allErrors);
}
/***
* 构建查询QueryWrapper (根据BindQuery注解构建相应的查询条件)
* @param entityOrDto Entity对象或者DTO对象 (属性若无BindQuery注解默认构建为为EQ相等条件)

View File

@ -4,15 +4,13 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.diboot.core.entity.BaseEntity;
import com.diboot.core.service.BaseService;
import com.diboot.core.util.BeanUtils;
import com.diboot.core.util.ContextHelper;
import com.diboot.core.util.V;
import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.Pagination;
import com.diboot.core.vo.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ResolvableType;
import org.springframework.validation.BindingResult;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
@ -97,15 +95,10 @@ public class BaseCrudRestController<E extends BaseEntity, VO extends Serializabl
/***
* 创建资源对象用于字类重写的方法
* @param entity
* @param result
* @return JsonResult
* @throws Exception
*/
protected JsonResult createEntity(E entity, BindingResult result, HttpServletRequest request) throws Exception {
// Model属性值验证结果
if (result != null && result.hasErrors()) {
return new JsonResult(Status.FAIL_VALIDATION, super.getBindingError(result));
}
protected JsonResult createEntity(E entity, HttpServletRequest request) throws Exception {
// 执行创建资源前的操作
String validateResult = this.beforeCreate(entity);
if (validateResult != null) {
@ -130,16 +123,10 @@ public class BaseCrudRestController<E extends BaseEntity, VO extends Serializabl
/***
* 根据ID更新资源对象用于字类重写的方法
* @param entity
* @param result
* @return JsonResult
* @throws Exception
*/
protected JsonResult updateEntity(Serializable id, E entity, BindingResult result,
HttpServletRequest request) throws Exception {
// Entity属性值验证结果
if (result.hasErrors()) {
return new JsonResult(Status.FAIL_VALIDATION, super.getBindingError(result));
}
protected JsonResult updateEntity(Serializable id, E entity, HttpServletRequest request) throws Exception {
// 执行更新资源前的操作
String validateResult = this.beforeUpdate(entity);
if (validateResult != null) {
@ -262,7 +249,7 @@ public class BaseCrudRestController<E extends BaseEntity, VO extends Serializabl
*/
protected Class<E> getEntityClass(){
if(this.entityClass == null){
initEntityVOClass();
this.entityClass = BeanUtils.getGenericityClass(this.getClass(), 0);
}
return this.entityClass;
}
@ -273,25 +260,8 @@ public class BaseCrudRestController<E extends BaseEntity, VO extends Serializabl
*/
protected Class<VO> getVOClass(){
if(this.voClasss == null){
initEntityVOClass();
this.voClasss = BeanUtils.getGenericityClass(this.getClass(), 1);
}
return this.voClasss;
}
/**
* 初始化Entity和VO的class
*/
private void initEntityVOClass(){
try{
ResolvableType resolvableType = ResolvableType.forClass(this.getClass()).getSuperType();
ResolvableType[] types = resolvableType.getSuperType().getGenerics();
if(V.notEmpty(types)){
this.entityClass = (Class<E>) types[0].resolve();
this.voClasss = (Class<VO>) types[1].resolve();
}
}
catch (Exception e){
log.warn("初始化Entity,VO class异常: "+ e.getMessage());
}
}
}

View File

@ -62,7 +62,7 @@ public abstract class BaseEntity implements Serializable {
}
/***
* model对象转为map
* Entity对象转为map
* @return
*/
public Map<String, Object> toMap(){
@ -71,7 +71,7 @@ public abstract class BaseEntity implements Serializable {
}
/**
* model对象转为String
* Entity对象转为String
* @return
*/
@Override

View File

@ -5,7 +5,7 @@ import java.util.Arrays;
import java.util.List;
/**
* Model的List包装类用于接收List并绑定校验的情况
* Entity的List包装类用于接收List并绑定校验的情况
* @author Mazhicheng
* @version 2.0
* @date 2018/11/8
@ -23,7 +23,7 @@ public class EntityList<T extends BaseEntity> {
this.entityList = entityList;
}
public List<T> getModelList(){
public List<T> getEntityList(){
return entityList;
}

View File

@ -1,18 +1,22 @@
package com.diboot.core.handle;
package com.diboot.core.handler;
import com.diboot.core.exception.BusinessException;
import com.diboot.core.util.S;
import com.diboot.core.util.V;
import com.diboot.core.vo.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
/**
* 全局异常统一处理的默认实现
@ -24,6 +28,30 @@ import java.util.Map;
public class DefaultExceptionHandler {
private final static Logger log = LoggerFactory.getLogger(DefaultExceptionHandler.class);
/**
* 统一处理校验错误 BindResult
* @param ex
* @return
*/
@ExceptionHandler({BindException.class, MethodArgumentNotValidException.class})
public Object validExceptionHandler(Exception ex){
Map<String, Object> map = new HashMap<>();
BindingResult br = null;
if(ex instanceof BindException){
br = ((BindException)ex).getBindingResult();
}
else if(ex instanceof MethodArgumentNotValidException){
br = ((MethodArgumentNotValidException)ex).getBindingResult();
}
if (br != null && br.hasErrors()) {
map.put("code", Status.FAIL_VALIDATION.code());
String validateErrorMsg = V.getBindingError(br);
map.put("msg", validateErrorMsg);
log.warn("数据校验失败, {}: {}", br.getObjectName(), validateErrorMsg);
}
return new ResponseEntity<>(map, HttpStatus.OK);
}
/**
* 统一异常处理类
* @param request
@ -43,8 +71,8 @@ public class DefaultExceptionHandler {
map.put("code", status.value());
map.put("msg", e.getMessage());
}
log.warn("请求处理异常", e);
if(isJsonRequest(request)) {
log.warn("JSON请求异常", e);
return new ResponseEntity<>(map, HttpStatus.OK);
}
else {
@ -96,4 +124,5 @@ public class DefaultExceptionHandler {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}
}

View File

@ -1,9 +1,9 @@
package com.diboot.core.service.impl;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.diboot.core.binding.RelationsBinder;
@ -14,6 +14,7 @@ import com.diboot.core.config.BaseConfig;
import com.diboot.core.config.Cons;
import com.diboot.core.mapper.BaseCrudMapper;
import com.diboot.core.service.BaseService;
import com.diboot.core.util.BeanUtils;
import com.diboot.core.util.S;
import com.diboot.core.util.V;
import com.diboot.core.vo.KeyValue;
@ -23,7 +24,9 @@ import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/***
* CRUD通用接口实现类
@ -35,6 +38,10 @@ import java.util.*;
*/
public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl<M, T> implements BaseService<T> {
private static final Logger log = LoggerFactory.getLogger(BaseServiceImpl.class);
/**
* Entity类与最佳排序字段间的映射缓存
*/
private static Map<String, String> ENTITY_ORDER_FIELD_CACHE_MAP = new ConcurrentHashMap<>();
/***
* 获取当前的Mapper对象
@ -52,7 +59,7 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
@Override
public boolean createEntity(T entity) {
if(entity == null){
warning("createModel", "参数entity为null");
warning("createEntity", "参数entity为null");
return false;
}
return super.save(entity);
@ -94,6 +101,7 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean createOrUpdateEntities(Collection entityList) {
if(V.isEmpty(entityList)){
warning("createOrUpdateEntities", "参数entityList为空!");
@ -128,7 +136,7 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
@Override
public List<T> getEntityList(Wrapper queryWrapper, Pagination pagination) {
if(pagination != null){
IPage<T> page = convertToIPage(pagination);
IPage<T> page = convertToIPage(queryWrapper, pagination);
page = super.page(page, queryWrapper);
// 如果重新执行了count进行查询则更新pagination中的总数
if(page.isSearchCount()){
@ -170,7 +178,7 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
@Override
public List<Map<String, Object>> getMapList(Wrapper queryWrapper, Pagination pagination) {
if(pagination != null){
IPage<T> page = convertToIPage(pagination);
IPage<T> page = convertToIPage(queryWrapper, pagination);
IPage<Map<String, Object>> resultPage = super.pageMaps(page, queryWrapper);
// 如果重新执行了count进行查询则更新pagination中的总数
if(page.isSearchCount()){
@ -261,30 +269,22 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
/***
* 转换为IPage
* @param pagination
* @param queryWrapper 查询条件
* @param pagination 分页
* @return
*/
protected Page<T> convertToIPage(Pagination pagination){
protected Page<T> convertToIPage(Wrapper queryWrapper, Pagination pagination){
if(pagination == null){
return null;
}
Page<T> page = new Page<T>()
.setCurrent(pagination.getPageIndex())
.setSize(pagination.getPageSize())
// 如果前端传递过来了缓存的总数则本次不再count统计
.setTotal(pagination.getTotalCount() > 0? -1 : pagination.getTotalCount());
// 排序
if(V.notEmpty(pagination.getAscList())){
pagination.getAscList().forEach(s -> {
page.addOrder(OrderItem.asc(s));
});
}
if(V.notEmpty(pagination.getDescList())){
pagination.getDescList().forEach(s -> {
page.addOrder(OrderItem.desc(s));
});
}
return page;
// 优化排序
String defaultOrderBy = getDefaultOrderField(queryWrapper);
//默认id属性存在
if(!Cons.FieldName.id.name().equals(defaultOrderBy)){
// 最佳字段不是id如create_time但默认查询字段为id需要清空默认值以免报错
pagination.clearDefaultOrder();
}
return (Page<T>)pagination.toIPage();
}
/***
@ -295,4 +295,30 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
private void warning(String method, String message){
log.warn(this.getClass().getName() + "."+ method +" 调用错误: "+message+", 请检查!");
}
/**
* 初始化Entity和VO的class
*/
private String getDefaultOrderField(Wrapper queryWrapper){
Class entityClass = BeanUtils.getGenericityClass(this.getClass(), 1);
if(entityClass == null){
return null;
}
if(!ENTITY_ORDER_FIELD_CACHE_MAP.containsKey(entityClass.getName())){
// 提取字段如果有升序id首选id
Field field = BeanUtils.extractField(entityClass, Cons.FieldName.id.name());
if(field != null){
TableField tableFieldAnno = field.getAnnotation(TableField.class);
if(tableFieldAnno == null || tableFieldAnno.exist() == true){
ENTITY_ORDER_FIELD_CACHE_MAP.put(entityClass.getName(), Cons.FieldName.id.name());
}
}
if(!ENTITY_ORDER_FIELD_CACHE_MAP.containsKey(entityClass.getName())){
ENTITY_ORDER_FIELD_CACHE_MAP.put(entityClass.getName(), "");
log.warn("{} 的默认排序字段id不存在请自行指定", entityClass.getName());
}
}
return ENTITY_ORDER_FIELD_CACHE_MAP.get(entityClass.getName());
}
}

View File

@ -3,6 +3,7 @@ package com.diboot.core.service.impl;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.diboot.core.entity.Dictionary;
import com.diboot.core.exception.BusinessException;
import com.diboot.core.mapper.DictionaryMapper;
import com.diboot.core.service.DictionaryService;
import com.diboot.core.util.BeanUtils;
@ -11,6 +12,7 @@ import com.diboot.core.util.ISetter;
import com.diboot.core.util.V;
import com.diboot.core.vo.DictionaryVO;
import com.diboot.core.vo.KeyValue;
import com.diboot.core.vo.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Primary;
@ -74,32 +76,33 @@ public class DictionaryServiceImpl extends BaseServiceImpl<DictionaryMapper, Dic
}
@Override
@Transactional
@Transactional(rollbackFor = Exception.class)
public boolean addDictTree(DictionaryVO dictVO) {
//将DictionaryVO转化为Dictionary
Dictionary dictionary = new Dictionary();
dictionary = (Dictionary) BeanUtils.copyProperties(dictVO, dictionary);
if(!super.createEntity(dictionary)){
log.warn("新建数据字典失败type="+dictVO.getType());
log.warn("新建数据字典定义失败type="+dictVO.getType());
return false;
}
List<Dictionary> children = dictVO.getChildren();
if(V.notEmpty(children)){
try {
for(Dictionary dict : children){
dict.setParentId(dictionary.getId());
dict.setType(dictionary.getType());
boolean success = true;
for(Dictionary dict : children){
dict.setParentId(dictionary.getId());
dict.setType(dictionary.getType());
boolean insertOK = super.createEntity(dict);
if (!insertOK){
log.warn("dictionary插入数据字典失败请检查");
success = false;
}
if(!super.createEntities(children)){
log.warn("新建子数据字典失败type="+dictVO.getType());
throw new RuntimeException();
}
} catch (Exception e) {
log.warn("新建子数据字典失败type="+dictVO.getType());
throw new RuntimeException();
}
if(!success){
String errorMsg = "新建数据字典子项失败type="+dictVO.getType();
log.warn(errorMsg);
throw new BusinessException(Status.FAIL_OPERATION, errorMsg);
}
}
return true;
}
}

View File

@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.core.ResolvableType;
import java.beans.BeanInfo;
import java.beans.Introspector;
@ -37,7 +38,7 @@ public class BeanUtils {
* 忽略对比的字段
*/
private static final Set<String> IGNORE_FIELDS = new HashSet<String>(){{
add("createTime");
add(Cons.FieldName.createTime.name());
}};
/***
@ -145,26 +146,26 @@ public class BeanUtils {
Object[] args = new Object[1];
String fieldType = type.getName();
// 类型不一致需转型
if(!value.getClass().getTypeName().equals(fieldType)){
if(value != null && !value.getClass().getTypeName().equals(fieldType)){
if(value instanceof String){
// String to Date
if(fieldType.equalsIgnoreCase(Date.class.getName())){
if(fieldType.equals(Date.class.getName())){
args[0] = D.fuzzyConvert((String)value);
}
// Map中的String型转换为其他型
else if(fieldType.equalsIgnoreCase(Boolean.class.getName())){
else if(fieldType.equals(Boolean.class.getName())){
args[0] = V.isTrue((String)value);
}
else if (fieldType.equalsIgnoreCase(Integer.class.getName()) || "int".equals(fieldType)) {
else if (fieldType.equals(Integer.class.getName()) || "int".equals(fieldType)) {
args[0] = Integer.parseInt((String)value);
}
else if (fieldType.equalsIgnoreCase(Long.class.getName())) {
else if (fieldType.equals(Long.class.getName())) {
args[0] = Long.parseLong((String)value);
}
else if (fieldType.equalsIgnoreCase(Double.class.getName())) {
else if (fieldType.equals(Double.class.getName())) {
args[0] = Double.parseDouble((String)value);
}
else if (fieldType.equalsIgnoreCase(Float.class.getName())) {
else if (fieldType.equals(Float.class.getName())) {
args[0] = Float.parseFloat((String)value);
}
else{
@ -172,9 +173,19 @@ public class BeanUtils {
log.warn("类型不一致,暂无法自动绑定,请手动转型一致后调用!字段类型: {} vs {} ", value.getClass().getTypeName(), fieldType);
}
}
// Integer 向上转型为 Long 绑定
else if(value.getClass().getTypeName().equals(Integer.class.getName()) && fieldType.equals(Long.class.getName())){
Integer intValue = (Integer)value;
args[0] = intValue.longValue();
}
// Float 向上转型为 Double 绑定
else if(value.getClass().getTypeName().equals(Float.class.getName()) && fieldType.equals(Double.class.getName())){
Float floatValue = (Float)value;
args[0] = floatValue.doubleValue();
}
else{
args[0] = value;
log.warn("类型不一致且Map中的value非String类型暂无法自动绑定请手动转型一致后调用 {} vs {} ", value.getClass().getTypeName(), fieldType);
log.warn("类型不一致,暂无法自动绑定,请手动转型一致后调用! {} vs {} ", value.getClass().getTypeName(), fieldType);
}
}
else{
@ -318,104 +329,74 @@ public class BeanUtils {
}
/***
* 构建上下级关联的树形结构的model
* @param allModels
* 构建上下级关联的树形结构的model上级parentId子节点children根节点=0
* @param allNodes 所有节点对象
* @param <T>
* @return
*/
public static <T extends BaseEntity> List<T> buildTree(List<T> allModels){
if(V.isEmpty(allModels)){
public static <T> List<T> buildTree(List<T> allNodes){
return buildTree(allNodes, 0);
}
/***
* 构建指定根节点的上下级关联的树形结构上级parentId子节点children
* @param allNodes 所有节点对象
* @param rootNodeId 跟节点ID
* @param <T>
* @return
*/
public static <T> List<T> buildTree(List<T> allNodes, Object rootNodeId){
if(V.isEmpty(allNodes)){
return null;
}
// 提取所有的top level对象
List<T> topLevelModels = new ArrayList();
for(T model : allModels){
for(T model : allNodes){
Object parentId = getProperty(model, Cons.FieldName.parentId.name());
if(parentId == null || V.fuzzyEqual(parentId, 0)){
if(parentId == null || V.fuzzyEqual(parentId, rootNodeId)){
topLevelModels.add(model);
}
}
if(V.isEmpty(topLevelModels)){
return topLevelModels;
}
// 提取向下一层的对象
buildDeeperLevelTree(topLevelModels, allModels);
// 返回第一层级节点二级及以上子级通过children属性获取
// 遍历第一级节点并挂载 children 子节点
for(T node : allNodes) {
Object nodeId = getProperty(node, Cons.FieldName.id.name());
List<T> children = buildTreeChildren(nodeId, allNodes);
setProperty(node, Cons.FieldName.children.name(), children);
}
return topLevelModels;
}
/*
* 构建上下级关联的树形结构去除顶层父级实体的parentId必须是为null或0的限制
* :通常情况下parentModels参数传null值就可以
* */
public static <T extends BaseEntity> List<T> buildTree(List<T> parentModels, List<T> allModels){
if(V.isEmpty(allModels)){
return null;
}
//获取顶层父级实体根据一个实体的parentId是否是allModels中的某个实体的主键来判断该实体是否为顶层父级实体
if(parentModels == null){
parentModels = new ArrayList<>();
Set<Long> idSet = new HashSet<>();
for(T model : allModels){
idSet.add(model.getId());
}
for(T model : allModels){
if(!idSet.contains((Long)getProperty(model, Cons.FieldName.parentId.name()))){
parentModels.add(model);
}
}
}
for(T parent : parentModels){
List<T> children = new ArrayList<>();
for(T model : allModels){
if(V.fuzzyEqual(parent.getId(), getProperty(model, Cons.FieldName.parentId.name()))
&& !V.fuzzyEqual(model.getId(), getProperty(model, Cons.FieldName.parentId.name()))){ //解除自循环实体的id==parentId的情况
children.add(model);
}
}
//递归调用
buildTree(children, allModels);
if(V.notEmpty(children)){
setProperty(parent, Cons.FieldName.children.name(), children);
}
}
return parentModels;
}
/***
* 构建下一层级树形结构
* @param parentModels
* @param allModels
* @param <T>
/**
* 递归构建树节点的子节点
* @param parentId
* @param nodeList
* @return
*/
private static <T extends BaseEntity> void buildDeeperLevelTree(List<T> parentModels, List<T> allModels){
List<T> deeperLevelModels = new ArrayList();
Map<String, T> parentLevelModelMap = convertToStringKeyObjectMap(parentModels, Cons.FieldName.id.name());
for(T model : allModels){
Object parentId = getProperty(model, Cons.FieldName.parentId.name());
if(parentLevelModelMap.keySet().contains(String.valueOf(parentId)) && !parentId.equals(model.getId())){
deeperLevelModels.add(model);
}
}
if(V.isEmpty(deeperLevelModels)){
return;
}
for(T model : deeperLevelModels){
Object parentId = getProperty(model, Cons.FieldName.parentId.name());
T parentModel = parentLevelModelMap.get(String.valueOf(parentId));
if(parentModel!=null){
List children = (List) getProperty(parentModel, Cons.FieldName.children.name());
private static <T> List<T> buildTreeChildren(Object parentId, List<T> nodeList) {
List<T> children = null;
for(T node : nodeList) {
Object nodeParentId = getProperty(node, Cons.FieldName.parentId.name());
if(nodeParentId != null && V.equals(nodeParentId, parentId)) {
if(children == null){
children = new ArrayList();
setProperty(parentModel, Cons.FieldName.children.name(), children);
children = new ArrayList<>();
}
children.add(model);
children.add(node);
}
}
// 递归进入下一层级
buildDeeperLevelTree(deeperLevelModels, allModels);
if(children != null){
for(T child : children) {
Object nodeId = getProperty(child, Cons.FieldName.id.name());
List<T> childNodeChildren = buildTreeChildren(nodeId, nodeList);
if(childNodeChildren == null) {
childNodeChildren = new ArrayList<>();
}
setProperty(child, Cons.FieldName.children.name(), childNodeChildren);
}
}
return children;
}
/***
@ -635,7 +616,7 @@ public class BeanUtils {
}
/**
* 获取类所有属性包含父类
* 获取类所有属性包含父类中属性
* @param clazz
* @return
*/
@ -657,6 +638,40 @@ public class BeanUtils {
return fieldList;
}
/**
* 获取类的指定属性包含父类中属性
* @param clazz
* @param fieldName
* @return
*/
public static Field extractField(Class clazz, String fieldName){
List<Field> allFields = extractAllFields(clazz);
if(V.notEmpty(allFields)){
for(Field field : allFields){
if(field.getName().equals(fieldName)){
return field;
}
}
}
return null;
}
/**
* 从宿主类定义中获取泛型定义类class
* @param hostClass
* @param index
* @return
*/
public static Class getGenericityClass(Class hostClass, int index){
ResolvableType resolvableType = ResolvableType.forClass(hostClass).getSuperType();
ResolvableType[] types = resolvableType.getSuperType().getGenerics();
if(V.notEmpty(types) && types.length > index){
return types[index].resolve();
}
log.warn("无法从 {} 类定义中获取泛型类{}", hostClass.getName(), index);
return null;
}
/***
* 获取类对应的Lambda
* @param fn

View File

@ -8,7 +8,6 @@ import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.ResolvableType;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ContextLoader;
@ -71,11 +70,11 @@ public class ContextHelper implements ApplicationContextAware {
/***
* 获取指定类型的单个Bean实例
* @param type
* @param clazz
* @return
*/
public static Object getBean(Class type){
return getApplicationContext().getBean(type);
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
/***
@ -131,9 +130,9 @@ public class ContextHelper implements ApplicationContextAware {
Map<String, IService> serviceMap = getApplicationContext().getBeansOfType(IService.class);
if(V.notEmpty(serviceMap)){
for(Map.Entry<String, IService> entry : serviceMap.entrySet()){
String entityClassName = getEntityClassByServiceImpl(entry.getValue().getClass());
if(V.notEmpty(entityClassName)){
ENTITY_SERVICE_CACHE.put(entityClassName, entry.getValue());
Class entityClass = BeanUtils.getGenericityClass(entry.getValue().getClass(), 1);
if(entityClass != null){
ENTITY_SERVICE_CACHE.put(entityClass.getName(), entry.getValue());
}
}
}
@ -152,31 +151,13 @@ public class ContextHelper implements ApplicationContextAware {
Map<String, BaseService> serviceMap = getApplicationContext().getBeansOfType(BaseService.class);
if(V.notEmpty(serviceMap)){
for(Map.Entry<String, BaseService> entry : serviceMap.entrySet()){
String entityClassName = getEntityClassByServiceImpl(entry.getValue().getClass());
if(V.notEmpty(entityClassName)){
ENTITY_BASE_SERVICE_CACHE.put(entityClassName, entry.getValue());
Class entityClass = BeanUtils.getGenericityClass(entry.getValue().getClass(), 1);
if(entityClass != null){
ENTITY_BASE_SERVICE_CACHE.put(entityClass.getName(), entry.getValue());
}
}
}
}
return ENTITY_BASE_SERVICE_CACHE.get(entity.getName());
}
/**
* 根据Service实现类的bean解析出Entity类名
* @param currentClass
* @return
*/
private static String getEntityClassByServiceImpl(Class currentClass){
ResolvableType superType = ResolvableType.forClass(currentClass).getSuperType();
ResolvableType[] genericsTypes = superType.getSuperType().getGenerics();
if(V.notEmpty(genericsTypes) && genericsTypes.length >= 2){
log.debug("Entity-Service: {} -> {}", genericsTypes[1].toString(), currentClass.getName());
return genericsTypes[1].toString();
}
else{
return null;
}
}
}

View File

@ -2,6 +2,8 @@ package com.diboot.core.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import java.sql.Timestamp;
import java.util.*;
@ -385,4 +387,21 @@ public class V {
}
}
/**
* 解析所有的验证错误信息转换为JSON
* @param result
* @return
*/
public static String getBindingError(BindingResult result){
if(result == null || !result.hasErrors()){
return null;
}
List<ObjectError> errors = result.getAllErrors();
List<String> allErrors = new ArrayList<>(errors.size());
for(ObjectError error : errors){
allErrors.add(error.getDefaultMessage().replaceAll("\"", "'"));
}
return S.join(allErrors);
}
}

View File

@ -34,9 +34,19 @@ public class JsonResult implements Serializable {
}
/**
* 默认成功有返回数据及附加提示信息
* 默认成功有返回数据
*/
public JsonResult(Object data, String... additionalMsg){
public JsonResult(Object data){
this.code = Status.OK.code();
this.msg = Status.OK.label();
initMsg(null);
this.data = data;
}
/**
* 默认成功有返回数据及附加提示信息
*/
public JsonResult(Object data, String additionalMsg){
this.code = Status.OK.code();
this.msg = Status.OK.label();
initMsg(additionalMsg);
@ -44,11 +54,22 @@ public class JsonResult implements Serializable {
}
/***
* 非成功指定状态及附加提示信息
* 非成功指定状态
* @param status
*/
public JsonResult(Status status){
this.code = status.code();
this.msg = status.label();
initMsg(null);
this.data = null;
}
/***
* 非成功指定状态及附加提示信息
* @param status
* @param additionalMsg
*/
public JsonResult(Status status, String... additionalMsg){
public JsonResult(Status status, String additionalMsg){
this.code = status.code();
this.msg = status.label();
initMsg(additionalMsg);
@ -56,9 +77,21 @@ public class JsonResult implements Serializable {
}
/**
* 非成功指定状态返回数据及附加提示信息
* 非成功指定状态返回数据
* @param status
* @param data
*/
public JsonResult(Status status, Object data){
this.code = status.code();
this.msg = status.label();
initMsg(null);
this.data = data;
}
/**
* 非成功指定状态返回数据及附加提示信息
*/
public JsonResult(Status status, Object data, String... additionalMsg){
public JsonResult(Status status, Object data, String additionalMsg){
this.code = status.code();
this.msg = status.label();
initMsg(additionalMsg);
@ -99,13 +132,13 @@ public class JsonResult implements Serializable {
* 赋值msg去掉重复前缀以支持与BusinessException嵌套使用
* @param additionalMsg
*/
private void initMsg(String... additionalMsg){
private void initMsg(String additionalMsg){
if(V.notEmpty(additionalMsg)){
if(S.startsWith(additionalMsg[0], this.msg)){
this.msg = additionalMsg[0];
if(S.startsWith(additionalMsg, this.msg)){
this.msg = additionalMsg;
}
else{
this.msg += ": " + additionalMsg[0];
this.msg += ": " + additionalMsg;
}
}
}

View File

@ -3,7 +3,7 @@ package com.diboot.core.vo;
import java.io.Serializable;
/**
* KeyValue键值对形式的VO
* KeyValue键值对形式的VO用于构建显示名Name-存储值Value形式的结果
* @author Mazhicheng
* @version v2.0
* @date 2019/1/4
@ -19,7 +19,7 @@ public class KeyValue implements Serializable {
}
/***
* key: 显示值
* key: 显示值需要显示的name/label文本
*/
private String k;

View File

@ -1,6 +1,10 @@
package com.diboot.core.vo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.diboot.core.config.BaseConfig;
import com.diboot.core.config.Cons;
import com.diboot.core.util.S;
import com.diboot.core.util.V;
import org.slf4j.Logger;
@ -8,7 +12,6 @@ import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@ -34,14 +37,14 @@ public class Pagination implements Serializable {
* count总数
*/
private long totalCount = 0;
/***
* 排序-升序排列的字段
/**
* 默认排序
*/
private List<String> ascList = null;
/***
* 降序列的字段默认以create_time降排列当指定了其他排列方式时以用户指定为准
private static final String DEFAULT_ORDER_BY = Cons.FieldName.id.name()+":"+Cons.ORDER_DESC;
/**
* 排序
*/
private List<String> descList = new ArrayList<>(Arrays.asList("create_time"));
private String orderBy = DEFAULT_ORDER_BY;
public Pagination(){
}
@ -81,46 +84,20 @@ public class Pagination implements Serializable {
this.totalCount = totalCount;
}
public void setOrderBy(String orderBy){
if(V.isEmpty(orderBy)){
return;
}
// 先清空默认排序规则
clearDefaultOrder();
// 指定新的排序规则
String[] orderByFields = S.split(orderBy);
for(String field : orderByFields){
// orderBy=name:DESC,age:ASC,birthdate
if(field.contains(":")){
String[] fieldAndOrder = S.split(field, ":");
if("DESC".equalsIgnoreCase(fieldAndOrder[1])){
if(descList == null){
descList = new ArrayList<>();
}
descList.add(fieldAndOrder[0]);
}
else{
if(ascList == null){
ascList = new ArrayList<>();
}
ascList.add(fieldAndOrder[0]);
}
}
else{
if(ascList == null){
ascList = new ArrayList<>();
}
ascList.add(field);
}
}
/***
* 获取数据库字段的列排序
* @return
*/
public String getOrderBy() {
return this.orderBy;
}
/***
* 清除默认排序
/**
* 设置排序
* @param orderBy
*/
public void clearDefaultOrder(){
ascList = null;
descList = null;
public void setOrderBy(String orderBy){
this.orderBy = orderBy;
}
/***
@ -134,20 +111,52 @@ public class Pagination implements Serializable {
return (int)Math.ceil((float) totalCount / pageSize);
}
/***
* 获取数据库字段的列排序用于service层调用
* @return
/**
* 清空默认排序
*/
public List<String> getAscList() {
return ascList;
public void clearDefaultOrder(){
// 是否为默认排序
if(V.equals(orderBy, DEFAULT_ORDER_BY)){
orderBy = null;
}
}
/***
* 获取数据库字段的列排序,用于service层调用
/**
* 转换为IPage
* @param <T>
* @return
*/
public List<String> getDescList() {
return descList;
public <T> IPage<T> toIPage(){
List<OrderItem> orderItemList = null;
// 解析排序
if(V.notEmpty(this.orderBy)){
orderItemList = new ArrayList<>();
// orderBy=shortName:DESC,age:ASC,birthdate
String[] orderByFields = S.split(this.orderBy);
for(String field : orderByFields){
if(field.contains(":")){
String[] fieldAndOrder = S.split(field, ":");
String columnName = S.toSnakeCase(fieldAndOrder[0]);
if(Cons.ORDER_DESC.equalsIgnoreCase(fieldAndOrder[1])){
orderItemList.add(OrderItem.desc(columnName));
}
else{
orderItemList.add(OrderItem.asc(columnName));
}
}
else{
orderItemList.add(OrderItem.asc(S.toSnakeCase(field)));
}
}
}
IPage<T> page = new Page<T>()
.setCurrent(getPageIndex())
.setSize(getPageSize())
// 如果前端传递过来了缓存的总数则本次不再count统计
.setTotal(getTotalCount() > 0? -1 : getTotalCount());
if(orderItemList != null){
((Page<T>) page).addOrder(orderItemList);
}
return page;
}
}

View File

@ -58,7 +58,7 @@ public enum Status {
FAIL_EXCEPTION(5000, "系统异常"),
/***
* 系统异常
* 缓存清空
*/
MEMORY_EMPTY_LOST(9999, "缓存清空");

View File

@ -47,7 +47,9 @@ public class TestEntityBinder {
// 验证直接关联和通过中间表间接关联的绑定
Assert.assertEquals(vo.getDepartmentId(), vo.getDepartment().getId());
Assert.assertNotNull(vo.getDepartment().getOrgId());
Assert.assertNotNull(vo.getOrganization());
// 测试绑定VO
Assert.assertNotNull(vo.getOrganizationVO());
System.out.println(JSON.stringify(vo.getOrganizationVO()));
System.out.println(JSON.stringify(vo));
}
}

View File

@ -9,6 +9,7 @@ import diboot.core.test.binder.entity.Department;
import diboot.core.test.binder.entity.User;
import diboot.core.test.binder.service.DepartmentService;
import diboot.core.test.binder.service.UserService;
import diboot.core.test.binder.vo.DepartmentVO;
import diboot.core.test.binder.vo.EntityListComplexBinderVO;
import diboot.core.test.binder.vo.EntityListSimpleBinderVO;
import diboot.core.test.config.SpringMvcConfig;
@ -58,7 +59,7 @@ public class TestEntityListBinder {
System.out.println(JSON.stringify(vo));
if(vo.getChildren() != null){
for(Department dept : vo.getChildren()){
for(DepartmentVO dept : vo.getChildren()){
System.out.println(dept.toString());
}
}

View File

@ -0,0 +1,38 @@
package diboot.core.test.binder.vo;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.TableField;
import com.diboot.core.entity.BaseEntity;
import diboot.core.test.binder.entity.Department;
/**
* 定时任务
* @author Mazhicheng
* @version v2.0
* @date 2018/12/27
*/
public class DepartmentVO {
private static final long serialVersionUID = -4849732665419794547L;
@TableField
private Long parentId;
@TableField(exist = false)
private String name;
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -19,19 +19,20 @@ public class EntityBinderVO extends User {
// 通过中间表关联Entity
@BindEntity(entity = Organization.class, condition = "this.department_id=department.id AND department.org_id=id") // AND ...
private Organization organization;
private OrganizationVO organizationVO;
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public Organization getOrganization() {
return organization;
public OrganizationVO getOrganizationVO() {
return organizationVO;
}
public void setOrganization(Organization organization) {
this.organization = organization;
public void setOrganizationVO(OrganizationVO organizationVO) {
this.organizationVO = organizationVO;
}
}

View File

@ -15,13 +15,13 @@ public class EntityListSimpleBinderVO extends Department {
// 直接关联多个Entity
@BindEntityList(entity = Department.class, condition = "this.id=parent_id")
private List<Department> children;
private List<DepartmentVO> children;
public List<Department> getChildren() {
public List<DepartmentVO> getChildren() {
return children;
}
public void setChildren(List<Department> children) {
public void setChildren(List<DepartmentVO> children) {
this.children = children;
}
}

View File

@ -0,0 +1,34 @@
package diboot.core.test.binder.vo;
import com.baomidou.mybatisplus.annotation.TableField;
/**
* <Description>
*
* @author Mazhicheng
* @version v2.0
* @date 2019/12/06
*/
public class OrganizationVO {
private Long parentId;
private String name;
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -7,6 +7,7 @@ import com.diboot.core.service.DictionaryService;
import com.diboot.core.util.BeanUtils;
import com.diboot.core.util.SqlExecutor;
import com.diboot.core.util.V;
import com.diboot.core.vo.KeyValue;
import com.diboot.core.vo.Pagination;
import diboot.core.test.StartupApplication;
import diboot.core.test.config.SpringMvcConfig;
@ -134,6 +135,13 @@ public class BaseServiceTest {
Assert.assertTrue(success);
}
@Test
public void testKV(){
List<KeyValue> keyValues = dictionaryService.getKeyValueList("GENDER");
Assert.assertTrue(keyValues.size() == 2);
Assert.assertTrue(keyValues.get(0).getV().equals("M"));
}
/**
* 清空测试数据
* @param type

View File

@ -1,6 +1,8 @@
package diboot.core.test.util;
import com.diboot.core.util.D;
import com.diboot.core.vo.JsonResult;
import com.diboot.core.vo.Status;
import org.junit.Assert;
import org.junit.Test;
@ -27,4 +29,18 @@ public class DTest {
}
}
@Test
public void testJsonResult(){
String token = "token";
JsonResult j1 = new JsonResult(token);
JsonResult j2 = new JsonResult(token, "申请token成功");
JsonResult j3 = new JsonResult(Status.OK, token);
JsonResult j4 = new JsonResult(Status.OK, token, "申请token成功");
JsonResult j5 = new JsonResult(Status.OK);
System.out.println(j1.getData());
System.out.println(j2.getData());
System.out.println(j3.getData());
System.out.println(j4.getData());
System.out.println(j5.getData());
}
}

View File

@ -24,7 +24,7 @@ public class PropertiesTest {
@Test
public void testGetString(){
String str1 = PropertiesUtils.get("spring.datasource.url");
String str2 = PropertiesUtils.get("spring.datasource.username2");
String str2 = PropertiesUtils.get("spring.datasource.username");
Assert.assertNotNull(str1);
Assert.assertNotNull(str2);
}

View File

@ -1,19 +0,0 @@
# spring config
spring.devtools.restart.enabled=true
#datasource config
spring.datasource.url=jdbc:mysql://localhost:3306/diboot_example?characterEncoding=utf8&serverTimezone=GMT%2B8
spring.datasource.username=diboot
spring.datasource.password=123456
spring.datasource.hikari.maximum-pool-size=5
# 数据库驱动
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
# logging config
logging.level.root=debug
logging.level.org.apache=info
logging.level.org.hibernate.validator=info
logging.level.org.springframework=info
logging.level.com.zaxxer.hikari=info
logging.level.com.diboot=debug
logging.level.org.mybatis=debug

View File

@ -14,7 +14,7 @@
</div>
<div class="custom content">
<div class="features">
<div class="feature" style="max-width: 35%;">
<div class="feature col-1">
<h2>diboot-core 精简高效内核</h2>
<p>
<ul>
@ -26,7 +26,7 @@
</ul>
</p>
</div>
<div class="feature" style="max-width: 60%">
<div class="feature col-2">
<h2>diboot-devtools 强大开发助理</h2>
<p>
<ul>
@ -44,29 +44,31 @@
<div class="footer-content">
<div class="footer-item"></div>
<div class="footer-item">
<h4>微信扫码进群</h4>
<img src="../public/add_wechat.png" alt="" width="120">
<h4>QQ群: 731690096</h4>
<img src="../public/add_qqqun.png" alt="" width="120">
</div>
<div class="footer-item">
<h4>联系我们</h4>
<ul>
<li>电话0512-62988949</li>
<li>Q Q281550336</li>
<li>QQ群731690096</li>
<li>邮箱service@dibo.ltd</li>
</ul>
<h4>微信群: wx20201024</h4>
<img src="../public/add_wechat.png" alt="" width="120">
</div>
<div class="footer-item">
</div>
</div>
<p class="copy-right">© 2015-2019 <a href="http://www.dibo.ltd">苏州帝博信息技术有限公司</a></p>
<p class="copy-right">© 2015-2020 <a href="http://www.dibo.ltd">苏州帝博信息技术有限公司</a>
<br>
<a class="ba-a" href="http://www.beian.miit.gov.cn/">
<u>
苏ICP备15013001号
</u>
</a>
</p>
</div>
</div>
<div class="modal-cover" v-show="showModal" @click="closeModal">
<div class="modal-content" @click="onModalContentClick($event)">
<video src="https://diboot.oss-cn-shanghai.aliyuncs.com/file/%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8diboot%E4%B8%8E%E5%85%B3%E8%81%94%E6%BC%94%E7%A4%BA_%E5%AD%97%E5%B9%95.mp4?nsukey=Z8MYZulBJM0GZXYNQgYGJGxRDvYMIWMsGj%2FkV%2BQpZD2aN7hsk7hreW1mXv71kxX7W5Sd61warYIPAAT4xDifYZEv3cQiiQiVW%2BqF%2FqomaxhPm1ht5jV5YkWLGiwcqQKeHmN5jn9%2FRWzaWTNweSZjWuif%2FZIciG6BX8UddZ0klnyyGfrtdB2eWCBAh%2F5n5rMPGdyUhYXAzzDsdPtyBxcY7w%3D%3D"
class="homeVideo" id="homeVideo" height="666" controls>
class="homeVideo" id="homeVideo" controls>
</video>
</div>
</div>
@ -152,7 +154,7 @@
}
}
.footer {
padding: 1rem 2.5rem 2.5rem;
padding: 1rem 2.5rem .5rem 2.5rem;
border-top: 1px solid #eaecef;
text-align: left;
color: #4e6e8e;
@ -227,7 +229,7 @@ a.button.white {
height: 100%;
background-color: rgba(0, 0, 0, 0.2);
z-index: 1000;
position: absolute;
position: fixed;
top: 0;
left: 0;
text-align: center;
@ -246,10 +248,64 @@ a.button.white {
background-color: white;
box-shadow: 0 0 15px black;
}
.homeVideo{
height: 666px;
}
.donate-image{
display: block;
margin: 10px auto;
width: 500px;
height: auto;
}
.feature.col-1{
max-width: 35%;
}
.feature.col-2{
max-width: 60%;
}
.ba-line{
margin: 0;
line-height: 28px;
text-align: center;
}
.copy-right .ba-a{
font-size: 16px;
color: #999;
}
@media screen and (max-width: 750px) {
.button.has-icon{
margin-top: 15px;
}
.home {
padding: 0;
}
.home .feature{
padding: 0;
}
.feature.col-1{
max-width: 100%;
}
.feature.col-2{
max-width: 100%;
}
.home .footer{
padding: 1rem 0;
}
.home .footer .footer-content .footer-item{
max-width: 100%;
flex-basis: 100%;
}
.modal-content{
width: 320px;
height: 180px;
margin-top: -90px;
margin-left: -160px;
}
.homeVideo{
height: 180px;
}
a.button.white{
margin-right: 0;
}
}
</style>

View File

@ -1,6 +1,6 @@
module.exports = {
title: 'Diboot 轻代码开发平台',
description: '2.0 - 更好更强的轻代码开发平台',
description: '2.0 - 您的自动化开发助理',
head: [
['link', {rel: 'icon', href: '/logo.png'}]
],
@ -55,7 +55,7 @@ module.exports = {
]
},
nav: [{
text: '首页', link: '/'
text: '首页', link: '/index.html'
}, {
text: 'core内核 指南',
link: '/guide/diboot-core/安装'
@ -63,12 +63,14 @@ module.exports = {
text: 'devtools助理 指南',
link: '/guide/diboot-devtools/介绍'
},{
text: '捐助Diboot团队',
text: '捐助我们',
link: '/guide/donate/'
},{
text: '项目合作',
link:'http://www.dibo.ltd/contect.html'
},{
text: 'Gitee', link: 'https://gitee.com/dibo_software/diboot-v2'
},{
text: 'GitHub', link: 'https://github.com/dibo-software/diboot-v2'
},{
text: '1.x旧版', link: 'https://www.diboot.com/diboot-v1/'

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

3
diboot-docs/build.sh Executable file
View File

@ -0,0 +1,3 @@
yarn
yarn build
gulp

View File

@ -21,17 +21,19 @@ MySQL、MariaDB、ORACLE、SQLServer、PostgreSQL
参考样例 [diboot-core-example](https://github.com/dibo-software/diboot-v2-example/tree/master/diboot-core-example)
> 如果您使用diboot-devtoolsdiboot-core会被自动引入无需再配置依赖。
## 相关依赖
:::tip
以下依赖在引入diboot-core-starter依赖中可以不再引入。
:::
* **javax.servlet-api**(javax.servlet:javax.servlet-api:4.0.1)
* **spring-boot-starter-web**(org.springframework.boot:spring-boot-starter-web:2.1.8.RELEASE)
* **spring-boot-starter-web**(org.springframework.boot:spring-boot-starter-web:2.2.1.RELEASE)
* **mybatis-plus-boot-starter**(com.baomidou:mybatis-plus-boot-starter:3.2.0)
* **commons-io**(commons-io:commons-io:2.6)
* **commons-lang3**(org.apache.commons:commons-lang3:3.8.1)
* **fastjson**(com.alibaba:fastjson:1.2.60)
:::tip
以上依赖在引入diboot-core-starter依赖的项目中可以不再引入。
需要额外添加的jar
:::
* **数据库驱动包** (如 mysql:mysql-connector-java:8.0.18)

View File

@ -91,17 +91,17 @@ Map map = BeanUtils.convertToStringKeyObjectListMap(allLists, fields);
* buildTree 方法
```java
//该方法用来构建上下级关联的实体关系树形结构顶层父级实体的parentId必须是为null或0入参为对象集合allModels
//该方法用来构建支持无限层级的树形结构默认顶层父级节点的parentId=0入参为对象集合allNodes
//方法定义
public static <T extends BaseEntity> List<T> buildTree(List<T> allModels){...}
public static <T> List<T> buildTree(List<T> allNodes){...}
//方法调用示例
List list = BeanUtils.buildTree(allModels);
List<Menu> menuTree = BeanUtils.buildTree(allNodes);
//该方法用来构建上下级关联的实体关系树形结构去除顶层父级实体的parentId必须是为null或0的限制入参为对象集合allModels
//该方法用来构建支持无限层级的树形结构指定顶层父级节点的parentId入参为allNodes, rootNodeId
//方法定义
public static <T extends BaseEntity> List<T> buildTree(List<T> parentModels, List<T> allModels){...}
public static <T> List<T> buildTree(List<T> allNodes, Object rootNodeId){
//方法调用示例
List list = BeanUtils.buildTree(parentModels, allModels);
List<Menu> menuTree = BeanUtils.buildTree(allNodes, 1L);
```
* extractDiff 方法

View File

@ -1 +1,14 @@
# 异常处理
# 异常处理
## DefaultExceptionHandler
* 默认异常处理,继承自该类并添加@ControllerAdvice注解即可自动支持:
* 兼容JSON请求和Html页面请求的Exception异常处理
* Entity绑定校验的统一处理BindException.class, MethodArgumentNotValidException.class
* 示例代码
~~~java
@ControllerAdvice
public class GeneralExceptionHandler extends DefaultExceptionHandler{
}
~~~

View File

@ -108,6 +108,7 @@ private String orgName;
### 绑定单个实体
> 绑定单个实体使用**@BindEntity**注解进行处理,将得到关联表对应的单个实体。
> 如果属性类型为VO等非Entity对象类型将自动转换为您指定的类型再绑定。
* 使用@BindEntity注解时需传两个参数分别是entity和condition。
* entity表示关联实体类
* condition表示关联条件。
@ -124,6 +125,7 @@ private Organization organization;
### 绑定实体列表
> 绑定实体列表使用**@BindEntityList**注解进行处理,将得到关联表对应的实体列表。
> 如果待绑定List中的泛型参数类型为VO等非Entity对象将自动转换为您指定的类型再绑定。
* 使用@BindEntityList注解时需传两个参数分别是entity和condition。
* entity表示关联实体类
* condition表示关联条件。

View File

@ -24,9 +24,9 @@ public class UserDTO{
* 将映射为 queryWrapper.eq("gender", "M").like("realname", "张")
*/
@GetMapping("/list")
public JsonResult getVOList(UserDto userDto) throws Exception{
//调用super.buildQueryWrapper(entityOrDto) 或者直接调用 QueryBuilder.toQueryWrapper(entityOrDto) 进行转换
QueryWrapper<User> queryWrapper = super.buildQueryWrapper(userDto);
public JsonResult getVOList(UserDto userDto, HttpServletRequest request) throws Exception{
//调用super.buildQueryWrapper(entityOrDto, request) 或者直接调用 QueryBuilder.toQueryWrapper(entityOrDto) 进行转换
QueryWrapper<User> queryWrapper = super.buildQueryWrapper(userDto, request);
//... 查询list
return new JsonResult(Status.OK, list);
}

Some files were not shown because too many files have changed in this diff Show More