file组件

This commit is contained in:
wyldusername 2019-10-14 18:55:35 +08:00
parent 054e5a28f4
commit 3c6819434b
59 changed files with 6359 additions and 53 deletions

View File

@ -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'
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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()));
}
}
}

View File

@ -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));
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

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

View File

@ -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; // 校验
}

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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> {
}

View File

@ -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>

View File

@ -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> {
}

View File

@ -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>

View File

@ -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> {
}

View File

@ -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);
}

View File

@ -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 {
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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
)
}
}

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View 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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}

View File

@ -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>

View File

@ -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
>

View File

@ -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);
}

View File

@ -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);
/***
* 创建或更新entityentity.id存在则新建否则更新
* @param entity
* @return
*/
boolean createOrUpdateEntity(T entity);
/**
* 批量创建或更新entityentity.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);
}

View File

@ -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;
}
}

View File

@ -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+", 请检查!");
}
}

View File

@ -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 (&lt 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;
}
}

View File

@ -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]+" 值存在 nullBeanUtils.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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}
}
}

View File

@ -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("-", "");
}
}

View File

@ -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));
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 + "");
}
}

View File

@ -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")

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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'