file组件
This commit is contained in:
parent
054e5a28f4
commit
3c6819434b
|
@ -0,0 +1,27 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot'
|
||||
}
|
||||
|
||||
group 'com-commons-excel'
|
||||
version '2.0-SNAPSHOT'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
ext {
|
||||
mysqlConnectorVersion = "8.0.16"
|
||||
mybatisStarterVersion = "2.0.1"
|
||||
mybatisPlusVersion = "3.1.1"
|
||||
}
|
||||
dependencies {
|
||||
compile project(":dibo-commons-file")
|
||||
compile project(":diboot-core")
|
||||
|
||||
//easyexcel
|
||||
compile("com.alibaba:easyexcel:2.0.5")
|
||||
|
||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
package com.diboot.excel.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.diboot.commons.entity.BaseFile;
|
||||
import com.diboot.commons.file.FileHelper;
|
||||
import com.diboot.commons.service.BaseFileService;
|
||||
import com.diboot.commons.utils.S;
|
||||
import com.diboot.commons.utils.V;
|
||||
import com.diboot.commons.vo.JsonResult;
|
||||
import com.diboot.commons.vo.Pagination;
|
||||
import com.diboot.commons.vo.Status;
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
import com.diboot.core.service.BaseService;
|
||||
import com.diboot.excel.entity.BaseExcelDataEntity;
|
||||
import com.diboot.excel.entity.ExcelColumn;
|
||||
import com.diboot.excel.listener.BaseExcelDataListener;
|
||||
import com.diboot.excel.service.ExcelColumnService;
|
||||
import com.diboot.excel.service.ExcelImportRecordService;
|
||||
import com.diboot.excel.utils.EasyExcelHelper;
|
||||
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@com.ltd
|
||||
* @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 com.diboot.commons.service.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(com.diboot.commons.vo.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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package com.diboot.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.diboot.core.util.V;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package com.diboot.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()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.diboot.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));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package com.diboot.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());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.diboot.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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.diboot.excel.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/***
|
||||
* excel数据导入导出实体基类
|
||||
* @auther wangyl
|
||||
* @date 2019-10-9
|
||||
*/
|
||||
@Data
|
||||
public class BaseExcelDataEntity {
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.diboot.excel.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Excel列定义
|
||||
* @author Mazc@com.ltd
|
||||
* @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; // 校验
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.diboot.excel.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Excel导入记录
|
||||
* @author Mazc@com.ltd
|
||||
* @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
|
||||
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
package com.diboot.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.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 com.diboot.excel.entity.BaseExcelDataEntity;
|
||||
import com.diboot.excel.entity.ExcelColumn;
|
||||
import com.diboot.excel.service.ExcelColumnService;
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.diboot.excel.mapper;
|
||||
|
||||
import com.diboot.core.mapper.BaseCrudMapper;
|
||||
import com.diboot.excel.entity.ExcelColumn;
|
||||
|
||||
/**
|
||||
* @author Lishuaifei
|
||||
* @description
|
||||
* @creatime 2019-07-11 17:03
|
||||
*/
|
||||
public interface ExcelColumnMapper extends BaseCrudMapper<ExcelColumn> {
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?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.excel.mapper.ExcelColumnMapper">
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,13 @@
|
|||
package com.diboot.excel.mapper;
|
||||
|
||||
import com.diboot.core.mapper.BaseCrudMapper;
|
||||
import com.diboot.excel.entity.ExcelImportRecord;
|
||||
|
||||
/**
|
||||
* @author Lishuaifei
|
||||
* @description
|
||||
* @creatime 2019-07-11 17:03
|
||||
*/
|
||||
public interface ExcelImportRecordMapper extends BaseCrudMapper<ExcelImportRecord> {
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?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.excel.mapper.ExcelImportRecordMapper">
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,13 @@
|
|||
package com.diboot.excel.service;
|
||||
|
||||
import com.diboot.core.service.BaseService;
|
||||
import com.diboot.excel.entity.ExcelColumn;
|
||||
|
||||
/**
|
||||
* @author Lishuaifei
|
||||
* @description
|
||||
* @creatime 2019-07-11 17:05
|
||||
*/
|
||||
public interface ExcelColumnService extends BaseService<ExcelColumn> {
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.diboot.excel.service;
|
||||
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
import com.diboot.core.service.BaseService;
|
||||
import com.diboot.excel.entity.ExcelImportRecord;
|
||||
|
||||
|
||||
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);
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.diboot.excel.service.impl;
|
||||
|
||||
import com.diboot.core.service.impl.BaseServiceImpl;
|
||||
import com.diboot.excel.entity.ExcelColumn;
|
||||
import com.diboot.excel.mapper.ExcelColumnMapper;
|
||||
import com.diboot.excel.service.ExcelColumnService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author Lishuaifei
|
||||
* @description
|
||||
* @creatime 2019-07-11 17:06
|
||||
*/
|
||||
@Service
|
||||
public class ExcelColumnServiceImpl extends BaseServiceImpl<ExcelColumnMapper, ExcelColumn> implements ExcelColumnService {
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package com.diboot.excel.service.impl;
|
||||
|
||||
import com.diboot.core.entity.BaseEntity;
|
||||
import com.diboot.core.service.impl.BaseServiceImpl;
|
||||
import com.diboot.excel.entity.ExcelImportRecord;
|
||||
import com.diboot.excel.mapper.ExcelImportRecordMapper;
|
||||
import com.diboot.excel.service.ExcelImportRecordService;
|
||||
import com.diboot.commons.utils.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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package com.diboot.excel.utils;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.excel.entity.BaseExcelDataEntity;
|
||||
import com.diboot.excel.listener.BaseExcelDataListener;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
//环境配置,默认是开发环境
|
||||
def profile = System.getProperty("profile") ?: "develop"
|
||||
sourceSets {
|
||||
main {
|
||||
resources {
|
||||
srcDirs "src/main/java", "src/main/resources/$profile"
|
||||
include '**/*.properties'
|
||||
include '**/*.xml'
|
||||
include '**/*.dtd'
|
||||
include '**/*.class'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def poiversion = "3.17"
|
||||
|
||||
// office文件相关依赖
|
||||
compile("org.apache.poi:poi:$poiversion",
|
||||
"org.apache.poi:poi-ooxml:$poiversion")
|
||||
// 图片压缩
|
||||
compile ("net.coobird:thumbnailator:0.4.8")
|
||||
// 文件下载
|
||||
compile("org.apache.httpcomponents:httpclient:4.5.9")
|
||||
// apache commons
|
||||
compile("commons-fileupload:commons-fileupload:1.3.3",
|
||||
"org.apache.commons:commons-lang3:3.6",
|
||||
"commons-io:commons-io:2.6")
|
||||
|
||||
// 引入validator jar
|
||||
compile("org.hibernate:hibernate-validator:6.0.7.Final",
|
||||
"org.glassfish:javax.el:3.0.1-b08")
|
||||
|
||||
}
|
||||
|
||||
//create a single Jar with all dependencies
|
||||
jar{
|
||||
manifest {
|
||||
attributes('Implementation-Title': 'Dibo Commons - file',
|
||||
'Implementation-Version': version
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package com.diboot.commons.config;
|
||||
|
||||
import com.diboot.commons.utils.PropertiesUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/***
|
||||
* 系统默认配置
|
||||
* @author Mazhicheng
|
||||
* @version 2.0
|
||||
* @date 2019/01/01
|
||||
*/
|
||||
public class BaseConfig {
|
||||
private static final Logger log = LoggerFactory.getLogger(BaseConfig.class);
|
||||
|
||||
/**
|
||||
* 从默认的/指定的 Properties文件获取配置
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String getProperty(String key, String... propertiesFileName){
|
||||
return PropertiesUtils.get(key, propertiesFileName);
|
||||
}
|
||||
|
||||
/***
|
||||
* 从默认的/指定的 Properties文件获取boolean值
|
||||
* @param key
|
||||
* @param propertiesFileName
|
||||
* @return
|
||||
*/
|
||||
public static boolean isTrue(String key, String... propertiesFileName){
|
||||
return PropertiesUtils.getBoolean(key, propertiesFileName);
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取int类型
|
||||
* @param key
|
||||
* @param propertiesFileName
|
||||
* @return
|
||||
*/
|
||||
public static int getInteger(String key, String... propertiesFileName){
|
||||
return PropertiesUtils.getInteger(key, propertiesFileName);
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取截取长度
|
||||
* @return
|
||||
*/
|
||||
public static int getCutLength(){
|
||||
Integer length = PropertiesUtils.getInteger("system.default.cutLength");
|
||||
if(length != null){
|
||||
return length;
|
||||
}
|
||||
return 20;
|
||||
}
|
||||
|
||||
/***
|
||||
* 默认页数
|
||||
* @return
|
||||
*/
|
||||
public static int getPageSize() {
|
||||
Integer length = PropertiesUtils.getInteger("pagination.default.pageSize");
|
||||
if(length != null){
|
||||
return length;
|
||||
}
|
||||
return 20;
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取批量插入的每批次数量
|
||||
* @return
|
||||
*/
|
||||
public static int getBatchSize() {
|
||||
return 1000;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package com.diboot.commons.config;
|
||||
|
||||
public class BaseCons {
|
||||
|
||||
/**
|
||||
* 默认字符集UTF-8
|
||||
*/
|
||||
public static final String CHARSET_UTF8 = "UTF-8";
|
||||
/***
|
||||
* ISO-8859-1
|
||||
*/
|
||||
public static final String CHARSET_ISO8859_1 = "ISO8859-1";
|
||||
|
||||
/**
|
||||
* 分隔符 ,
|
||||
*/
|
||||
public static final String SEPARATOR_COMMA = ",";
|
||||
|
||||
public static final String SEPARATOR_UNDERSCORE = "_";
|
||||
|
||||
/***
|
||||
* 默认字段名定义
|
||||
*/
|
||||
public enum FieldName{
|
||||
/**
|
||||
* 主键属性名
|
||||
*/
|
||||
id,
|
||||
/**
|
||||
* 新建时间属性名
|
||||
*/
|
||||
create_time,
|
||||
/**
|
||||
* 默认的上级ID属性名
|
||||
*/
|
||||
parentId,
|
||||
/**
|
||||
* 子节点属性名
|
||||
*/
|
||||
children
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package com.diboot.commons.entity;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.diboot.commons.utils.JSON;
|
||||
import com.diboot.commons.utils.V;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Entity基础父类
|
||||
* @author Lishuaifei
|
||||
* @version v2.0
|
||||
* @date 2018/12/27
|
||||
*/
|
||||
public abstract class BaseEntity implements Serializable {
|
||||
private static final long serialVersionUID = 10203L;
|
||||
|
||||
@NotNull(message = "编号不能为空!")
|
||||
@Length(max = 32, message = "编号长度超出了最大限制!")
|
||||
@TableId(type = IdType.UUID)
|
||||
private String uuid = null;
|
||||
|
||||
/***
|
||||
* 默认逻辑删除标记,deleted=0有效
|
||||
*/
|
||||
@TableLogic
|
||||
@JSONField(serialize = false)
|
||||
@TableField("is_deleted")
|
||||
private boolean deleted = false;
|
||||
|
||||
/***
|
||||
* 默认记录创建时间字段,新建时由数据库赋值
|
||||
*/
|
||||
@TableField(update="now()")
|
||||
private Date createTime;
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public Date getCreateTime() {
|
||||
return this.createTime;
|
||||
}
|
||||
|
||||
public void setCreateTime(Date createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public void setDeleted(boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
/***
|
||||
* model对象转为map
|
||||
* @return
|
||||
*/
|
||||
public Map<String, Object> toMap(){
|
||||
String jsonStr = JSON.stringify(this);
|
||||
return JSON.toMap(jsonStr);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
package com.diboot.commons.entity;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
@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 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,637 @@
|
|||
package com.diboot.commons.file;
|
||||
|
||||
import com.diboot.commons.file.http.CustomSSLSocketFactory;
|
||||
import com.diboot.commons.utils.PropertiesUtils;
|
||||
import com.diboot.commons.utils.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.*;
|
||||
|
||||
/***
|
||||
* 文件操作辅助类
|
||||
* @author Mazc@com.ltd
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.diboot.commons.file.audio;
|
||||
|
||||
import com.diboot.commons.utils.RuntimeHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/***
|
||||
* Dibo 音频处理类
|
||||
* @author Mazc@com.ltd
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package com.diboot.commons.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@com.ltd
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package com.diboot.commons.file.image;
|
||||
|
||||
import com.diboot.commons.utils.Base64;
|
||||
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;
|
||||
|
||||
/***
|
||||
* 图片操作辅助类
|
||||
* @author Mazc@com.ltd
|
||||
*/
|
||||
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.decodeFast(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);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
package com.diboot.commons.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@com.ltd
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.diboot.commons.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import com.diboot.commons.entity.BaseFile;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
/**
|
||||
* @author Mazc@com.ltd
|
||||
* @version 2017/4/18
|
||||
*
|
||||
*/
|
||||
public interface BaseFileMapper extends BaseMapper<BaseFile> {
|
||||
|
||||
@Select("SELECT * FROM file ${ew.customSqlSegment}")
|
||||
BaseFile getModel(@Param(Constants.WRAPPER) Wrapper<BaseFile> wrapper);
|
||||
|
||||
int updateModel(@Param("m") BaseFile model);
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?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.commons.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>
|
|
@ -0,0 +1,291 @@
|
|||
<?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
|
||||
>
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.diboot.commons.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import com.diboot.commons.entity.BaseFile;
|
||||
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);
|
||||
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
package com.diboot.commons.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.diboot.commons.vo.KeyValue;
|
||||
import com.diboot.commons.vo.Pagination;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Lishuaifei
|
||||
* @description 基础Service
|
||||
* @creatime 2019-07-18 15:20
|
||||
*/
|
||||
public interface BaseService<T> {
|
||||
|
||||
/**
|
||||
* 获取Entity实体
|
||||
* @param id 主键
|
||||
* @return entity
|
||||
*/
|
||||
T getEntity(Serializable id);
|
||||
|
||||
/**
|
||||
* 创建Entity实体
|
||||
* @param entity
|
||||
* @return true:成功, false:失败
|
||||
*/
|
||||
boolean createEntity(T entity);
|
||||
|
||||
/***
|
||||
* 批量创建Entity
|
||||
* @param entityList 实体对象列表
|
||||
* @return true:成功, false: 失败
|
||||
*/
|
||||
boolean createEntities(Collection<T> entityList);
|
||||
|
||||
/**
|
||||
* 更新Entity实体
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
boolean updateEntity(T entity);
|
||||
|
||||
/**
|
||||
* 更新Entity实体(更新符合条件的所有非空字段)
|
||||
* @param entity
|
||||
* @param updateCriteria
|
||||
* @return
|
||||
*/
|
||||
boolean updateEntity(T entity, Wrapper updateCriteria);
|
||||
|
||||
/**
|
||||
* 更新Entity实体(仅更新updateWrapper.set指定的字段)
|
||||
* @param updateWrapper
|
||||
* @return
|
||||
*/
|
||||
boolean updateEntity(Wrapper updateWrapper);
|
||||
|
||||
/***
|
||||
* 创建或更新entity(entity.id存在则新建,否则更新)
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
boolean createOrUpdateEntity(T entity);
|
||||
|
||||
/**
|
||||
* 批量创建或更新entity(entity.id存在则新建,否则更新)
|
||||
* @param entityList
|
||||
* @return
|
||||
*/
|
||||
boolean createOrUpdateEntities(Collection entityList);
|
||||
|
||||
/**
|
||||
* 根据主键删除实体
|
||||
* @param id 主键
|
||||
* @return true:成功, false:失败
|
||||
*/
|
||||
boolean deleteEntity(Serializable id);
|
||||
|
||||
/**
|
||||
* 按条件删除实体
|
||||
* @param queryWrapper
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
boolean deleteEntities(Wrapper queryWrapper) throws Exception;
|
||||
|
||||
/**
|
||||
* 获取符合条件的entity记录总数
|
||||
* @return
|
||||
*/
|
||||
int getEntityListCount(Wrapper queryWrapper);
|
||||
|
||||
/**
|
||||
* 获取指定条件的Entity集合
|
||||
* @param queryWrapper
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
List<T> getEntityList(Wrapper queryWrapper);
|
||||
|
||||
/**
|
||||
* 获取指定条件的Entity集合
|
||||
* @param queryWrapper
|
||||
* @param pagination
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
List<T> getEntityList(Wrapper queryWrapper, Pagination pagination);
|
||||
|
||||
/**
|
||||
* 获取指定条件的Entity集合
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
List<T> getEntityListByIds(List ids);
|
||||
|
||||
/**
|
||||
* 获取指定数量的entity记录
|
||||
* @param queryWrapper
|
||||
* @param limitCount
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
List<T> getEntityListLimit(Wrapper queryWrapper, int limitCount);
|
||||
|
||||
/**
|
||||
* 获取指定属性的Map列表
|
||||
* @param queryWrapper
|
||||
* @return
|
||||
*/
|
||||
List<Map<String, Object>> getMapList(Wrapper queryWrapper);
|
||||
|
||||
/**
|
||||
* 获取指定属性的Map列表
|
||||
* @param queryWrapper
|
||||
* @param pagination
|
||||
* @return
|
||||
*/
|
||||
List<Map<String, Object>> getMapList(Wrapper queryWrapper, Pagination pagination);
|
||||
|
||||
/***
|
||||
* 获取键值对的列表,用于构建select下拉选项等
|
||||
*
|
||||
* @param queryWrapper
|
||||
* @return
|
||||
*/
|
||||
List<KeyValue> getKeyValueList(Wrapper queryWrapper);
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.diboot.commons.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.diboot.commons.entity.BaseFile;
|
||||
import com.diboot.commons.mapper.BaseFileMapper;
|
||||
import com.diboot.commons.service.BaseFileService;
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,267 @@
|
|||
package com.diboot.commons.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
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.commons.config.BaseConfig;
|
||||
import com.diboot.commons.config.BaseCons;
|
||||
import com.diboot.commons.service.BaseService;
|
||||
import com.diboot.commons.utils.S;
|
||||
import com.diboot.commons.utils.V;
|
||||
import com.diboot.commons.vo.KeyValue;
|
||||
import com.diboot.commons.vo.Pagination;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Lishuaifei
|
||||
* @description 文件实现类
|
||||
* @creatime 2019-07-18 15:29
|
||||
*/
|
||||
public class BaseServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements BaseService<T> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BaseServiceImpl.class);
|
||||
|
||||
/***
|
||||
* 获取当前的Mapper对象
|
||||
* @return
|
||||
*/
|
||||
protected M getMapper(){
|
||||
return baseMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getEntity(Serializable id) {
|
||||
return super.getById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createEntity(T entity) {
|
||||
if(entity == null){
|
||||
warning("createModel", "参数entity为null");
|
||||
return false;
|
||||
}
|
||||
return super.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean createEntities(Collection entityList) {
|
||||
if(V.isEmpty(entityList)){
|
||||
warning("createEntities", "参数entityList为空!");
|
||||
return false;
|
||||
}
|
||||
// 批量插入
|
||||
return super.saveBatch(entityList, BaseConfig.getBatchSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateEntity(T entity) {
|
||||
return super.updateById(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateEntity(T entity, Wrapper updateCriteria) {
|
||||
return super.update(entity, updateCriteria);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateEntity(Wrapper updateWrapper) {
|
||||
return super.update(null, updateWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createOrUpdateEntity(T entity) {
|
||||
return super.saveOrUpdate(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createOrUpdateEntities(Collection entityList) {
|
||||
if(V.isEmpty(entityList)){
|
||||
warning("createOrUpdateEntities", "参数entityList为空!");
|
||||
return false;
|
||||
}
|
||||
// 批量插入
|
||||
return super.saveOrUpdateBatch(entityList, BaseConfig.getBatchSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteEntity(Serializable id) {
|
||||
return super.removeById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteEntities(Wrapper queryWrapper) throws Exception {
|
||||
return super.remove(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityListCount(Wrapper queryWrapper) {
|
||||
return super.count(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getEntityList(Wrapper queryWrapper) {
|
||||
return getEntityList(queryWrapper, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getEntityList(Wrapper queryWrapper, Pagination pagination) {
|
||||
if(pagination != null){
|
||||
IPage<T> page = convertToIPage(pagination);
|
||||
page = super.page(page, queryWrapper);
|
||||
// 如果重新执行了count进行查询,则更新pagination中的总数
|
||||
if(page.isSearchCount()){
|
||||
pagination.setTotalCount(page.getTotal());
|
||||
}
|
||||
return page.getRecords();
|
||||
}
|
||||
else{
|
||||
List<T> list = super.list(queryWrapper);
|
||||
if(list == null){
|
||||
list = Collections.emptyList();
|
||||
}
|
||||
else if(list.size() > BaseConfig.getBatchSize()){
|
||||
log.warn("单次查询记录数量过大,返回结果数={}", list.size());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getEntityListByIds(List ids) {
|
||||
QueryWrapper<T> queryWrapper = new QueryWrapper();
|
||||
queryWrapper.in(BaseCons.FieldName.id.name(), ids);
|
||||
return getEntityList(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getEntityListLimit(Wrapper queryWrapper, int limitCount) {
|
||||
IPage<T> page = new Page<>(1, limitCount);
|
||||
page = super.page(page, queryWrapper);
|
||||
return page.getRecords();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> getMapList(Wrapper queryWrapper) {
|
||||
return getMapList(queryWrapper, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> getMapList(Wrapper queryWrapper, Pagination pagination) {
|
||||
if(pagination != null){
|
||||
IPage<T> page = convertToIPage(pagination);
|
||||
IPage<Map<String, Object>> resultPage = super.pageMaps(page, queryWrapper);
|
||||
// 如果重新执行了count进行查询,则更新pagination中的总数
|
||||
if(page.isSearchCount()){
|
||||
pagination.setTotalCount(page.getTotal());
|
||||
}
|
||||
return resultPage.getRecords();
|
||||
}
|
||||
else{
|
||||
List<Map<String, Object>> list = super.listMaps(queryWrapper);
|
||||
if(list == null){
|
||||
list = Collections.emptyList();
|
||||
}
|
||||
else if(list.size() > BaseConfig.getBatchSize()){
|
||||
log.warn("单次查询记录数量过大,返回结果数={}", list.size());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyValue> getKeyValueList(Wrapper queryWrapper) {
|
||||
String sqlSelect = queryWrapper.getSqlSelect();
|
||||
// 最多支持3个属性:k, v, ext
|
||||
if(V.isEmpty(sqlSelect) || S.countMatches(sqlSelect, BaseCons.SEPARATOR_COMMA) > 2){
|
||||
log.error("调用错误: getKeyValueList必须用select依次指定返回的Key,Value, ext键值字段,如: new QueryWrapper<Dictionary>().lambda().select(Dictionary::getItemName, Dictionary::getItemValue)");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 获取mapList
|
||||
List<Map<String, Object>> mapList = super.listMaps(queryWrapper);
|
||||
if(mapList == null){
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 转换为Key-Value键值对
|
||||
String[] keyValueArray = sqlSelect.split(BaseCons.SEPARATOR_COMMA);
|
||||
List<KeyValue> keyValueList = new ArrayList<>(mapList.size());
|
||||
for(Map<String, Object> map : mapList){
|
||||
if(map.get(keyValueArray[0]) != null){
|
||||
KeyValue kv = new KeyValue((String)map.get(keyValueArray[0]), map.get(keyValueArray[1]));
|
||||
if(keyValueArray.length > 2){
|
||||
kv.setExt(map.get(keyValueArray[2]));
|
||||
}
|
||||
keyValueList.add(kv);
|
||||
}
|
||||
}
|
||||
return keyValueList;
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public <VO> List<VO> getViewObjectList(Wrapper queryWrapper, Pagination pagination, Class<VO> aClass) {
|
||||
List<T> entityList = getEntityList(queryWrapper, pagination);
|
||||
// 自动转换为VO并绑定关联对象
|
||||
List<VO> voList = RelationsBinder.convertAndBind(entityList, voClass);
|
||||
return null;
|
||||
}*/
|
||||
|
||||
/*@Override
|
||||
public <VO> VO getViewObject(Serializable id, Class<VO> aClass) {
|
||||
T entity = getEntity(id);
|
||||
if(entity == null){
|
||||
return null;
|
||||
}
|
||||
List<T> enityList = new ArrayList<>();
|
||||
enityList.add(entity);
|
||||
// 绑定
|
||||
List<VO> voList = RelationsBinder.convertAndBind(enityList, voClass);
|
||||
return null;
|
||||
}*/
|
||||
|
||||
/***
|
||||
* 转换为IPage
|
||||
* @param pagination
|
||||
* @return
|
||||
*/
|
||||
protected Page<T> convertToIPage(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;
|
||||
}
|
||||
|
||||
/***
|
||||
* 打印警告信息
|
||||
* @param method
|
||||
* @param message
|
||||
*/
|
||||
private void warning(String method, String message){
|
||||
log.warn(this.getClass().getName() + "."+ method +" 调用错误: "+message+", 请检查!");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,616 @@
|
|||
package com.diboot.commons.utils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/** A very fast and memory efficient class to encode and decode to and from BASE64 in full accordance
|
||||
* with RFC 2045.<br><br>
|
||||
* On Windows XP sp1 with 1.4.2_04 and later ;), this encoder and decoder is about 10 times faster
|
||||
* on small arrays (10 - 1000 bytes) and 2-3 times as fast on larger arrays (10000 - 1000000 bytes)
|
||||
* compared to <code>sun.misc.Encoder()/Decoder()</code>.<br><br>
|
||||
*
|
||||
* On byte arrays the encoder is about 20% faster than Jakarta Commons Base64 Codec for encode and
|
||||
* about 50% faster for decoding large arrays. This implementation is about twice as fast on very small
|
||||
* arrays (< 30 bytes). If source/destination is a <code>String</code> this
|
||||
* version is about three times as fast due to the fact that the Commons Codec result has to be recoded
|
||||
* to a <code>String</code> from <code>byte[]</code>, which is very expensive.<br><br>
|
||||
*
|
||||
* This encode/decode algorithm doesn't create any temporary arrays as many other codecs do, it only
|
||||
* allocates the resulting array. This produces less garbage and it is possible to handle arrays twice
|
||||
* as large as algorithms that create a temporary array. (E.g. Jakarta Commons Codec). It is unknown
|
||||
* whether Sun's <code>sun.misc.Encoder()/Decoder()</code> produce temporary arrays but since performance
|
||||
* is quite low it probably does.<br><br>
|
||||
*
|
||||
* The encoder produces the same output as the Sun one except that the Sun's encoder appends
|
||||
* a trailing line separator if the last character isn't a pad. Unclear why but it only adds to the
|
||||
* length and is probably a side effect. Both are in conformance with RFC 2045 though.<br>
|
||||
* Commons codec seem to always att a trailing line separator.<br><br>
|
||||
*
|
||||
* <b>Note!</b>
|
||||
* The encode/decode method pairs (types) come in three versions with the <b>exact</b> same algorithm and
|
||||
* thus a lot of code redundancy. This is to not create any temporary arrays for transcoding to/from different
|
||||
* format types. The methods not used can simply be commented out.<br><br>
|
||||
*
|
||||
* There is also a "fast" version of all decode methods that works the same way as the normal ones, but
|
||||
* har a few demands on the decoded input. Normally though, these fast verions should be used if the source if
|
||||
* the input is known and it hasn't bee tampered with.<br><br>
|
||||
*
|
||||
* If you find the code useful or you find a bug, please send me a note at base64 @ miginfocom . com.
|
||||
*
|
||||
* Licence (BSD):
|
||||
* ==============
|
||||
*
|
||||
* Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 @ miginfocom . com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list
|
||||
* of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
* Neither the name of the MiG InfoCom AB nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* @version 2.2
|
||||
* @author Mikael Grev
|
||||
* Date: 2004-aug-02
|
||||
* Time: 11:31:11
|
||||
*/
|
||||
|
||||
public class Base64
|
||||
{
|
||||
private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
|
||||
private static final int[] IA = new int[256];
|
||||
static {
|
||||
Arrays.fill(IA, -1);
|
||||
for (int i = 0, iS = CA.length; i < iS; i++) {
|
||||
IA[CA[i]] = i;
|
||||
}
|
||||
IA['='] = 0;
|
||||
}
|
||||
|
||||
// ****************************************************************************************
|
||||
// * char[] version
|
||||
// ****************************************************************************************
|
||||
|
||||
/** Encodes a raw byte array into a BASE64 <code>char[]</code> representation i accordance with RFC 2045.
|
||||
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
|
||||
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||
* little faster.
|
||||
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||
*/
|
||||
public final static char[] encodeToChar(byte[] sArr, boolean lineSep)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = sArr != null ? sArr.length : 0;
|
||||
if (sLen == 0) {
|
||||
return new char[0];
|
||||
}
|
||||
|
||||
int eLen = (sLen / 3) * 3; // Length of even 24-bits.
|
||||
int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count
|
||||
int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
|
||||
char[] dArr = new char[dLen];
|
||||
|
||||
// Encode even 24-bits
|
||||
for (int s = 0, d = 0, cc = 0; s < eLen;) {
|
||||
// Copy next three bytes into lower 24 bits of int, paying attension to sign.
|
||||
int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);
|
||||
|
||||
// Encode the int into four chars
|
||||
dArr[d++] = CA[(i >>> 18) & 0x3f];
|
||||
dArr[d++] = CA[(i >>> 12) & 0x3f];
|
||||
dArr[d++] = CA[(i >>> 6) & 0x3f];
|
||||
dArr[d++] = CA[i & 0x3f];
|
||||
|
||||
// Add optional line separator
|
||||
if (lineSep && ++cc == 19 && d < dLen - 2) {
|
||||
dArr[d++] = '\r';
|
||||
dArr[d++] = '\n';
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Pad and encode last bits if source isn't even 24 bits.
|
||||
int left = sLen - eLen; // 0 - 2.
|
||||
if (left > 0) {
|
||||
// Prepare the int
|
||||
int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);
|
||||
|
||||
// Set last four chars
|
||||
dArr[dLen - 4] = CA[i >> 12];
|
||||
dArr[dLen - 3] = CA[(i >>> 6) & 0x3f];
|
||||
dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '=';
|
||||
dArr[dLen - 1] = '=';
|
||||
}
|
||||
return dArr;
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with
|
||||
* and without line separators.
|
||||
* @param sArr The source array. <code>null</code> or length 0 will return an empty array.
|
||||
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||
*/
|
||||
public final static byte[] decode(char[] sArr)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = sArr != null ? sArr.length : 0;
|
||||
if (sLen == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
// Count illegal characters (including '\r', '\n') to know what size the returned array will be,
|
||||
// so we don't have to reallocate & copy it later.
|
||||
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
|
||||
for (int i = 0; i < sLen; i++) { // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
|
||||
if (IA[sArr[i]] < 0) {
|
||||
sepCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
|
||||
if ((sLen - sepCnt) % 4 != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int pad = 0;
|
||||
for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0;) {
|
||||
if (sArr[i] == '=') {
|
||||
pad++;
|
||||
}
|
||||
}
|
||||
|
||||
int len = ((sLen - sepCnt) * 6 >> 3) - pad;
|
||||
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
for (int s = 0, d = 0; d < len;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = 0;
|
||||
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
|
||||
int c = IA[sArr[s++]];
|
||||
if (c >= 0) {
|
||||
i |= c << (18 - j * 6);
|
||||
} else {
|
||||
j--;
|
||||
}
|
||||
}
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
if (d < len) {
|
||||
dArr[d++]= (byte) (i >> 8);
|
||||
if (d < len) {
|
||||
dArr[d++] = (byte) i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dArr;
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as
|
||||
* fast as {@link #decode(char[])}. The preconditions are:<br>
|
||||
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
|
||||
* + Line separator must be "\r\n", as specified in RFC 2045
|
||||
* + The array must not contain illegal characters within the encoded string<br>
|
||||
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
|
||||
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||
* @return The decoded array of bytes. May be of length 0.
|
||||
*/
|
||||
public final static byte[] decodeFast(char[] sArr)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = sArr.length;
|
||||
if (sLen == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
|
||||
|
||||
// Trim illegal chars from start
|
||||
while (sIx < eIx && IA[sArr[sIx]] < 0) {
|
||||
sIx++;
|
||||
}
|
||||
|
||||
// Trim illegal chars from end
|
||||
while (eIx > 0 && IA[sArr[eIx]] < 0) {
|
||||
eIx--;
|
||||
}
|
||||
|
||||
// get the padding count (=) (0, 1 or 2)
|
||||
int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end.
|
||||
int cCnt = eIx - sIx + 1; // Content count including possible separators
|
||||
int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
|
||||
|
||||
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
// Decode all but the last 0 - 2 bytes.
|
||||
int d = 0;
|
||||
for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
|
||||
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
dArr[d++] = (byte) (i >> 8);
|
||||
dArr[d++] = (byte) i;
|
||||
|
||||
// If line separator, jump over it.
|
||||
if (sepCnt > 0 && ++cc == 19) {
|
||||
sIx += 2;
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < len) {
|
||||
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
|
||||
int i = 0;
|
||||
for (int j = 0; sIx <= eIx - pad; j++) {
|
||||
i |= IA[sArr[sIx++]] << (18 - j * 6);
|
||||
}
|
||||
|
||||
for (int r = 16; d < len; r -= 8) {
|
||||
dArr[d++] = (byte) (i >> r);
|
||||
}
|
||||
}
|
||||
|
||||
return dArr;
|
||||
}
|
||||
|
||||
// ****************************************************************************************
|
||||
// * byte[] version
|
||||
// ****************************************************************************************
|
||||
|
||||
/** Encodes a raw byte array into a BASE64 <code>byte[]</code> representation i accordance with RFC 2045.
|
||||
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
|
||||
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||
* little faster.
|
||||
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||
*/
|
||||
public final static byte[] encodeToByte(byte[] sArr, boolean lineSep)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = sArr != null ? sArr.length : 0;
|
||||
if (sLen == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
int eLen = (sLen / 3) * 3; // Length of even 24-bits.
|
||||
int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count
|
||||
int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
|
||||
byte[] dArr = new byte[dLen];
|
||||
|
||||
// Encode even 24-bits
|
||||
for (int s = 0, d = 0, cc = 0; s < eLen;) {
|
||||
// Copy next three bytes into lower 24 bits of int, paying attension to sign.
|
||||
int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);
|
||||
|
||||
// Encode the int into four chars
|
||||
dArr[d++] = (byte) CA[(i >>> 18) & 0x3f];
|
||||
dArr[d++] = (byte) CA[(i >>> 12) & 0x3f];
|
||||
dArr[d++] = (byte) CA[(i >>> 6) & 0x3f];
|
||||
dArr[d++] = (byte) CA[i & 0x3f];
|
||||
|
||||
// Add optional line separator
|
||||
if (lineSep && ++cc == 19 && d < dLen - 2) {
|
||||
dArr[d++] = '\r';
|
||||
dArr[d++] = '\n';
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Pad and encode last bits if source isn't an even 24 bits.
|
||||
int left = sLen - eLen; // 0 - 2.
|
||||
if (left > 0) {
|
||||
// Prepare the int
|
||||
int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);
|
||||
|
||||
// Set last four chars
|
||||
dArr[dLen - 4] = (byte) CA[i >> 12];
|
||||
dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f];
|
||||
dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '=';
|
||||
dArr[dLen - 1] = '=';
|
||||
}
|
||||
return dArr;
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with
|
||||
* and without line separators.
|
||||
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||
*/
|
||||
public final static byte[] decode(byte[] sArr)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = sArr.length;
|
||||
|
||||
// Count illegal characters (including '\r', '\n') to know what size the returned array will be,
|
||||
// so we don't have to reallocate & copy it later.
|
||||
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
|
||||
for (int i = 0; i < sLen; i++) { // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
|
||||
if (IA[sArr[i] & 0xff] < 0) {
|
||||
sepCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
|
||||
if ((sLen - sepCnt) % 4 != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int pad = 0;
|
||||
for (int i = sLen; i > 1 && IA[sArr[--i] & 0xff] <= 0;) {
|
||||
if (sArr[i] == '=') {
|
||||
pad++;
|
||||
}
|
||||
}
|
||||
|
||||
int len = ((sLen - sepCnt) * 6 >> 3) - pad;
|
||||
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
for (int s = 0, d = 0; d < len;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = 0;
|
||||
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
|
||||
int c = IA[sArr[s++] & 0xff];
|
||||
if (c >= 0) {
|
||||
i |= c << (18 - j * 6);
|
||||
} else {
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
if (d < len) {
|
||||
dArr[d++]= (byte) (i >> 8);
|
||||
if (d < len) {
|
||||
dArr[d++] = (byte) i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dArr;
|
||||
}
|
||||
|
||||
|
||||
/** Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as
|
||||
* fast as {@link #decode(byte[])}. The preconditions are:<br>
|
||||
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
|
||||
* + Line separator must be "\r\n", as specified in RFC 2045
|
||||
* + The array must not contain illegal characters within the encoded string<br>
|
||||
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
|
||||
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||
* @return The decoded array of bytes. May be of length 0.
|
||||
*/
|
||||
public final static byte[] decodeFast(byte[] sArr)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = sArr.length;
|
||||
if (sLen == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
|
||||
|
||||
// Trim illegal chars from start
|
||||
while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0) {
|
||||
sIx++;
|
||||
}
|
||||
|
||||
// Trim illegal chars from end
|
||||
while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0) {
|
||||
eIx--;
|
||||
}
|
||||
|
||||
// get the padding count (=) (0, 1 or 2)
|
||||
int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end.
|
||||
int cCnt = eIx - sIx + 1; // Content count including possible separators
|
||||
int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
|
||||
|
||||
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
// Decode all but the last 0 - 2 bytes.
|
||||
int d = 0;
|
||||
for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
|
||||
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
dArr[d++] = (byte) (i >> 8);
|
||||
dArr[d++] = (byte) i;
|
||||
|
||||
// If line separator, jump over it.
|
||||
if (sepCnt > 0 && ++cc == 19) {
|
||||
sIx += 2;
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < len) {
|
||||
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
|
||||
int i = 0;
|
||||
for (int j = 0; sIx <= eIx - pad; j++) {
|
||||
i |= IA[sArr[sIx++]] << (18 - j * 6);
|
||||
}
|
||||
|
||||
for (int r = 16; d < len; r -= 8) {
|
||||
dArr[d++] = (byte) (i >> r);
|
||||
}
|
||||
}
|
||||
|
||||
return dArr;
|
||||
}
|
||||
|
||||
// ****************************************************************************************
|
||||
// * String version
|
||||
// ****************************************************************************************
|
||||
|
||||
/** Encodes a raw byte array into a BASE64 <code>String</code> representation i accordance with RFC 2045.
|
||||
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
|
||||
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||
* little faster.
|
||||
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||
*/
|
||||
public final static String encodeToString(byte[] sArr, boolean lineSep)
|
||||
{
|
||||
// Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower.
|
||||
return new String(encodeToChar(sArr, lineSep));
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded <code>String</code>. All illegal characters will be ignored and can handle both strings with
|
||||
* and without line separators.<br>
|
||||
* <b>Note!</b> It can be up to about 2x the speed to call <code>decode(str.toCharArray())</code> instead. That
|
||||
* will create a temporary array though. This version will use <code>str.charAt(i)</code> to iterate the string.
|
||||
* @param str The source string. <code>null</code> or length 0 will return an empty array.
|
||||
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||
*/
|
||||
public final static byte[] decode(String str)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = str != null ? str.length() : 0;
|
||||
if (sLen == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
// Count illegal characters (including '\r', '\n') to know what size the returned array will be,
|
||||
// so we don't have to reallocate & copy it later.
|
||||
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
|
||||
for (int i = 0; i < sLen; i++) { // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
|
||||
if (IA[str.charAt(i)] < 0) {
|
||||
sepCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
|
||||
if ((sLen - sepCnt) % 4 != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Count '=' at end
|
||||
int pad = 0;
|
||||
for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0;) {
|
||||
if (str.charAt(i) == '=') {
|
||||
pad++;
|
||||
}
|
||||
}
|
||||
|
||||
int len = ((sLen - sepCnt) * 6 >> 3) - pad;
|
||||
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
for (int s = 0, d = 0; d < len;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = 0;
|
||||
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
|
||||
int c = IA[str.charAt(s++)];
|
||||
if (c >= 0) {
|
||||
i |= c << (18 - j * 6);
|
||||
} else {
|
||||
j--;
|
||||
}
|
||||
}
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
if (d < len) {
|
||||
dArr[d++]= (byte) (i >> 8);
|
||||
if (d < len) {
|
||||
dArr[d++] = (byte) i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dArr;
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as
|
||||
* fast as {@link #decode(String)}. The preconditions are:<br>
|
||||
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
|
||||
* + Line separator must be "\r\n", as specified in RFC 2045
|
||||
* + The array must not contain illegal characters within the encoded string<br>
|
||||
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
|
||||
* @param s The source string. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||
* @return The decoded array of bytes. May be of length 0.
|
||||
*/
|
||||
public final static byte[] decodeFast(String s)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = s.length();
|
||||
if (sLen == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
|
||||
|
||||
// Trim illegal chars from start
|
||||
while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0) {
|
||||
sIx++;
|
||||
}
|
||||
|
||||
// Trim illegal chars from end
|
||||
while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0) {
|
||||
eIx--;
|
||||
}
|
||||
|
||||
// get the padding count (=) (0, 1 or 2)
|
||||
int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end.
|
||||
int cCnt = eIx - sIx + 1; // Content count including possible separators
|
||||
int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
|
||||
|
||||
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
// Decode all but the last 0 - 2 bytes.
|
||||
int d = 0;
|
||||
for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)];
|
||||
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
dArr[d++] = (byte) (i >> 8);
|
||||
dArr[d++] = (byte) i;
|
||||
|
||||
// If line separator, jump over it.
|
||||
if (sepCnt > 0 && ++cc == 19) {
|
||||
sIx += 2;
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < len) {
|
||||
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
|
||||
int i = 0;
|
||||
for (int j = 0; sIx <= eIx - pad; j++) {
|
||||
i |= IA[s.charAt(sIx++)] << (18 - j * 6);
|
||||
}
|
||||
|
||||
for (int r = 16; d < len; r -= 8) {
|
||||
dArr[d++] = (byte) (i >> r);
|
||||
}
|
||||
}
|
||||
|
||||
return dArr;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,731 @@
|
|||
package com.diboot.commons.utils;
|
||||
|
||||
import com.diboot.commons.config.BaseCons;
|
||||
import com.diboot.commons.entity.BaseEntity;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
import org.springframework.cglib.beans.BeanCopier;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.Serializable;
|
||||
import java.lang.invoke.SerializedLambda;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Bean相关处理工具类
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/01/01
|
||||
*/
|
||||
public class BeanUtils {
|
||||
private static final Logger log = LoggerFactory.getLogger(BeanUtils.class);
|
||||
|
||||
/**
|
||||
* 连接符号
|
||||
*/
|
||||
private static final String CHANGE_FLAG = "->";
|
||||
|
||||
/**
|
||||
* 忽略对比的字段
|
||||
*/
|
||||
private static final Set<String> IGNORE_FIELDS = new HashSet<String>(){{
|
||||
add("createTime");
|
||||
}};
|
||||
|
||||
/***
|
||||
* 缓存BeanCopier
|
||||
*/
|
||||
private static Map<String, BeanCopier> BEAN_COPIER_INST_MAP = new ConcurrentHashMap<>();
|
||||
/***
|
||||
* 缓存类-Lambda的映射关系
|
||||
*/
|
||||
private static Map<Class, SerializedLambda> CLASS_LAMDBA_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
/***
|
||||
* 获取实例
|
||||
* @param source
|
||||
* @param target
|
||||
* @return
|
||||
*/
|
||||
private static BeanCopier getBeanCopierInstance(Object source, Object target){
|
||||
//build key
|
||||
String beanCopierKey = source.getClass().toString() +"_"+ target.getClass().toString();
|
||||
BeanCopier copierInst = BEAN_COPIER_INST_MAP.get(beanCopierKey);
|
||||
if(copierInst == null){
|
||||
copierInst = BeanCopier.create(source.getClass(), target.getClass(), false);
|
||||
BEAN_COPIER_INST_MAP.put(beanCopierKey, copierInst);
|
||||
}
|
||||
return copierInst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy属性到另一个对象
|
||||
* @param source
|
||||
* @param target
|
||||
*/
|
||||
public static Object copyProperties(Object source, Object target){
|
||||
BeanCopier copierInst = getBeanCopierInstance(source, target);
|
||||
copierInst.copy(source, target, null);
|
||||
return target;
|
||||
}
|
||||
|
||||
/***
|
||||
* 将对象转换为另外的对象实例
|
||||
* @param source
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> T convert(Object source, Class<T> clazz){
|
||||
if(source == null){
|
||||
return null;
|
||||
}
|
||||
T target = null;
|
||||
try{
|
||||
target = clazz.getConstructor().newInstance();
|
||||
copyProperties(source, target);
|
||||
}
|
||||
catch (Exception e){
|
||||
log.warn("对象转换异常, class="+clazz.getName());
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/***
|
||||
* 将对象转换为另外的对象实例
|
||||
* @param sourceList
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> convertList(List sourceList, Class<T> clazz){
|
||||
if(V.isEmpty(sourceList)){
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<T> resultList = new ArrayList<>();
|
||||
// 类型相同,直接跳过
|
||||
if(clazz.getName().equals(sourceList.get(0).getClass().getName())){
|
||||
return sourceList;
|
||||
}
|
||||
// 不同,则转换
|
||||
try{
|
||||
for(Object source : sourceList){
|
||||
T target = clazz.getConstructor().newInstance();
|
||||
copyProperties(source, target);
|
||||
resultList.add(target);
|
||||
}
|
||||
}
|
||||
catch (Exception e){
|
||||
log.warn("对象转换异常, class="+clazz.getName());
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/***
|
||||
* 附加Map中的属性值到Model
|
||||
* @param model
|
||||
* @param propMap
|
||||
*/
|
||||
public static void bindProperties(Object model, Map<String, Object> propMap){
|
||||
try{// 获取类属性
|
||||
BeanInfo beanInfo = Introspector.getBeanInfo(model.getClass());
|
||||
// 给 JavaBean 对象的属性赋值
|
||||
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
|
||||
for (PropertyDescriptor descriptor : propertyDescriptors) {
|
||||
String propertyName = descriptor.getName();
|
||||
if (!propMap.containsKey(propertyName)){
|
||||
continue;
|
||||
}
|
||||
Object value = propMap.get(propertyName);
|
||||
Class type = descriptor.getWriteMethod().getParameterTypes()[0];
|
||||
Object[] args = new Object[1];
|
||||
String fieldType = type.getName();
|
||||
// 类型不一致,需转型
|
||||
if(!value.getClass().getTypeName().equals(fieldType)){
|
||||
if(value instanceof String){
|
||||
// String to Date
|
||||
if(fieldType.equalsIgnoreCase(Date.class.getName())){
|
||||
args[0] = D.fuzzyConvert((String)value);
|
||||
}
|
||||
// Map中的String型转换为其他型
|
||||
else if(fieldType.equalsIgnoreCase(Boolean.class.getName())){
|
||||
args[0] = V.isTrue((String)value);
|
||||
}
|
||||
else if (fieldType.equalsIgnoreCase(Integer.class.getName()) || "int".equals(fieldType)) {
|
||||
args[0] = Integer.parseInt((String)value);
|
||||
}
|
||||
else if (fieldType.equalsIgnoreCase(Long.class.getName())) {
|
||||
args[0] = Long.parseLong((String)value);
|
||||
}
|
||||
else if (fieldType.equalsIgnoreCase(Double.class.getName())) {
|
||||
args[0] = Double.parseDouble((String)value);
|
||||
}
|
||||
else if (fieldType.equalsIgnoreCase(Float.class.getName())) {
|
||||
args[0] = Float.parseFloat((String)value);
|
||||
}
|
||||
else{
|
||||
args[0] = value;
|
||||
log.warn("类型不一致,暂无法自动绑定,请手动转型一致后调用!字段类型: {} vs {} ", value.getClass().getTypeName(), fieldType);
|
||||
}
|
||||
}
|
||||
else{
|
||||
args[0] = value;
|
||||
log.warn("类型不一致,且Map中的value非String类型,暂无法自动绑定,请手动转型一致后调用! {} vs {} ", value.getClass().getTypeName(), fieldType);
|
||||
}
|
||||
}
|
||||
else{
|
||||
args[0] = value;
|
||||
}
|
||||
descriptor.getWriteMethod().invoke(model, args);
|
||||
}
|
||||
}
|
||||
catch (Exception e){
|
||||
log.warn("复制Map属性到Model异常: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取对象的属性值
|
||||
* @param obj
|
||||
* @param field
|
||||
* @return
|
||||
*/
|
||||
public static Object getProperty(Object obj, String field){
|
||||
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(obj);
|
||||
return wrapper.getPropertyValue(field);
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取对象的属性值并转换为String
|
||||
* @param obj
|
||||
* @param field
|
||||
* @return
|
||||
*/
|
||||
public static String getStringProperty(Object obj, String field){
|
||||
Object property = getProperty(obj, field);
|
||||
if(property == null){
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(property);
|
||||
}
|
||||
|
||||
/***
|
||||
* 设置属性值
|
||||
* @param obj
|
||||
* @param field
|
||||
* @param value
|
||||
*/
|
||||
public static void setProperty(Object obj, String field, Object value) {
|
||||
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(obj);
|
||||
wrapper.setPropertyValue(field, value);
|
||||
}
|
||||
|
||||
/***
|
||||
* Key-Model对象Map
|
||||
* @param allLists
|
||||
* @return
|
||||
*/
|
||||
public static <T> Map<Object, T> convert2KeyModelMap(List<T> allLists, String... fields){
|
||||
if(allLists == null || allLists.isEmpty()){
|
||||
return null;
|
||||
}
|
||||
Map<Object, T> allListMap = new LinkedHashMap<>(allLists.size());
|
||||
// 转换为map
|
||||
try{
|
||||
for(T model : allLists){
|
||||
Object key = null;
|
||||
if(V.isEmpty(fields)){ //未指定字段,以id为key
|
||||
key = getProperty(model, BaseCons.FieldName.id.name());
|
||||
}
|
||||
else if(fields.length == 1){ // 指定了一个字段,以该字段为key,类型同该字段
|
||||
key = getProperty(model, fields[0]);
|
||||
}
|
||||
else{ // 指定了多个字段,以字段S.join的结果为key,类型为String
|
||||
List list = new ArrayList();
|
||||
for(String fld : fields){
|
||||
list.add(getProperty(model, fld));
|
||||
}
|
||||
key = S.join(list);
|
||||
}
|
||||
if(key != null){
|
||||
allListMap.put(key, model);
|
||||
}
|
||||
else{
|
||||
log.warn(model.getClass().getName() + " 的属性 "+fields[0]+" 值存在 null,BeanUtils.convert2KeyModelMap转换结果需要确认!");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e){
|
||||
log.warn("转换key-model异常", e);
|
||||
}
|
||||
return allListMap;
|
||||
}
|
||||
|
||||
/***
|
||||
* Key-Object对象Map
|
||||
* @param allLists
|
||||
* @return
|
||||
*/
|
||||
public static <T> Map<String, T> convertToStringKeyObjectMap(List<T> allLists, String... fields){
|
||||
if(allLists == null || allLists.isEmpty()){
|
||||
return null;
|
||||
}
|
||||
Map<String, T> allListMap = new LinkedHashMap<>(allLists.size());
|
||||
// 转换为map
|
||||
try{
|
||||
for(T model : allLists){
|
||||
String key = null;
|
||||
if(V.isEmpty(fields)){
|
||||
//未指定字段,以id为key
|
||||
key = getStringProperty(model, BaseCons.FieldName.id.name());
|
||||
}
|
||||
// 指定了一个字段,以该字段为key,类型同该字段
|
||||
else if(fields.length == 1){
|
||||
key = getStringProperty(model, fields[0]);
|
||||
}
|
||||
else{ // 指定了多个字段,以字段S.join的结果为key,类型为String
|
||||
List list = new ArrayList();
|
||||
for(String fld : fields){
|
||||
list.add(getProperty(model, fld));
|
||||
}
|
||||
key = S.join(list);
|
||||
}
|
||||
if(key != null){
|
||||
allListMap.put(key, model);
|
||||
}
|
||||
else{
|
||||
log.warn(model.getClass().getName() + " 的属性 "+fields[0]+" 值存在 null,转换结果需要确认!");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e){
|
||||
log.warn("转换key-model异常", e);
|
||||
}
|
||||
return allListMap;
|
||||
}
|
||||
|
||||
/***
|
||||
* Key-Object-List列表Map
|
||||
* @param allLists
|
||||
* @param fields
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> Map<String, List<T>> convertToStringKeyObjectListMap(List<T> allLists, String... fields){
|
||||
if (allLists == null || allLists.isEmpty()){
|
||||
return null;
|
||||
}
|
||||
Map<String, List<T>> allListMap = new LinkedHashMap<>(allLists.size());
|
||||
// 转换为map
|
||||
try {
|
||||
for (T model : allLists){
|
||||
String key = null;
|
||||
if(V.isEmpty(fields)){
|
||||
//未指定字段,以id为key
|
||||
key = getStringProperty(model, BaseCons.FieldName.id.name());
|
||||
}
|
||||
// 指定了一个字段,以该字段为key,类型同该字段
|
||||
else if(fields.length == 1){
|
||||
key = getStringProperty(model, fields[0]);
|
||||
}
|
||||
else{ // 指定了多个字段,以字段S.join的结果为key,类型为String
|
||||
List list = new ArrayList();
|
||||
for(String fld : fields){
|
||||
list.add(getProperty(model, fld));
|
||||
}
|
||||
key = S.join(list);
|
||||
}
|
||||
if(key != null){
|
||||
List<T> list = allListMap.get(key);
|
||||
if (list == null){
|
||||
list = new ArrayList<T>();
|
||||
allListMap.put(key, list);
|
||||
}
|
||||
list.add(model);
|
||||
}
|
||||
else{
|
||||
log.warn(model.getClass().getName() + " 的属性 "+fields[0]+" 值存在 null,转换结果需要确认!");
|
||||
}
|
||||
}
|
||||
} catch (Exception e){
|
||||
log.warn("转换key-model-list异常", e);
|
||||
}
|
||||
|
||||
return allListMap;
|
||||
}
|
||||
|
||||
/***
|
||||
* 构建上下级关联的树形结构的model
|
||||
* @param allModels
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
/*public static <T extends BaseEntity> List<T> buildTree(List<T> allModels){
|
||||
if(V.isEmpty(allModels)){
|
||||
return null;
|
||||
}
|
||||
// 提取所有的top level对象
|
||||
List<T> topLevelModels = new ArrayList();
|
||||
for(T model : allModels){
|
||||
Object parentId = getProperty(model, BaseCons.FieldName.parentId.name());
|
||||
if(parentId == null || V.fuzzyEqual(parentId, 0)){
|
||||
topLevelModels.add(model);
|
||||
}
|
||||
}
|
||||
if(V.isEmpty(topLevelModels)){
|
||||
return topLevelModels;
|
||||
}
|
||||
// 提取向下一层的对象
|
||||
buildDeeperLevelTree(topLevelModels, allModels);
|
||||
// 返回第一层级节点(二级及以上子级通过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, BaseCons.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, BaseCons.FieldName.parentId.name()))
|
||||
&& !V.fuzzyEqual(model.getId(), getProperty(model, BaseCons.FieldName.parentId.name()))){ //解除自循环,如:实体的id==parentId的情况
|
||||
children.add(model);
|
||||
}
|
||||
}
|
||||
//递归调用
|
||||
buildTree(children, allModels);
|
||||
if(V.notEmpty(children)){
|
||||
setProperty(parent, BaseCons.FieldName.children.name(), children);
|
||||
}
|
||||
}
|
||||
|
||||
return parentModels;
|
||||
}*/
|
||||
|
||||
/***
|
||||
* 构建下一层级树形结构
|
||||
* @param parentModels
|
||||
* @param allModels
|
||||
* @param <T>
|
||||
*/
|
||||
/*private static <T extends BaseEntity> void buildDeeperLevelTree(List<T> parentModels, List<T> allModels){
|
||||
List<T> deeperLevelModels = new ArrayList();
|
||||
Map<String, T> parentLevelModelMap = convertToStringKeyObjectMap(parentModels, BaseCons.FieldName.id.name());
|
||||
for(T model : allModels){
|
||||
Object parentId = getProperty(model, BaseCons.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, BaseCons.FieldName.parentId.name());
|
||||
T parentModel = parentLevelModelMap.get(String.valueOf(parentId));
|
||||
if(parentModel!=null){
|
||||
List children = (List) getProperty(parentModel, BaseCons.FieldName.children.name());
|
||||
if(children == null){
|
||||
children = new ArrayList();
|
||||
setProperty(parentModel, BaseCons.FieldName.children.name(), children);
|
||||
}
|
||||
children.add(model);
|
||||
}
|
||||
}
|
||||
// 递归进入下一层级
|
||||
buildDeeperLevelTree(deeperLevelModels, allModels);
|
||||
}*/
|
||||
|
||||
/***
|
||||
* 提取两个model的差异值
|
||||
* @param oldModel
|
||||
* @param newModel
|
||||
* @return
|
||||
*/
|
||||
public static String extractDiff(BaseEntity oldModel, BaseEntity newModel){
|
||||
return extractDiff(oldModel, newModel, null);
|
||||
}
|
||||
|
||||
/***
|
||||
* 提取两个model的差异值,只对比指定字段
|
||||
* @param oldModel
|
||||
* @param newModel
|
||||
* @return
|
||||
*/
|
||||
public static String extractDiff(BaseEntity oldModel, BaseEntity newModel, Set<String> fields){
|
||||
if(newModel == null || oldModel == null){
|
||||
log.warn("调用错误,Model不能为空!");
|
||||
return null;
|
||||
}
|
||||
Map<String, Object> oldMap = oldModel.toMap();
|
||||
Map<String, Object> newMap = newModel.toMap();
|
||||
Map<String, Object> result = new HashMap<>(oldMap.size()+newMap.size());
|
||||
for(Map.Entry<String, Object> entry : oldMap.entrySet()){
|
||||
if(IGNORE_FIELDS.contains(entry.getKey())){
|
||||
continue;
|
||||
}
|
||||
String oldValue = entry.getValue()!=null ? String.valueOf(entry.getValue()) : "";
|
||||
Object newValueObj = newMap.get(entry.getKey());
|
||||
String newValue = newValueObj!=null? String.valueOf(newValueObj) : "";
|
||||
// 设置变更的值
|
||||
boolean checkThisField = fields == null || fields.contains(entry.getKey());
|
||||
if(checkThisField && !oldValue.equals(newValue)){
|
||||
result.put(entry.getKey(), S.join(oldValue, CHANGE_FLAG, newValue));
|
||||
}
|
||||
// 从新的map中移除该key
|
||||
if(newValueObj!=null){
|
||||
newMap.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
if(!newMap.isEmpty()){
|
||||
for(Map.Entry<String, Object> entry : newMap.entrySet()){
|
||||
if(IGNORE_FIELDS.contains(entry.getKey())){
|
||||
continue;
|
||||
}
|
||||
Object newValueObj = entry.getValue();
|
||||
String newValue = newValueObj!=null? String.valueOf(newValueObj) : "";
|
||||
// 设置变更的值
|
||||
if(fields==null || fields.contains(entry.getKey())){
|
||||
result.put(entry.getKey(), S.join("", CHANGE_FLAG, newValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
oldMap = null;
|
||||
newMap = null;
|
||||
// 转换结果为String
|
||||
return JSON.toJSONString(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从list对象列表中提取指定属性值到新的List
|
||||
* @param objectList 对象list
|
||||
* @param getterFn get方法
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
/* public static <E,T> List collectToList(List<E> objectList, IGetter<T> getterFn){
|
||||
if(V.isEmpty(objectList)){
|
||||
return Collections.emptyList();
|
||||
}
|
||||
String getterPropName = convertToFieldName(getterFn);
|
||||
return collectToList(objectList, getterPropName);
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 从list对象列表中提取Id主键值到新的List
|
||||
* @param objectList 对象list
|
||||
* @param <E>
|
||||
* @return
|
||||
*/
|
||||
public static <E> List collectIdToList(List<E> objectList){
|
||||
if(V.isEmpty(objectList)){
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return collectToList(objectList, BaseCons.FieldName.id.name());
|
||||
}
|
||||
|
||||
/***
|
||||
* 从list对象列表中提取指定属性值到新的List
|
||||
* @param objectList
|
||||
* @param getterPropName
|
||||
* @param <E>
|
||||
* @return
|
||||
*/
|
||||
public static <E> List collectToList(List<E> objectList, String getterPropName){
|
||||
List fieldValueList = new ArrayList();
|
||||
try{
|
||||
for(E object : objectList){
|
||||
Object fieldValue = getProperty(object, getterPropName);
|
||||
if(!fieldValueList.contains(fieldValue)){
|
||||
fieldValueList.add(fieldValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e){
|
||||
log.warn("提取属性值异常, getterPropName="+getterPropName, e);
|
||||
}
|
||||
return fieldValueList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定map中的属性值到list
|
||||
* @param setFieldFn
|
||||
* @param getFieldFun
|
||||
* @param fromList
|
||||
* @param valueMatchMap
|
||||
* @param <T1>
|
||||
*/
|
||||
/*public static <T1,T2,R,E> void bindPropValueOfList(ISetter<T1,R> setFieldFn, List<E> fromList, IGetter<T2> getFieldFun, Map valueMatchMap){
|
||||
if(V.isEmpty(fromList)){
|
||||
return;
|
||||
}
|
||||
// function转换为字段名
|
||||
String setterFieldName = convertToFieldName(setFieldFn), getterFieldName = convertToFieldName(getFieldFun);
|
||||
bindPropValueOfList(setterFieldName, fromList, getterFieldName, valueMatchMap);
|
||||
}*/
|
||||
|
||||
/***
|
||||
* 从对象集合提取某个属性值到list中
|
||||
* @param setterFieldName
|
||||
* @param fromList
|
||||
* @param getterFieldName
|
||||
* @param valueMatchMap
|
||||
* @param <E>
|
||||
*/
|
||||
public static <E> void bindPropValueOfList(String setterFieldName, List<E> fromList, String getterFieldName, Map valueMatchMap){
|
||||
if(V.isEmpty(fromList) || V.isEmpty(valueMatchMap)){
|
||||
return;
|
||||
}
|
||||
try{
|
||||
for(E object : fromList){
|
||||
Object fieldValue = getProperty(object, getterFieldName);
|
||||
Object value = null;
|
||||
if(valueMatchMap.containsKey(fieldValue)){
|
||||
value = valueMatchMap.get(fieldValue);
|
||||
}
|
||||
else{
|
||||
// 获取到当前的属性值
|
||||
String fieldValueStr = getStringProperty(object, getterFieldName);
|
||||
// 获取到当前的value
|
||||
value = valueMatchMap.get(fieldValueStr);
|
||||
}
|
||||
// 赋值
|
||||
setProperty(object, setterFieldName, value);
|
||||
}
|
||||
}
|
||||
catch (Exception e){
|
||||
log.warn("设置属性值异常, setterFieldName="+setterFieldName, e);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* 转换方法引用为属性名
|
||||
* @param fn
|
||||
* @return
|
||||
*/
|
||||
/*public static <T> String convertToFieldName(IGetter<T> fn) {
|
||||
SerializedLambda lambda = getSerializedLambda(fn);
|
||||
String methodName = lambda.getImplMethodName();
|
||||
String prefix = null;
|
||||
if(methodName.startsWith("get")){
|
||||
prefix = "get";
|
||||
}
|
||||
else if(methodName.startsWith("is")){
|
||||
prefix = "is";
|
||||
}
|
||||
if(prefix == null){
|
||||
log.warn("无效的getter方法: "+methodName);
|
||||
}
|
||||
return S.uncapFirst(S.substringAfter(methodName, prefix));
|
||||
}*/
|
||||
|
||||
/***
|
||||
* 转换方法引用为属性名
|
||||
* @param fn
|
||||
* @return
|
||||
*/
|
||||
/*public static <T,R> String convertToFieldName(ISetter<T,R> fn) {
|
||||
SerializedLambda lambda = getSerializedLambda(fn);
|
||||
String methodName = lambda.getImplMethodName();
|
||||
if(!methodName.startsWith("set")){
|
||||
log.warn("无效的setter方法: "+methodName);
|
||||
}
|
||||
return S.uncapFirst(S.substringAfter(methodName, "set"));
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 获取类所有属性(包含父类)
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
public static List<Field> extractAllFields(Class clazz){
|
||||
List<Field> fieldList = new ArrayList<>();
|
||||
Set<String> fieldNameSet = new HashSet<>();
|
||||
while (clazz != null) {
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
if(V.notEmpty(fields)){ //被重写属性,以子类override的为准
|
||||
Arrays.stream(fields).forEach((field)->{
|
||||
if(!fieldNameSet.contains(field.getName())){
|
||||
fieldList.add(field);
|
||||
fieldNameSet.add(field.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return fieldList;
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取类对应的Lambda
|
||||
* @param fn
|
||||
* @return
|
||||
*/
|
||||
private static SerializedLambda getSerializedLambda(Serializable fn){
|
||||
SerializedLambda lambda = CLASS_LAMDBA_CACHE.get(fn.getClass());
|
||||
if(lambda == null){
|
||||
try{
|
||||
Method method = fn.getClass().getDeclaredMethod("writeReplace");
|
||||
method.setAccessible(Boolean.TRUE);
|
||||
lambda = (SerializedLambda) method.invoke(fn);
|
||||
CLASS_LAMDBA_CACHE.put(fn.getClass(), lambda);
|
||||
}
|
||||
catch (Exception e){
|
||||
log.error("获取SerializedLambda异常, class="+fn.getClass().getSimpleName(), e);
|
||||
}
|
||||
}
|
||||
return lambda;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为指定类型的对象
|
||||
* @param type
|
||||
* @param value
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static Object convertValue2Type(String value, String type) throws Exception{
|
||||
if (V.isEmpty(type)){
|
||||
throw new Exception("参数传递错误:类型未定义!");
|
||||
}
|
||||
// 如果值为空 返回包装类型null
|
||||
if (value == null){
|
||||
return value;
|
||||
}
|
||||
type = S.trim(type);
|
||||
// 处理boolean值
|
||||
if (Boolean.class.getSimpleName().equalsIgnoreCase(type)){
|
||||
return V.isTrue(value);
|
||||
}
|
||||
// 其他情况交由BeanUtils转换
|
||||
// 其他情况直接返回string
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,323 @@
|
|||
package com.diboot.commons.utils;
|
||||
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 提供常用的日期操作的工具类
|
||||
* @author MaZhicheng
|
||||
* @version 2.0
|
||||
* @date 2019/01/01
|
||||
*/
|
||||
public class D extends DateUtils{
|
||||
private static final Logger log = LoggerFactory.getLogger(DateUtils.class);
|
||||
|
||||
/***
|
||||
* 日期时间格式
|
||||
*/
|
||||
public static final String FORMAT_DATE_y2M = "yyMM";
|
||||
public static final String FORMAT_DATE_y2Md = "yyMMdd";
|
||||
public static final String FORMAT_DATE_y4 = "yyyy";
|
||||
public static final String FORMAT_DATE_y4Md = "yyyyMMdd";
|
||||
public static final String FORMAT_DATE_Y4MD = "yyyy-MM-dd";
|
||||
public static final String FORMAT_TIMESTAMP = "yyMMddhhmmss";
|
||||
public static final String FORMAT_TIME_HHmm = "HH:mm";
|
||||
public static final String FORMAT_TIME_HHmmss = "HH:mm:ss";
|
||||
public static final String FORMAT_DATETIME_Y4MDHM = "yyyy-MM-dd HH:mm";
|
||||
public static final String FORMAT_DATETIME_Y4MDHMS = "yyyy-MM-dd HH:mm:ss";
|
||||
/***
|
||||
* 星期
|
||||
*/
|
||||
protected static final String[] WEEK = new String[]{"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
|
||||
|
||||
/***
|
||||
* 当前的日期时间
|
||||
* @return format指定格式的日期时间
|
||||
*/
|
||||
public static String now(String format){
|
||||
Calendar cal = Calendar.getInstance();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(format);
|
||||
return sdf.format(cal.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前日期时间串
|
||||
* @return yyMMddhhmmss
|
||||
*/
|
||||
public static String toTimestamp(Date date){
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_TIMESTAMP);
|
||||
return sdf.format(date.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取月份
|
||||
* @return
|
||||
*/
|
||||
public static String getMonth(){
|
||||
return now(FORMAT_DATE_y2M);
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取今天的日期
|
||||
* @return yyyyMMdd
|
||||
*/
|
||||
public static String today(){
|
||||
return now(FORMAT_DATE_y4Md);
|
||||
}
|
||||
|
||||
/***
|
||||
* 转换字符串为日期date
|
||||
* @param datetime
|
||||
* @param fmt
|
||||
* @return
|
||||
*/
|
||||
public static Date convert2FormatDate(String datetime, String fmt){
|
||||
if (StringUtils.isBlank(datetime)){
|
||||
return null;
|
||||
}
|
||||
SimpleDateFormat format = new SimpleDateFormat(fmt);
|
||||
try {
|
||||
Date date = format.parse(datetime);
|
||||
return date;
|
||||
}
|
||||
catch (ParseException e) {
|
||||
log.warn("日期格式转换异常");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/***
|
||||
* 转换date为格式化字符串
|
||||
* @param date
|
||||
* @param fmt
|
||||
* @return
|
||||
*/
|
||||
public static String convert2FormatString(Date date, String fmt) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
} else {
|
||||
SimpleDateFormat format = new SimpleDateFormat(fmt);
|
||||
return format.format(date);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取格式化的日期
|
||||
* @param date 基准日期
|
||||
* @param daysOffset 偏移量
|
||||
* @return yyyy-MM-dd
|
||||
*/
|
||||
public static String getDate(Date date, int... daysOffset){
|
||||
if(date == null){
|
||||
date = new Date();
|
||||
}
|
||||
if(daysOffset != null && daysOffset.length > 0){
|
||||
date = addDays(date, daysOffset[0]);
|
||||
}
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_DATE_Y4MD);
|
||||
return sdf.format(date);
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取格式化的日期时间
|
||||
* @param date
|
||||
* @return yyyy-MM-dd HH:mm
|
||||
*/
|
||||
public static String getDateTime(Date date, int... daysOffset){
|
||||
if(date == null){
|
||||
date = new Date();
|
||||
}
|
||||
if(daysOffset != null && daysOffset.length > 0){
|
||||
date = addDays(date, daysOffset[0]);
|
||||
}
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_DATETIME_Y4MDHM);
|
||||
return sdf.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是工作时间段,用于后台程序等
|
||||
* @return
|
||||
*/
|
||||
public static boolean isWorkingTime(){
|
||||
Calendar cal = Calendar.getInstance();
|
||||
int hour = cal.get(Calendar.HOUR_OF_DAY);
|
||||
return (hour >= 8 && hour < 20);
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取上午/下午
|
||||
* @return
|
||||
*/
|
||||
public static String getAmPm() {
|
||||
Calendar c = Calendar.getInstance();
|
||||
int hours = c.get(Calendar.HOUR_OF_DAY);
|
||||
if (hours <= 9){
|
||||
return "早上";
|
||||
}
|
||||
else if (9 < hours && hours <= 12){
|
||||
return "上午";
|
||||
}
|
||||
else if (12 < hours && hours <= 13){
|
||||
return "中午";
|
||||
}
|
||||
else if (13 < hours && hours <= 18){
|
||||
return "下午";
|
||||
}
|
||||
else{
|
||||
return "晚上";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到当前的年月YYMM,用于生成文件夹名称
|
||||
* @return
|
||||
*/
|
||||
public static String getYearMonth(){
|
||||
Calendar cal = Calendar.getInstance();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_DATE_y2M);
|
||||
return sdf.format(cal.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到当前的年月YYMM,用于生成文件夹
|
||||
* @return
|
||||
*/
|
||||
public static String getYearMonthDay(){
|
||||
Calendar cal = Calendar.getInstance();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_DATE_y2Md);
|
||||
return sdf.format(cal.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到当前的年月YYMM,用于生成文件夹
|
||||
* @return
|
||||
*/
|
||||
public static int getDay(){
|
||||
Calendar cal = Calendar.getInstance();
|
||||
return cal.get(Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取日期对应的星期
|
||||
* @param date
|
||||
* @return
|
||||
*/
|
||||
public static String getWeek(Date date){
|
||||
return WEEK[Calendar.getInstance().get(Calendar.DAY_OF_WEEK)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 毫秒数转date
|
||||
* @param timeMillis
|
||||
* @return
|
||||
*/
|
||||
public static Date timeMillis2Date(Long timeMillis){
|
||||
return new Date(timeMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串时间戳转日期
|
||||
* @param value
|
||||
* @return
|
||||
* @throws ParseException
|
||||
*/
|
||||
public static Date datetimeString2Date(String value){
|
||||
return convert2DateTime(value, FORMAT_DATETIME_Y4MDHMS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串时间戳转日期
|
||||
* @return
|
||||
* @throws ParseException
|
||||
*/
|
||||
public static Date convert2Date(String date){
|
||||
return convert2FormatDate(date, FORMAT_DATE_Y4MD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串时间戳转日期
|
||||
* @param dateTime
|
||||
* @return
|
||||
* @throws ParseException
|
||||
*/
|
||||
public static Date convert2DateTime(String dateTime, String... dateFormat){
|
||||
String f = FORMAT_DATETIME_Y4MDHM;
|
||||
if(dateFormat != null && dateFormat.length > 0){
|
||||
f = dateFormat[0];
|
||||
}
|
||||
return convert2FormatDate(dateTime, f);
|
||||
}
|
||||
|
||||
/***
|
||||
* 模糊转换日期
|
||||
* @param dateString
|
||||
* @return
|
||||
*/
|
||||
public static Date fuzzyConvert(String dateString){
|
||||
if(V.isEmpty(dateString)){
|
||||
return null;
|
||||
}
|
||||
// 清洗
|
||||
if(dateString.contains("-")){
|
||||
}
|
||||
else if(dateString.contains("月")){
|
||||
dateString = dateString.replaceAll("年", "-").replaceAll("月", "-").replaceAll("日", "").replaceAll("号", "");
|
||||
}
|
||||
else{
|
||||
dateString = dateString.replaceAll("\\/", "-").replaceAll("\\.", "-");
|
||||
}
|
||||
String[] parts = dateString.split(" ");
|
||||
String[] ymd = parts[0].split("-");
|
||||
if(ymd.length >= 3){
|
||||
if(ymd[0].length() == 2){
|
||||
ymd[0] = String.valueOf(Calendar.getInstance().get(Calendar.YEAR)).substring(0, 2) + ymd[0];
|
||||
}
|
||||
if(ymd[1].length() == 1){
|
||||
ymd[1] = "0" + ymd[1];
|
||||
}
|
||||
if(ymd[2].length() == 1){
|
||||
ymd[2] = "0" + ymd[2];
|
||||
}
|
||||
}
|
||||
parts[0] = S.join(ymd, "-");
|
||||
if(parts.length == 1){
|
||||
return D.convert2FormatDate(parts[0], D.FORMAT_DATE_Y4MD);
|
||||
}
|
||||
// 18:20:30:103
|
||||
String[] hmsArray = new String[3];
|
||||
String[] hms = parts[1].split(":");
|
||||
if(hms[0].length() == 1){
|
||||
hms[0] = "0" + hms[0];
|
||||
}
|
||||
hmsArray[0] = hms[0];
|
||||
if(hms.length >= 2){
|
||||
if(hms[1].length() == 1){
|
||||
hms[1] = "0" + hms[1];
|
||||
}
|
||||
hmsArray[1] = hms[1];
|
||||
}
|
||||
else{
|
||||
hmsArray[1] = "00";
|
||||
}
|
||||
if(hms.length >= 3){
|
||||
if(hms[2].length() == 1){
|
||||
hms[2] = "0" + hms[2];
|
||||
}
|
||||
hmsArray[2] = hms[2];
|
||||
}
|
||||
else{
|
||||
hmsArray[2] = "00";
|
||||
}
|
||||
parts[1] = S.join(hmsArray, ":");
|
||||
return D.convert2FormatDate(S.join(parts, " "), D.FORMAT_DATETIME_Y4MDHMS);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
package com.diboot.commons.utils;
|
||||
|
||||
import com.diboot.commons.config.BaseConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 加解密工具类 (提供AES加解密,MD5多次哈希...)
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/01/01
|
||||
*/
|
||||
public class Encryptor {
|
||||
private static final Logger log = LoggerFactory.getLogger(Encryptor.class);
|
||||
|
||||
/**
|
||||
* 算法
|
||||
*/
|
||||
private static final String KEY_ALGORITHM = "AES";
|
||||
private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5PADDING";
|
||||
/**
|
||||
* 默认加密seed(可通过配置文件)
|
||||
*/
|
||||
private static final String KEY_DEFAULT = V.notEmpty(BaseConfig.getProperty("diboot.encryptor.seed"))? BaseConfig.getProperty("diboot.encryptor.seed") : "DibootV2";
|
||||
|
||||
private static final String KEY_FILL = "abcdefghijklmnop";
|
||||
|
||||
/**
|
||||
* 加密Cipher缓存
|
||||
*/
|
||||
private static Map<String, Cipher> encryptorMap = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* 解密Cipher缓存
|
||||
*/
|
||||
private static Map<String, Cipher> decryptorMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 加密字符串(可指定加密密钥)
|
||||
* @param input 待加密文本
|
||||
* @param key 密钥(可选)
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String encrypt(String input, String... key){
|
||||
String seedKey = V.notEmpty(key)? key[0] : KEY_DEFAULT;
|
||||
try{
|
||||
Cipher cipher = getEncryptor(seedKey);
|
||||
byte[] enBytes = cipher.doFinal(input.getBytes());
|
||||
return Base64.getEncoder().encodeToString(enBytes);
|
||||
}
|
||||
catch(Exception e){
|
||||
log.error("加密出错:"+input, e);
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密字符串
|
||||
* @param input 待解密文本
|
||||
* @param key 加密key(可选)
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String decrypt(String input, String... key){
|
||||
if(V.isEmpty(input)){
|
||||
return input;
|
||||
}
|
||||
String seedKey = V.notEmpty(key)? key[0] : KEY_DEFAULT;
|
||||
try{
|
||||
Cipher cipher = getDecryptor(seedKey);
|
||||
byte[] deBytes = Base64.getDecoder().decode(input.getBytes());
|
||||
return new String(cipher.doFinal(deBytes));
|
||||
}
|
||||
catch(Exception e){
|
||||
log.error("解密出错:"+input, e);
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取指定key的加密器
|
||||
* @param key 加密密钥
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private static Cipher getEncryptor(String key) throws Exception{
|
||||
byte[] keyBytes = getKey(key);
|
||||
Cipher encryptor = encryptorMap.get(new String(keyBytes));
|
||||
if(encryptor == null){
|
||||
SecretKeySpec skeyspec = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
|
||||
encryptor = Cipher.getInstance(CIPHER_ALGORITHM);
|
||||
encryptor.init(Cipher.ENCRYPT_MODE, skeyspec);
|
||||
// 放入缓存
|
||||
encryptorMap.put(key, encryptor);
|
||||
}
|
||||
return encryptor;
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取指定key的解密器
|
||||
* @param key 解密密钥
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private static Cipher getDecryptor(String key) throws Exception{
|
||||
byte[] keyBytes = getKey(key);
|
||||
Cipher decryptor = encryptorMap.get(new String(keyBytes));
|
||||
if(decryptor == null){
|
||||
SecretKeySpec skeyspec = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
|
||||
decryptor = Cipher.getInstance(CIPHER_ALGORITHM);
|
||||
decryptor.init(Cipher.DECRYPT_MODE, skeyspec);
|
||||
// 放入缓存
|
||||
decryptorMap.put(key, decryptor);
|
||||
}
|
||||
return decryptor;
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取key,如非16位则调整为16位
|
||||
* @param seed
|
||||
* @return
|
||||
*/
|
||||
private static byte[] getKey(String seed){
|
||||
if(V.isEmpty(seed)){
|
||||
seed = KEY_DEFAULT;
|
||||
}
|
||||
if(seed.length() < 16){
|
||||
seed = seed + S.cut(KEY_FILL, 16-seed.length());
|
||||
}
|
||||
else if(seed.length() > 16){
|
||||
seed = S.cut(KEY_FILL, 16);
|
||||
}
|
||||
return seed.getBytes();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package com.diboot.commons.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.serializer.SerializeConfig;
|
||||
import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/***
|
||||
* JSON操作辅助类
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/01/01
|
||||
*/
|
||||
public class JSON extends JSONObject{
|
||||
private static final Logger log = LoggerFactory.getLogger(JSON.class);
|
||||
|
||||
/**
|
||||
* 序列化配置
|
||||
*/
|
||||
private static SerializeConfig serializeConfig = new SerializeConfig();
|
||||
static {
|
||||
serializeConfig.put(Date.class, new SimpleDateFormatSerializer(D.FORMAT_DATETIME_Y4MDHM));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Java对象转换为Json String
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
public static String stringify(Object object){
|
||||
return toJSONString(object, serializeConfig);
|
||||
}
|
||||
|
||||
/***
|
||||
* 将JSON字符串转换为java对象
|
||||
* @param jsonStr
|
||||
* @return
|
||||
*/
|
||||
public static Map toMap(String jsonStr){
|
||||
return parseObject(jsonStr);
|
||||
}
|
||||
|
||||
/***
|
||||
* 将JSON字符串转换为java对象
|
||||
* @param jsonStr
|
||||
* @return
|
||||
*/
|
||||
public static LinkedHashMap toLinkedHashMap(String jsonStr){
|
||||
if(V.isEmpty(jsonStr)){
|
||||
return null;
|
||||
}
|
||||
return toJavaObject(jsonStr, LinkedHashMap.class);
|
||||
}
|
||||
|
||||
/***
|
||||
* 将JSON字符串转换为java对象
|
||||
* @param jsonStr
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
public static <T> T toJavaObject(String jsonStr, Class<T> clazz){
|
||||
return JSONObject.parseObject(jsonStr, clazz);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
package com.diboot.commons.utils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 配置文件工具类
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/01/01
|
||||
*/
|
||||
public class PropertiesUtils {
|
||||
private static final Logger log = LoggerFactory.getLogger(PropertiesUtils.class);
|
||||
|
||||
/***
|
||||
* 缓存多个资源文件
|
||||
*/
|
||||
private static Map<String, Properties> allPropertiesMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 默认资源文件名称
|
||||
*/
|
||||
private static final String DEFAULT_PROPERTIES_NAME = "application.properties";
|
||||
|
||||
/***
|
||||
* Properties配置工厂类
|
||||
*/
|
||||
private static PropertiesFactoryBean pfb = new PropertiesFactoryBean();
|
||||
|
||||
/***
|
||||
* 获取指定的配置文件
|
||||
* @return
|
||||
*/
|
||||
private static Properties getProperties(String... propertiesFileNameArgs){
|
||||
// 获取文件名
|
||||
String propFileName = getPropertiesFileName(propertiesFileNameArgs);
|
||||
// 从缓存读取
|
||||
Properties props = allPropertiesMap.get(propFileName);
|
||||
if(props == null){
|
||||
if(propFileName.endsWith(".properties")){
|
||||
pfb.setLocation(new ClassPathResource(propFileName));
|
||||
try{
|
||||
pfb.afterPropertiesSet();
|
||||
props = pfb.getObject();
|
||||
}
|
||||
catch (Exception e){
|
||||
log.warn("无法找到配置文件: " + propFileName, e);
|
||||
}
|
||||
}
|
||||
else if(propFileName.endsWith(".yaml")){
|
||||
props = yamlToProperties(propFileName);
|
||||
}
|
||||
if(props != null){
|
||||
allPropertiesMap.put(propFileName, props);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
/***
|
||||
* 读取配置项的值
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static String get(String key, String... propertiesFileName){
|
||||
// 获取文件名
|
||||
String propFileName = getPropertiesFileName(propertiesFileName);
|
||||
// 获取配置值
|
||||
String value = getConfigValueFromPropFile(key, propFileName);
|
||||
if(value == null && DEFAULT_PROPERTIES_NAME.equals(propFileName)){
|
||||
// 如果是默认配置,读取不到再尝试从当前profile配置中读取
|
||||
String profile = getConfigValueFromPropFile("spring.profiles.active", DEFAULT_PROPERTIES_NAME);
|
||||
if(V.notEmpty(profile)){
|
||||
value = getConfigValueFromPropFile(key, S.substringBeforeLast(DEFAULT_PROPERTIES_NAME, ".") + "-"+profile+".properties");
|
||||
}
|
||||
}
|
||||
if(value == null){
|
||||
log.trace("配置文件 {} 中未找到配置项: {}", propertiesFileName, key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/***
|
||||
* 读取int型的配置项
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static Integer getInteger(String key, String... propertiesFileName){
|
||||
// 获取文件名
|
||||
String propFileName = getPropertiesFileName(propertiesFileName);
|
||||
// 获取配置值
|
||||
String value = get(key, propFileName);
|
||||
if(V.notEmpty(value)){
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
log.trace("配置文件 {} 中未找到配置项: {}", propertiesFileName, key);
|
||||
return null;
|
||||
}
|
||||
|
||||
/***
|
||||
* 读取boolean值的配置项
|
||||
*/
|
||||
public static boolean getBoolean(String key, String... propertiesFileName) {
|
||||
// 获取文件名
|
||||
String propFileName = getPropertiesFileName(propertiesFileName);
|
||||
// 获取配置值
|
||||
String value = get(key, propFileName);
|
||||
if(V.notEmpty(value)){
|
||||
return V.isTrue(value);
|
||||
}
|
||||
log.trace("配置文件 "+(V.notEmpty(propertiesFileName)? propertiesFileName[0] : "")+" 中未找到配置项: "+key + ", 启用默认值 false.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取配置文件名称
|
||||
* @param propertiesFileName
|
||||
* @return
|
||||
*/
|
||||
private static String getPropertiesFileName(String... propertiesFileName){
|
||||
// 获取文件名
|
||||
if(propertiesFileName != null && propertiesFileName.length > 0){
|
||||
return propertiesFileName[0];
|
||||
}
|
||||
else{
|
||||
return DEFAULT_PROPERTIES_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* 从配置文件中读取配置值
|
||||
* @param key
|
||||
* @param propertiesFileName
|
||||
* @return
|
||||
*/
|
||||
private static String getConfigValueFromPropFile(String key, String propertiesFileName){
|
||||
// 获取配置值
|
||||
Properties properties = getProperties(propertiesFileName);
|
||||
if(properties != null){
|
||||
if(properties.containsKey(key)){
|
||||
String value = properties.getProperty(key);
|
||||
// 任何password相关的参数需解密
|
||||
boolean isSensitiveConfig = key.contains(".password") || key.contains(".secret");
|
||||
if(value != null && isSensitiveConfig){
|
||||
value = Encryptor.decrypt(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取yaml并转换为Properties
|
||||
* @param yamlSource
|
||||
* @return
|
||||
*/
|
||||
private static Properties yamlToProperties(String yamlSource) {
|
||||
try {
|
||||
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
|
||||
yaml.setResources(new ClassPathResource(yamlSource));
|
||||
return yaml.getObject();
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Cannot read yaml "+yamlSource, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package com.diboot.commons.utils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/***
|
||||
* Java执行命令行辅助类
|
||||
* @author Mazc@com.ltd
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package com.diboot.commons.utils;
|
||||
|
||||
import com.diboot.commons.config.BaseConfig;
|
||||
import com.diboot.commons.config.BaseCons;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
/***
|
||||
* String 操作类
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/01/01
|
||||
*/
|
||||
public class S extends StringUtils{
|
||||
/***
|
||||
* 默认分隔符 ,
|
||||
*/
|
||||
public static final String SEPARATOR = ",";
|
||||
|
||||
/***
|
||||
* 裁剪字符串,显示前部分+...
|
||||
* @param input
|
||||
* @return
|
||||
*/
|
||||
public static String cut(String input){
|
||||
return cut(input, BaseConfig.getCutLength());
|
||||
}
|
||||
|
||||
/***
|
||||
* 裁剪字符串,显示前部分+...
|
||||
* @param input
|
||||
* @return
|
||||
*/
|
||||
public static String cut(String input, int cutLength){
|
||||
return substring(input, 0, cutLength);
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* 将list拼接成string,默认分隔符:,
|
||||
* @param stringList
|
||||
* @return
|
||||
*/
|
||||
public static String join(List<String> stringList){
|
||||
return StringUtils.join(stringList, SEPARATOR);
|
||||
}
|
||||
|
||||
/***
|
||||
* 将list拼接成string,默认分隔符:,
|
||||
* @param stringArray
|
||||
* @return
|
||||
*/
|
||||
public static String join(String[] stringArray){
|
||||
return StringUtils.join(stringArray, SEPARATOR);
|
||||
}
|
||||
|
||||
/***
|
||||
* 按,拆分字符串
|
||||
* @param joinedStr
|
||||
* @return
|
||||
*/
|
||||
public static String[] split(String joinedStr){
|
||||
if(joinedStr == null){
|
||||
return null;
|
||||
}
|
||||
return joinedStr.split(SEPARATOR);
|
||||
}
|
||||
|
||||
/***
|
||||
* 转换为String数组(避免转型异常)
|
||||
* @param stringList
|
||||
* @return
|
||||
*/
|
||||
public static String[] toStringArray(List<String> stringList){
|
||||
return stringList.toArray(new String[stringList.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得随机串
|
||||
* @return
|
||||
*/
|
||||
public static String newUuid() {
|
||||
return UUID.randomUUID().toString().replaceAll("-", "");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
package com.diboot.commons.utils;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
|
||||
/***
|
||||
* Validator校验类
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/01/01
|
||||
*
|
||||
*/
|
||||
public class V {
|
||||
private static final Logger log = LoggerFactory.getLogger(V.class);
|
||||
|
||||
/***
|
||||
* 对象是否为空
|
||||
* @param obj
|
||||
* @return
|
||||
*/
|
||||
public static boolean isEmpty(Object obj){
|
||||
if(obj instanceof String){
|
||||
return isEmpty((String)obj);
|
||||
}
|
||||
else if(obj instanceof Collection){
|
||||
return isEmpty((Collection)obj);
|
||||
}
|
||||
else if(obj instanceof Map){
|
||||
return isEmpty((Map)obj);
|
||||
}
|
||||
else if(obj instanceof String[]){
|
||||
return isEmpty((String[])obj);
|
||||
}
|
||||
else{
|
||||
return obj == null;
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* 字符串是否为空
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static boolean isEmpty(String value){
|
||||
return S.isBlank(value);
|
||||
}
|
||||
|
||||
/***
|
||||
* 字符串数组是否不为空
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
public static boolean isEmpty(String[] values){
|
||||
return values == null || values.length == 0;
|
||||
}
|
||||
|
||||
/***
|
||||
* 集合为空
|
||||
* @param list
|
||||
* @return
|
||||
*/
|
||||
public static <T> boolean isEmpty(Collection<T> list) {
|
||||
return list == null || list.isEmpty();
|
||||
}
|
||||
|
||||
/***
|
||||
* Map为空
|
||||
* @param obj
|
||||
* @return
|
||||
*/
|
||||
public static boolean isEmpty(Map obj){
|
||||
return obj == null || obj.isEmpty();
|
||||
}
|
||||
|
||||
/***
|
||||
* 对象是否为空
|
||||
* @param obj
|
||||
* @return
|
||||
*/
|
||||
public static boolean notEmpty(Object obj){
|
||||
if(obj instanceof String){
|
||||
return notEmpty((String)obj);
|
||||
}
|
||||
else if(obj instanceof Collection){
|
||||
return notEmpty((Collection)obj);
|
||||
}
|
||||
else if(obj instanceof Map){
|
||||
return notEmpty((Map)obj);
|
||||
}
|
||||
else if(obj instanceof String[]){
|
||||
return notEmpty((String[])obj);
|
||||
}
|
||||
else{
|
||||
return obj != null;
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* 字符串是否不为空
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static boolean notEmpty(String value){
|
||||
return S.isNotBlank(value);
|
||||
}
|
||||
|
||||
/***
|
||||
* 字符串数组是否不为空
|
||||
* @param values
|
||||
* @return
|
||||
*/
|
||||
public static boolean notEmpty(String[] values){
|
||||
return values != null && values.length > 0;
|
||||
}
|
||||
|
||||
/***
|
||||
* 集合不为空
|
||||
* @param list
|
||||
* @return
|
||||
*/
|
||||
public static <T> boolean notEmpty(Collection<T> list) {
|
||||
return list != null && !list.isEmpty();
|
||||
}
|
||||
|
||||
/***
|
||||
* Map为空
|
||||
* @param obj
|
||||
* @return
|
||||
*/
|
||||
public static boolean notEmpty(Map obj){
|
||||
return obj != null && !obj.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否boolean值范围
|
||||
*/
|
||||
private static final Set<String> TRUE_SET = new HashSet(){{
|
||||
add("true"); add("是"); add("y"); add("yes"); add("1");
|
||||
}};
|
||||
private static final Set<String> FALSE_SET = new HashSet(){{
|
||||
add("false"); add("否"); add("n"); add("no"); add("0");
|
||||
}};
|
||||
|
||||
/***
|
||||
* 转换为boolean类型, 并判定是否为true
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public static boolean isTrue(String value){
|
||||
if(value == null){
|
||||
return false;
|
||||
}
|
||||
value = S.trim(value).toLowerCase();
|
||||
return TRUE_SET.contains(value);
|
||||
}
|
||||
|
||||
/***
|
||||
* 判定两个对象是否不同类型或不同值
|
||||
* @param source
|
||||
* @param target
|
||||
* @return
|
||||
*/
|
||||
public static boolean notEquals(Object source, Object target){
|
||||
return !equals(source, target);
|
||||
}
|
||||
|
||||
/***
|
||||
* 判定两个对象是否类型相同值相等
|
||||
* @param source
|
||||
* @param target
|
||||
* @return
|
||||
*/
|
||||
public static <T> boolean equals(T source, T target){
|
||||
if(source == null && target == null){
|
||||
return true;
|
||||
}
|
||||
else if(source == null || target == null){
|
||||
return false;
|
||||
}
|
||||
// 不为空,调用equals比较
|
||||
else if(source instanceof Comparable){
|
||||
return (source).equals(target);
|
||||
}
|
||||
else if(source instanceof Collection){
|
||||
Collection sourceList = (Collection)source, targetList = (Collection)target;
|
||||
// size不等
|
||||
if(sourceList.size() != targetList.size()){
|
||||
return false;
|
||||
}
|
||||
for(Object obj : sourceList){
|
||||
if(!targetList.contains(obj)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
log.warn("暂未实现类型 "+ source.getClass().getSimpleName() + "-"+ target.getClass().getSimpleName() + " 的比对!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* 模糊对比是否相等(类型不同的转成String对比)
|
||||
* @param source
|
||||
* @param target
|
||||
* @return
|
||||
*/
|
||||
public static boolean fuzzyEqual(Object source, Object target){
|
||||
if(equals(source, target)){
|
||||
return true;
|
||||
}
|
||||
// Boolean-String类型
|
||||
if(source instanceof Boolean && target instanceof String){
|
||||
return (boolean) source == V.isTrue((String)target);
|
||||
}
|
||||
if(target instanceof Boolean && source instanceof String){
|
||||
return (boolean) target == V.isTrue((String)source);
|
||||
}
|
||||
// Date-String类型
|
||||
else if((source instanceof Timestamp || source instanceof Date) && target instanceof String){
|
||||
return D.getDateTime((Date)source).equals(target) || D.getDate((Date)source).equals(target);
|
||||
}
|
||||
else if((target instanceof Timestamp || target instanceof Date) && source instanceof String){
|
||||
return D.getDateTime((Date)target).equals(source) || D.getDate((Date)target).equals(source);
|
||||
}
|
||||
else{
|
||||
return String.valueOf(source).equals(String.valueOf(target));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package com.diboot.commons.vo;
|
||||
|
||||
import com.diboot.commons.utils.V;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* JSON返回结果
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/01/01
|
||||
*/
|
||||
public class JsonResult implements Serializable {
|
||||
private static final long serialVersionUID = 1001L;
|
||||
|
||||
/***
|
||||
* 状态码
|
||||
*/
|
||||
private int code;
|
||||
/***
|
||||
* 消息内容
|
||||
*/
|
||||
private String msg;
|
||||
/***
|
||||
* 返回结果数据
|
||||
*/
|
||||
private Object data;
|
||||
|
||||
/**
|
||||
* 默认成功,无返回数据
|
||||
*/
|
||||
public JsonResult(){
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认成功,有返回数据(及附加提示信息)
|
||||
*/
|
||||
public JsonResult(Object data, String... additionalMsg){
|
||||
this.code = Status.OK.code();
|
||||
this.msg = Status.OK.label();
|
||||
this.data = data;
|
||||
if(V.notEmpty(additionalMsg)){
|
||||
this.msg += ": " + additionalMsg[0];
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* 非成功,指定状态(及附加提示信息)
|
||||
* @param status
|
||||
* @param additionalMsg
|
||||
*/
|
||||
public JsonResult(Status status, String... additionalMsg){
|
||||
this.code = status.code();
|
||||
this.msg = status.label();
|
||||
if(V.notEmpty(additionalMsg)){
|
||||
this.msg += ": " + additionalMsg[0];
|
||||
}
|
||||
this.data = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 非成功,指定状态、返回数据(及附加提示信息)
|
||||
*/
|
||||
public JsonResult(Status status, Object data, String... additionalMsg){
|
||||
this.code = status.code();
|
||||
this.msg = status.label();
|
||||
if(V.notEmpty(additionalMsg)){
|
||||
this.msg += ": " + additionalMsg[0];
|
||||
}
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/***
|
||||
* 自定义JsonResult
|
||||
* @param code
|
||||
* @param label
|
||||
* @param data
|
||||
*/
|
||||
public JsonResult(int code, String label, Object data){
|
||||
this.code = code;
|
||||
this.msg = label;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/***
|
||||
* 绑定分页信息
|
||||
* @param pagination
|
||||
*/
|
||||
public JsonResult bindPagination(Pagination pagination){
|
||||
return new PagingJsonResult(this, pagination);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.diboot.commons.vo;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* KeyValue键值对形式的VO
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/1/4
|
||||
*/
|
||||
public class KeyValue implements Serializable {
|
||||
private static final long serialVersionUID = -2358161241655186720L;
|
||||
|
||||
public KeyValue(){}
|
||||
|
||||
public KeyValue(String key, Object value){
|
||||
this.k = key;
|
||||
this.v = value;
|
||||
}
|
||||
|
||||
/***
|
||||
* key: 显示值
|
||||
*/
|
||||
private String k;
|
||||
|
||||
/***
|
||||
* value: 存储值
|
||||
*/
|
||||
private Object v;
|
||||
|
||||
/**
|
||||
* 扩展值
|
||||
*/
|
||||
private Object ext;
|
||||
|
||||
public String getK() {
|
||||
return k;
|
||||
}
|
||||
|
||||
public void setK(String k) {
|
||||
this.k = k;
|
||||
}
|
||||
|
||||
public Object getV() {
|
||||
return v;
|
||||
}
|
||||
|
||||
public void setV(Object v) {
|
||||
this.v = v;
|
||||
}
|
||||
|
||||
public Object getExt() {
|
||||
return ext;
|
||||
}
|
||||
|
||||
public void setExt(Object ext) {
|
||||
this.ext = ext;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
package com.diboot.commons.vo;
|
||||
|
||||
import com.diboot.commons.config.BaseConfig;
|
||||
import com.diboot.commons.config.BaseCons;
|
||||
import com.diboot.commons.utils.S;
|
||||
import com.diboot.commons.utils.V;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分页 (属性以下划线开头以避免与提交参数字段冲突)
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/01/01
|
||||
*/
|
||||
public class Pagination implements Serializable {
|
||||
private static final Logger log = LoggerFactory.getLogger(Pagination.class);
|
||||
|
||||
private static final long serialVersionUID = -4083929594112114522L;
|
||||
|
||||
/***
|
||||
* 当前页
|
||||
*/
|
||||
private int pageIndex = 1;
|
||||
/***
|
||||
* 默认每页数量10
|
||||
*/
|
||||
private int pageSize = BaseConfig.getPageSize();
|
||||
/***
|
||||
* count总数
|
||||
*/
|
||||
private long totalCount = 0;
|
||||
/***
|
||||
* 排序-升序排列的字段
|
||||
*/
|
||||
private List<String> ascList = null;
|
||||
/***
|
||||
* 降序排列的字段(默认以ID降序排列,当指定了其他排列方式时以用户指定为准)
|
||||
*/
|
||||
private List<String> descList = new ArrayList<>(Arrays.asList(BaseCons.FieldName.id.name()));
|
||||
|
||||
public Pagination(){
|
||||
}
|
||||
|
||||
/***
|
||||
* 指定当前页数
|
||||
*/
|
||||
public Pagination(int pageIndex){
|
||||
setPageIndex(pageIndex);
|
||||
}
|
||||
|
||||
public int getPageIndex() {
|
||||
return pageIndex;
|
||||
}
|
||||
|
||||
public void setPageIndex(int pageIndex) {
|
||||
this.pageIndex = pageIndex;
|
||||
}
|
||||
|
||||
public int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
public void setPageSize(int pageSize) {
|
||||
if(pageSize > 1000){
|
||||
log.warn("分页pageSize过大,将被调整为默认限值,请检查调用是否合理!pageSize="+ pageSize);
|
||||
pageSize = 1000;
|
||||
}
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
public long getTotalCount() {
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
public void setTotalCount(long totalCount) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* 清除默认排序
|
||||
*/
|
||||
public void clearDefaultOrder(){
|
||||
ascList = null;
|
||||
descList = null;
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取总的页数
|
||||
* @return
|
||||
*/
|
||||
public int getTotalPage() {
|
||||
if(totalCount <= 0){
|
||||
return 0;
|
||||
}
|
||||
return (int)Math.ceil((float) totalCount / pageSize);
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取数据库字段的列排序,用于service层调用
|
||||
* @return
|
||||
*/
|
||||
public List<String> getAscList() {
|
||||
return ascList;
|
||||
}
|
||||
|
||||
/***
|
||||
* 获取数据库字段的列排序,,用于service层调用
|
||||
* @return
|
||||
*/
|
||||
public List<String> getDescList() {
|
||||
return descList;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package com.diboot.commons.vo;
|
||||
|
||||
/**
|
||||
* JSON返回结果
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/01/01
|
||||
*/
|
||||
public class PagingJsonResult extends JsonResult{
|
||||
private static final long serialVersionUID = 1001L;
|
||||
|
||||
/***
|
||||
* 分页相关信息
|
||||
*/
|
||||
private Pagination page;
|
||||
|
||||
/**
|
||||
* 默认成功,无返回数据
|
||||
*/
|
||||
public PagingJsonResult(JsonResult jsonResult, Pagination pagination){
|
||||
super(jsonResult.getCode(), jsonResult.getMsg(), jsonResult.getData());
|
||||
this.page = pagination;
|
||||
}
|
||||
|
||||
public Pagination getPage() {
|
||||
return page;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package com.diboot.commons.vo;
|
||||
|
||||
/**
|
||||
* 状态码定义
|
||||
* @author Mazhicheng
|
||||
* @version v2.0
|
||||
* @date 2019/01/01
|
||||
*/
|
||||
public enum Status {
|
||||
/***
|
||||
* 请求处理成功
|
||||
*/
|
||||
OK(0, "操作成功"),
|
||||
|
||||
/***
|
||||
* 部分成功(一般用于批量处理场景,只处理筛选后的合法数据)
|
||||
*/
|
||||
WARN_PARTIAL_SUCCESS(1001, "部分成功"),
|
||||
|
||||
/***
|
||||
* 有潜在的性能问题
|
||||
*/
|
||||
WARN_PERFORMANCE_ISSUE(1002, "潜在的性能问题"),
|
||||
|
||||
/***
|
||||
* 传入参数不对
|
||||
*/
|
||||
FAIL_INVALID_PARAM(4000, "请求参数不匹配"),
|
||||
|
||||
/***
|
||||
* Token无效或已过期
|
||||
*/
|
||||
FAIL_INVALID_TOKEN(4001, "Token无效或已过期"),
|
||||
|
||||
/***
|
||||
* 没有权限执行该操作
|
||||
*/
|
||||
FAIL_NO_PERMISSION(4003, "无权执行该操作"),
|
||||
|
||||
/***
|
||||
* 请求资源不存在
|
||||
*/
|
||||
FAIL_NOT_FOUND(4004, "请求资源不存在"),
|
||||
|
||||
/***
|
||||
* 数据校验不通过
|
||||
*/
|
||||
FAIL_VALIDATION(4005, "数据校验不通过"),
|
||||
|
||||
/***
|
||||
* 操作执行失败
|
||||
*/
|
||||
FAIL_OPERATION(4006, "操作执行失败"),
|
||||
|
||||
/***
|
||||
* 系统异常
|
||||
*/
|
||||
FAIL_EXCEPTION(5000, "系统异常"),
|
||||
|
||||
/***
|
||||
* 系统异常
|
||||
*/
|
||||
MEMORY_EMPTY_LOST(9999, "缓存清空");
|
||||
|
||||
private int code;
|
||||
private String label;
|
||||
Status(int code, String label){
|
||||
this.code = code;
|
||||
this.label = label;
|
||||
}
|
||||
public int code(){
|
||||
return this.code;
|
||||
}
|
||||
public String label(){
|
||||
return this.label;
|
||||
}
|
||||
public static int getCode(String value){
|
||||
for(Status eu : Status.values()){
|
||||
if(eu.name().equals(value)){
|
||||
return eu.code();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public static String getLabel(String value){
|
||||
for(Status eu : Status.values()){
|
||||
if(eu.name().equals(value)){
|
||||
return eu.label();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -264,18 +264,20 @@ public class V {
|
|||
if (range.contains("-")) {
|
||||
String[] arr = range.split("-");
|
||||
if (notEmpty(arr[0])) {
|
||||
if ((value.length() < Integer.parseInt(arr[0]))) {
|
||||
if (V.isEmpty(value) || value.length() < Integer.parseInt(arr[0])) {
|
||||
errorMsgList.add("长度少于最小限制数: " + arr[0]);
|
||||
}
|
||||
}
|
||||
if (notEmpty(arr[1])) {
|
||||
if (value.length() > Integer.parseInt(arr[1])) {
|
||||
errorMsgList.add("长度超出最大限制数: " + arr[1]);
|
||||
if(V.notEmpty(value)){
|
||||
if (value.length() > Integer.parseInt(arr[1])) {
|
||||
errorMsgList.add("长度超出最大限制数: " + arr[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!(value.length() == Integer.parseInt(range))) {
|
||||
if (V.isEmpty(value) || !(value.length() == Integer.parseInt(range))) {
|
||||
errorMsgList.add("长度限制: " + range + "位");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ dependencies {
|
|||
compile project(":diboot-shiro-wx-mp")
|
||||
// compile project(":diboot-shiro-wx-cp")
|
||||
compile project(":diboot-components-msg")
|
||||
compile project(":dibo-commons-file")
|
||||
compile project(":dibo-commons-excel")
|
||||
|
||||
// 七牛
|
||||
compile ("com.qiniu:qiniu-java-sdk:7.2.7")
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package com.diboot.example.controller;
|
||||
|
||||
import com.diboot.core.config.BaseConfig;
|
||||
import com.diboot.core.util.S;
|
||||
import com.diboot.commons.entity.BaseFile;
|
||||
import com.diboot.commons.file.FileHelper;
|
||||
import com.diboot.commons.service.BaseFileService;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.core.vo.JsonResult;
|
||||
import com.diboot.core.vo.Status;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
@ -16,8 +17,6 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -26,16 +25,24 @@ import java.util.List;
|
|||
public class FileUploadController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileUploadController.class);
|
||||
|
||||
private static String SAVE_PATH = BaseConfig.getProperty("files.storage.directory");
|
||||
@Autowired
|
||||
private BaseFileService baseFileService;
|
||||
|
||||
/*
|
||||
* 单图片
|
||||
* */
|
||||
* 单图片
|
||||
* */
|
||||
@PostMapping("/image")
|
||||
public JsonResult imageUpload(@RequestParam("image") MultipartFile image, ModelMap modelMap, HttpServletRequest request){
|
||||
|
||||
String fileName = saveFile(image);
|
||||
if(V.notEmpty(fileName)){
|
||||
String fileName = image.getOriginalFilename();
|
||||
String ext = fileName.substring(fileName.lastIndexOf(".")+1);
|
||||
String newFileName = com.diboot.commons.utils.S.newUuid() + "." + ext;
|
||||
String filePath = FileHelper.saveImage(image, newFileName);
|
||||
BaseFile file = new BaseFile();
|
||||
file.setName(image.getOriginalFilename());
|
||||
file.setFileType(ext);
|
||||
file.setPath(filePath);
|
||||
baseFileService.createEntity(file);
|
||||
if(V.notEmpty(filePath)){
|
||||
modelMap.put("url", fileName);
|
||||
modelMap.put("status", "done");
|
||||
return new JsonResult(modelMap);
|
||||
|
@ -53,7 +60,7 @@ public class FileUploadController {
|
|||
if(V.notEmpty(images)){
|
||||
List<String> fileNameList = new ArrayList();
|
||||
for(MultipartFile image : images){
|
||||
String fileName = saveFile(image);
|
||||
String fileName = FileHelper.saveImage(image);
|
||||
if(V.notEmpty(fileName)){
|
||||
fileNameList.add(fileName);
|
||||
}else{
|
||||
|
@ -73,11 +80,11 @@ public class FileUploadController {
|
|||
}
|
||||
|
||||
/*
|
||||
* 单文件
|
||||
* */
|
||||
* 单文件
|
||||
* */
|
||||
@PostMapping("/office")
|
||||
public JsonResult officeUpload(@RequestParam("office") MultipartFile office, ModelMap modelMap, HttpServletRequest request){
|
||||
String fileName = saveFile(office);
|
||||
String fileName = FileHelper.saveImage(office);
|
||||
if(V.notEmpty(fileName)){
|
||||
modelMap.put("url", fileName);
|
||||
modelMap.put("status", "done");
|
||||
|
@ -95,7 +102,7 @@ public class FileUploadController {
|
|||
if(V.notEmpty(offices)){
|
||||
List<String> fileNameList = new ArrayList();
|
||||
for(MultipartFile office : offices){
|
||||
String fileName = saveFile(office);
|
||||
String fileName = FileHelper.saveImage(office);
|
||||
if(V.notEmpty(fileName)){
|
||||
fileNameList.add(fileName);
|
||||
}else{
|
||||
|
@ -112,35 +119,4 @@ public class FileUploadController {
|
|||
return new JsonResult(Status.FAIL_OPERATION, modelMap);
|
||||
}
|
||||
|
||||
public String saveFile(MultipartFile file){
|
||||
if(V.isEmpty(file)){
|
||||
return null;
|
||||
}
|
||||
String fileName = file.getOriginalFilename();
|
||||
String ext = fileName.substring(fileName.lastIndexOf(".")+1);
|
||||
String newFileName = S.newUuid() + "." + ext;
|
||||
String fullPath = SAVE_PATH + newFileName;
|
||||
File f = new File(fullPath);
|
||||
if(!f.exists()){
|
||||
try {
|
||||
FileUtils.writeByteArrayToFile(f, file.getBytes());
|
||||
if(logger.isDebugEnabled()){
|
||||
logger.info("文件保存成功");
|
||||
return fullPath;
|
||||
//如果上传文件到七牛,打开此注释
|
||||
/*String link = QiniuHelper.uploadFile2Qiniu(newFileName, SAVE_PATH);
|
||||
if(V.notEmpty(link)){
|
||||
logger.info("文件上传七牛成功");
|
||||
return link;
|
||||
}*/
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("文件保存失败");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package com.diboot.example.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.diboot.commons.entity.BaseFile;
|
||||
import com.diboot.commons.file.FileHelper;
|
||||
import com.diboot.commons.vo.JsonResult;
|
||||
import com.diboot.commons.vo.Pagination;
|
||||
import com.diboot.core.entity.Dictionary;
|
||||
import com.diboot.core.service.BaseService;
|
||||
import com.diboot.core.service.DictionaryService;
|
||||
import com.diboot.core.util.BeanUtils;
|
||||
import com.diboot.core.vo.Status;
|
||||
import com.diboot.example.entity.DictionaryExcelData;
|
||||
import com.diboot.example.listener.DictionaryExcelDataListener;
|
||||
import com.diboot.excel.controller.BaseExcelImportController;
|
||||
import com.diboot.excel.listener.BaseExcelDataListener;
|
||||
import com.diboot.excel.utils.EasyExcelHelper;
|
||||
import com.diboot.shiro.service.RoleService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
/***
|
||||
* 角色数据导入相关操作Controller
|
||||
* @author wangyongliang
|
||||
* @version 2019-7-11
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/roleImport")
|
||||
@Scope("prototype")
|
||||
public class RoleImportController extends BaseExcelImportController {
|
||||
private static final Logger logger = LoggerFactory.getLogger(RoleImportController.class);
|
||||
|
||||
@Autowired
|
||||
private RoleService roleService;
|
||||
|
||||
@Autowired
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
@Autowired
|
||||
private DictionaryExcelDataListener dictionaryExcelDataListener;
|
||||
|
||||
@Override
|
||||
public Class getModelClass() {
|
||||
return Dictionary.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class getExcelDataClass() {
|
||||
return DictionaryExcelData.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseService getBusinessService() {
|
||||
return roleService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseExcelDataListener getExcelDataListener() {
|
||||
return dictionaryExcelDataListener;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 获取文件上传记录(分页)
|
||||
* */
|
||||
@GetMapping("/list")
|
||||
public JsonResult list(Pagination pagination, HttpServletRequest request) throws Exception {
|
||||
LambdaQueryWrapper<BaseFile> wrapper = new LambdaQueryWrapper();
|
||||
return super.listPaging(wrapper, pagination, request);
|
||||
}
|
||||
|
||||
/***
|
||||
* 预览数据
|
||||
* @throws Exception
|
||||
*/
|
||||
@PostMapping("/preview")
|
||||
public JsonResult preview( HttpServletRequest request) throws Exception {
|
||||
return super.preview(request);
|
||||
}
|
||||
|
||||
/***
|
||||
* 保存数据
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
@PostMapping("/previewSave")
|
||||
public JsonResult previewSave(HttpServletRequest request) throws Exception {
|
||||
return super.previewSave(request);
|
||||
}
|
||||
|
||||
/***
|
||||
* 上传excel并保存
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
@PostMapping("/upload")
|
||||
public JsonResult upload(HttpServletRequest request) throws Exception {
|
||||
return super.upload(request);
|
||||
}
|
||||
|
||||
/*
|
||||
* 列表页数据导出
|
||||
* */
|
||||
@GetMapping("/listExport")
|
||||
public JsonResult listExport(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
List<Dictionary> entityList = dictionaryService.getEntityList(null);
|
||||
List<DictionaryExcelData> dataList = BeanUtils.convertList(entityList, DictionaryExcelData.class);
|
||||
String filePath = FileHelper.getFileStorageDirectory()+"元数据导出.xls";
|
||||
//导出数据
|
||||
boolean success = EasyExcelHelper.simpleWrite(filePath, DictionaryExcelData.class, dataList);
|
||||
if(!success){
|
||||
return new JsonResult(Status.FAIL_OPERATION, "文件导出失败");
|
||||
}
|
||||
FileHelper.downloadLocalFile(filePath, request, response);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.diboot.example.entity;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.diboot.excel.converter.StringConverter;
|
||||
import com.diboot.excel.entity.BaseExcelDataEntity;
|
||||
import lombok.Data;
|
||||
/*
|
||||
* dictionary数据导入导出实体类
|
||||
* */
|
||||
@Data
|
||||
public class DictionaryExcelData extends BaseExcelDataEntity {
|
||||
|
||||
@ExcelProperty(value = "类型", converter = StringConverter.class)
|
||||
private String type;
|
||||
|
||||
@ExcelProperty(value = "名称", converter = StringConverter.class)
|
||||
private String itemName;
|
||||
|
||||
@ExcelProperty(value = "值", converter = StringConverter.class)
|
||||
private String itemValue;
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.diboot.example.listener;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.diboot.core.entity.Dictionary;
|
||||
import com.diboot.core.service.BaseService;
|
||||
import com.diboot.core.service.DictionaryService;
|
||||
import com.diboot.core.util.V;
|
||||
import com.diboot.example.entity.DictionaryExcelData;
|
||||
import com.diboot.excel.listener.BaseExcelDataListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
@Scope("prototype")
|
||||
public class DictionaryExcelDataListener extends BaseExcelDataListener<DictionaryExcelData, Dictionary> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(DictionaryExcelDataListener.class);
|
||||
|
||||
@Autowired
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
@Override
|
||||
protected List<String> customVerify(List<DictionaryExcelData> dataList) {
|
||||
if(V.isEmpty(dataList)){
|
||||
return null;
|
||||
}
|
||||
List<String> errorMsgs = new ArrayList();
|
||||
Set isRepeatSet = new HashSet();
|
||||
for(int i=0;i<dataList.size();i++){
|
||||
String error = "";
|
||||
String type = dataList.get(i).getType();
|
||||
if(!isRepeatSet.add(type)){
|
||||
error += "表格中的元数据类型重复";
|
||||
}
|
||||
|
||||
if (V.notEmpty(error)) {
|
||||
errorMsgs.add("[第" + (i + 1) + "行]: " + error);
|
||||
}
|
||||
}
|
||||
|
||||
return errorMsgs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseService getBusinessService() {
|
||||
return dictionaryService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<Dictionary> getModelClass() {
|
||||
return Dictionary.class;
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ public class SysUserVO extends SysUser {
|
|||
@BindDict(type="USER_STATUS", field="status")
|
||||
private String statusLabel;
|
||||
|
||||
@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id AND user_role.user_type='SysUser'")
|
||||
@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id AND user_role.user_type='SysUser' AND user_role.is_deleted = 0")
|
||||
private List<Role> roleList;
|
||||
|
||||
@TableField(exist = false)
|
||||
|
|
|
@ -15,4 +15,7 @@ include 'diboot-shiro-wx-mp'
|
|||
include 'diboot-docs'
|
||||
include 'diboot-shiro-wx-cp'
|
||||
include 'diboot-components-msg'
|
||||
include 'diboot-report'
|
||||
include 'dibo-commons-file'
|
||||
include 'dibo-commons-excel'
|
||||
|
||||
|
|
Loading…
Reference in New Issue