增加服务器公共方法与共用插件
This commit is contained in:
parent
e593b545d4
commit
33d8018999
|
|
@ -0,0 +1,27 @@
|
|||
package com.aisino.iles.common.config;
|
||||
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.FormHttpMessageConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* http客户端配置
|
||||
* @author hx
|
||||
* @since 20250303
|
||||
*/
|
||||
@Configuration
|
||||
public class HttpClientConfig {
|
||||
/**
|
||||
* 提供默认支持的rest http调用客户端
|
||||
* @param builder http客户端构造器
|
||||
* @return http调用客户端
|
||||
*/
|
||||
@Bean
|
||||
public RestTemplate restTemplate(RestTemplateBuilder builder) {
|
||||
return builder
|
||||
.additionalMessageConverters(new FormHttpMessageConverter()) // 支持在RequestEntity body设置form urlencoded参数信息
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.aisino.iles.common.controller;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.aisino.iles.common.util.Constants;
|
||||
import com.aisino.iles.common.model.Ok;
|
||||
import com.aisino.iles.common.model.Result;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 公共/动态日志输出开关
|
||||
* @author hx
|
||||
* @since 2023-07-14
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(value = Constants.NO_AUTH_API_PREFIX, produces = "application/json;charset=UTF-8")
|
||||
@Slf4j
|
||||
public class DynamicLogSwitcherController {
|
||||
/**
|
||||
* 日志开关接口地址
|
||||
*/
|
||||
private final String LOG_SWITCHER_URI = "/log-switcher";
|
||||
// 获取日志工厂会话
|
||||
private final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
|
||||
/**
|
||||
* 日志开关
|
||||
* @param logger 日志
|
||||
* @param level 日志级别
|
||||
*/
|
||||
@PostMapping(LOG_SWITCHER_URI)
|
||||
public Result<?> logSwitcher(String logger, String level){
|
||||
// 调整目标日志级别
|
||||
String msg = StrUtil.format("调整{}的日志级别为{}",logger,level);
|
||||
log.info(msg);
|
||||
loggerContext.getLogger(logger).setLevel(Level.toLevel(level));
|
||||
return Ok.of(msg, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
package com.aisino.iles.common.controller;
|
||||
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.aisino.iles.common.util.Constants;
|
||||
import com.aisino.iles.common.model.Ok;
|
||||
import com.aisino.iles.common.model.Result;
|
||||
import com.aisino.iles.common.model.enums.IndustryCategoryForFile;
|
||||
import com.aisino.iles.common.service.FtpService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RestController
|
||||
@RequestMapping(Constants.NO_AUTH_API_PREFIX)
|
||||
public class FileManagerController {
|
||||
|
||||
private final FtpService ftpService;
|
||||
/**
|
||||
* 获取下载地址
|
||||
*/
|
||||
private static final String DOWNLOAD_URL = "/download-url/**";
|
||||
/**
|
||||
* 下载数据
|
||||
*/
|
||||
private static final String DOWNLOAD_DATA = "/download-data/**";
|
||||
|
||||
public FileManagerController(FtpService ftpService) {
|
||||
this.ftpService = ftpService;
|
||||
}
|
||||
|
||||
@PostMapping(value = "/file/getFileUrl")
|
||||
public Ok<String> getFileUrl(
|
||||
@NotBlank(message = "key不能为空")
|
||||
@RequestParam(required = false)
|
||||
String key
|
||||
) {
|
||||
return Ok.of(ftpService.getFileUrl(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传单个流文件
|
||||
*
|
||||
* @param data 文件数据
|
||||
* @return 上传结果
|
||||
*/
|
||||
@PostMapping(value = "/file/upload", produces = "application/json;charset=UTF-8")
|
||||
public Ok<HashMap<String, Object>> uploadFileBinary(
|
||||
@NotEmpty(message = "文件内容不能为空")
|
||||
@RequestBody(required = false)
|
||||
byte[] data,
|
||||
@NotBlank(message = "文件名不能为空")
|
||||
@RequestHeader(required = false)
|
||||
String fileName,
|
||||
@NotBlank(message = "行业类别不能为空")
|
||||
@RequestHeader(name = "industry-category", required = false)
|
||||
String industryCategory
|
||||
) {
|
||||
try {
|
||||
InputStream is = new ByteArrayInputStream(data);
|
||||
String key;
|
||||
if (fileName.matches("^A/\\d{8}/.+")) {
|
||||
key = fileName;
|
||||
} else {
|
||||
key = FtpService.generateKey(
|
||||
IndustryCategoryForFile.getIndustryCategoryByCode(industryCategory),
|
||||
LocalDate.now(),
|
||||
UUID.randomUUID().toString(),
|
||||
Optional.of(fileName)
|
||||
.map(StrUtil::trimToNull)
|
||||
.map(FileNameUtil::extName)
|
||||
.map(StrUtil::trimToNull)
|
||||
.filter(s -> s.length() <= 4)
|
||||
.map("."::concat)
|
||||
.orElse("")
|
||||
);
|
||||
}
|
||||
String url = ftpService.uploadFileWithFileName(key, is, true);
|
||||
return Ok.of(new HashMap<String, Object>() {{
|
||||
put("saveName", key);
|
||||
put("url", url);
|
||||
}});
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("上传流文件异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件下载连接
|
||||
*
|
||||
* @return 下载地址(在data属性里)
|
||||
*/
|
||||
@GetMapping(DOWNLOAD_URL)
|
||||
public Result<?> downloadUrl(HttpServletRequest req) {
|
||||
String key = req.getRequestURI().split(Constants.NO_AUTH_API_PREFIX + DOWNLOAD_URL.split("\\*\\*")[0])[1];
|
||||
return Ok.of(ftpService.getFileUrl(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载数据内容
|
||||
*
|
||||
* @param req 请求
|
||||
*/
|
||||
@GetMapping(DOWNLOAD_DATA)
|
||||
public void downloadData(HttpServletRequest req, HttpServletResponse res) {
|
||||
String key = req.getRequestURI().split(Constants.NO_AUTH_API_PREFIX + DOWNLOAD_DATA.split("\\*\\*")[0])[1];
|
||||
res.setContentType("application/octet-stream");
|
||||
try (InputStream inputStream = ftpService.getFileAsStream(key);
|
||||
OutputStream outputStream = res.getOutputStream()) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int len;
|
||||
while ((len = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, len);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
package com.aisino.iles.common.controller;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import com.aisino.iles.common.util.Constants;
|
||||
import com.aisino.iles.common.model.Result;
|
||||
import com.aisino.iles.common.model.UploadFile;
|
||||
import com.aisino.iles.common.model.enums.IndustryCategoryForFile;
|
||||
import com.aisino.iles.common.service.FtpService;
|
||||
import com.aisino.iles.common.util.Base64Util;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
|
||||
|
||||
/**
|
||||
* 接收上传文件转发到ftp服务器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(Constants.API_PREFIX)
|
||||
@Validated
|
||||
public class FilesUploadController {
|
||||
private final FtpService ftpService;
|
||||
private final String[] allowFileType = new String[] {"JPG","GIF","PNG","JPEG","BMP","DOC","DOCX","XLS"
|
||||
,"XLSX","PDF","PPT","PPTX","TXT","CVS","TIF","WEBP","TIFF","ZIP","RAR","7Z","MP4","AVI","WMV"
|
||||
,"MOV","JSON","MD","PSD","EXE"};
|
||||
|
||||
public FilesUploadController(FtpService ftpService) {
|
||||
this.ftpService = ftpService;
|
||||
}
|
||||
|
||||
@PostMapping("/uploads")
|
||||
public Map<String, Object> uploads(StandardMultipartHttpServletRequest request) {
|
||||
Map<String, Object> res = new HashMap<>();
|
||||
res.put("result", "success");
|
||||
String industryCategory = request.getHeader("industry-category");
|
||||
IndustryCategoryForFile ic = IndustryCategoryForFile.getIndustryCategoryByCode(industryCategory);
|
||||
if (ic == null) {
|
||||
res.put("result", "failed");
|
||||
res.put("code", 1);
|
||||
res.put("success", false);
|
||||
res.put("msg", "上传失败,请求头参数industry-category为空或无效");
|
||||
return res;
|
||||
}
|
||||
MultiValueMap<String, MultipartFile> multiFileMap = request.getMultiFileMap();
|
||||
Map<String, String> nameMap = new HashMap<>();
|
||||
for (String key : multiFileMap.keySet()) {
|
||||
List<MultipartFile> multipartFiles = multiFileMap.get(key);
|
||||
for (MultipartFile file : multipartFiles) {
|
||||
try {
|
||||
String fileName = file.getOriginalFilename();
|
||||
String[] fileSplit = fileName.split("\\.");
|
||||
if(fileSplit.length==2 &&
|
||||
Arrays.stream(allowFileType).filter(o -> o.equals(fileSplit[1].toUpperCase())).count()==1) {
|
||||
nameMap.put("fileName", fileName);
|
||||
String savePathName = ftpService.uploadTempFile(ic, fileName, file.getInputStream());
|
||||
if (!"".equals(savePathName)) { // 上传成功
|
||||
nameMap.put("savePathName", savePathName);
|
||||
nameMap.put("url", ftpService.getFileUrl(savePathName));
|
||||
nameMap.put("downloadUrl", ftpService.getFileDownloadUrl(savePathName));
|
||||
res.put("result", "success");
|
||||
res.put("code", 0);
|
||||
res.put("success", true);
|
||||
res.put("msg", "上传成功");
|
||||
} else {
|
||||
nameMap.put("savePathName", "");
|
||||
nameMap.put("url", "");
|
||||
res.put("result", "failed");
|
||||
res.put("code", 1);
|
||||
res.put("success", false);
|
||||
res.put("msg", "上传失败");
|
||||
}
|
||||
} else {
|
||||
res.put("success", false);
|
||||
res.put("result", "failed");
|
||||
res.put("code", 1);
|
||||
res.put("msg", fileName + "为不允许的文件类型,上传失败");
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
nameMap.put("savePathName", "");
|
||||
res.put("result", "failed");
|
||||
res.put("code", 1);
|
||||
res.put("success", false);
|
||||
res.put("msg", "上传异常");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
res.put("data", nameMap);
|
||||
return res;
|
||||
}
|
||||
|
||||
@PostMapping("/multipleUpload")
|
||||
public Map<String, Object> multipleUpload(@RequestParam("files") MultipartFile[] multipartFiles,
|
||||
@RequestHeader(value = "industry-category", required = false) String industryCategory) {
|
||||
Map<String, Object> res = new HashMap<>();
|
||||
Map<String, List<String>> nameMap = new HashMap<>();
|
||||
IndustryCategoryForFile ic = IndustryCategoryForFile.getIndustryCategoryByCode(industryCategory);
|
||||
if (ic == null) {
|
||||
res.put("code", 1);
|
||||
res.put("result", "failed");
|
||||
res.put("success", false);
|
||||
res.put("msg", "上传失败,请求头参数industry-category为空或无效");
|
||||
return res;
|
||||
}
|
||||
List<String> fileNameList = new ArrayList<>();
|
||||
List<String> savePathNameList = new ArrayList<>();
|
||||
List<String> fileAccessPaths = new ArrayList<>();
|
||||
if (ArrayUtils.isNotEmpty(multipartFiles)) {
|
||||
for (MultipartFile file : multipartFiles) {
|
||||
try {
|
||||
String fileName = file.getOriginalFilename();
|
||||
String savePathName = ftpService.uploadTempFile(ic, fileName, file.getInputStream());
|
||||
String fileAccessPath = ftpService.getFileUrl(savePathName);
|
||||
if (StringUtils.isNotBlank(savePathName)) { // 上传成功
|
||||
fileNameList.add(fileName);
|
||||
savePathNameList.add(savePathName);
|
||||
fileAccessPaths.add(fileAccessPath);
|
||||
res.put("result", "success");
|
||||
res.put("code", 0);
|
||||
res.put("success", true);
|
||||
res.put("msg", "上传成功");
|
||||
} else {
|
||||
res.put("result", "failed");
|
||||
res.put("code", 1);
|
||||
res.put("success", false);
|
||||
res.put("msg", "文件:" + fileName + ",上传失败!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
res.put("result", "failed");
|
||||
res.put("code", 1);
|
||||
res.put("success", false);
|
||||
res.put("msg", "上传异常");
|
||||
}
|
||||
}
|
||||
nameMap.put("fileNames", fileNameList);
|
||||
nameMap.put("savePathNames", savePathNameList);
|
||||
nameMap.put("fileAccessPaths", fileAccessPaths);
|
||||
}
|
||||
res.put("data", nameMap);
|
||||
return res;
|
||||
}
|
||||
|
||||
@GetMapping("/downloadAsBase64/{savePathName}")
|
||||
public String downloadAsBase64(@PathVariable String savePathName) {
|
||||
String fileSeverUrl = ftpService.getFileUrl(savePathName);
|
||||
return Base64Util.imageBase64FromUrl(fileSeverUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多文件上传base64版本
|
||||
*/
|
||||
@PostMapping("/multiple-upload/base64")
|
||||
public Result<List<UploadFile>> multipleUploadFromBase64(@NotNull(message = "上传文件不能为空")@RequestBody List<UploadFile> uploadFiles,@NotNull(message = "请求头:industry-category行业类别代码不能为空") @RequestHeader(value = "industry-category", required = false) String industryCategory) {
|
||||
IndustryCategoryForFile ic = IndustryCategoryForFile.getIndustryCategoryByCode(industryCategory);
|
||||
if (ic == null) {
|
||||
return Result.of(1, false, "上传失败,请求头参数industry-category为空或无效", null);
|
||||
}
|
||||
return Optional.ofNullable(ftpService.uploadFilesWithBase64(uploadFiles, ic))
|
||||
.map(ufs->Result.of(0, true, "上传成功", ufs, null))
|
||||
.orElseGet(() -> Result.of(1, false, "上传失败,上传文件为空", null));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
* @param names 文件key列表
|
||||
*/
|
||||
@DeleteMapping("/deleteFiles")
|
||||
public void deleteFiles(@RequestBody List<String> names) {
|
||||
names.forEach(ftpService::deletePathFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件post 方式
|
||||
* @param names 文件key列表
|
||||
*/
|
||||
@PostMapping("/deleteFiles")
|
||||
public void deleteBypostFiles(@RequestBody List<String> names) {
|
||||
names.forEach(ftpService::deletePathFile);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.aisino.iles.common.controller;
|
||||
|
||||
import com.aisino.iles.common.util.Constants;
|
||||
import com.aisino.iles.common.model.Ok;
|
||||
import com.aisino.iles.common.model.Result;
|
||||
import com.aisino.iles.common.service.FtpService;
|
||||
import com.aisino.iles.common.util.Base64Util;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(Constants.NO_AUTH_API_PREFIX)
|
||||
@Slf4j
|
||||
public class ImgTransformController {
|
||||
|
||||
private static final String OSS_IMG_URI = "/hycsmobile/bfile/hycszaglxx*/**";
|
||||
private static final Pattern imgRegex = Pattern.compile("/hycsmobile/bfile/hycszaglxx\\d*/(.*)");
|
||||
|
||||
@Resource
|
||||
private FtpService ftpService;
|
||||
|
||||
/**
|
||||
* 图片转base64
|
||||
*
|
||||
* @return JSON格式图片信息
|
||||
*/
|
||||
@GetMapping(OSS_IMG_URI)
|
||||
public Result<Object> imgTransformBase64(HttpServletRequest request) {
|
||||
Matcher imgMatcher = imgRegex.matcher(request.getRequestURI());
|
||||
imgMatcher.find();
|
||||
log.debug("savePathName = {}", imgMatcher.group(1));
|
||||
String savePathName = imgMatcher.group(1);
|
||||
String base64 = Base64Util.FileBase64FromUrl(ftpService.getFileUrl(savePathName, true));
|
||||
return Ok.of(base64);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
package com.aisino.iles.common.iface;
|
||||
|
||||
import com.aisino.iles.common.model.ExcelSheetStruct;
|
||||
import com.aisino.iles.common.model.ExportExcel;
|
||||
import com.aisino.iles.common.util.ExportExcelUtil;
|
||||
import com.aisino.iles.common.util.InvokeUtility;
|
||||
import jxl.format.Alignment;
|
||||
import jxl.format.Border;
|
||||
import jxl.format.BorderLineStyle;
|
||||
import jxl.format.VerticalAlignment;
|
||||
import jxl.write.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* excel表格导出工具
|
||||
* @param <T> 需要导出的数据结构
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ExportExcelHelper<T> extends Logger {
|
||||
/**
|
||||
* 数据分堆提取方法
|
||||
* @param page 页数
|
||||
* @param pageSize 数据限制数
|
||||
* @param total 总数据数
|
||||
* @return 已分页的数据
|
||||
*/
|
||||
Page<T> exportData(Integer page, Integer pageSize, Integer total);
|
||||
|
||||
/**
|
||||
* 默认数据提取方法生成器(简化模式)
|
||||
* @return 一个由已分堆数据和剩余待分堆数据方法
|
||||
*/
|
||||
default Map<List<T>, Supplier<?>> exportDataGenerator() {
|
||||
int page = 1, pageSize = 2000;
|
||||
return this.exportDataGenerator(page, pageSize, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 实际分堆数据提取方法生成器
|
||||
* @param page 页数
|
||||
* @param pageSize 数据限制数
|
||||
* @param totalNumber 总数据数
|
||||
* @return 一个由已分堆数据和剩余待分堆数据方法
|
||||
*/
|
||||
default Map<List<T>, Supplier<?>> exportDataGenerator(Integer page, Integer pageSize, Integer totalNumber) {
|
||||
Page<T> pager = exportData(page, pageSize, totalNumber);
|
||||
Map<List<T>, Supplier<?>> resultMap = new HashMap<>();
|
||||
if (pager.isEmpty()) {
|
||||
return resultMap;
|
||||
} else if (pager.hasNext()) {
|
||||
resultMap.put(pager.getContent(), () -> exportDataGenerator(page + 1, pageSize, ((int) (pager.getTotalElements()))));
|
||||
} else {
|
||||
resultMap.put(pager.getContent(), null);
|
||||
}
|
||||
return resultMap;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入数据到工作区
|
||||
* @param book 工作区
|
||||
* @param labels 表格显示列,和表格字段一一对应
|
||||
* @param props 表格字段,和表格显示列一一对应
|
||||
* @param fileName 表头
|
||||
* @throws WriteException 表格写入异常
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
default void writeToBook(WritableWorkbook book, List<String> labels, List<String> props, String fileName) throws WriteException, IOException {
|
||||
Integer rowLimit = 30000; //单个表单无法超过65535行
|
||||
//内容
|
||||
WritableFont wf2 = new WritableFont(WritableFont.createFont("宋体"), 10);
|
||||
WritableCellFormat wcf2 = new WritableCellFormat(wf2);
|
||||
wcf2.setAlignment(Alignment.CENTRE);
|
||||
wcf2.setVerticalAlignment(VerticalAlignment.CENTRE);
|
||||
|
||||
Map<List<T>, Supplier<?>> dataGeneratorMap = this.exportDataGenerator();
|
||||
writeDataMapToBook(dataGeneratorMap, 0, 0, rowLimit, exportExcel -> {
|
||||
List<T> data = exportExcel.getData();
|
||||
// // 尝试数据加密
|
||||
// data = data.stream().peek(SM4Util::objectSm4Encrypt).collect(Collectors.toList()); 暂时去掉加密
|
||||
int dataIdx = exportExcel.getDataIdx(), sheetIdx = exportExcel.getSheetIdx(), limit = rowLimit;
|
||||
WritableSheet sheet = null;
|
||||
try {
|
||||
sheet = book.getSheet(sheetIdx - 1);
|
||||
} catch (IndexOutOfBoundsException ignored) {
|
||||
}
|
||||
try {
|
||||
if (dataIdx + 1 > limit || dataIdx == 0) {
|
||||
dataIdx = 0;
|
||||
ExcelSheetStruct sheetStruct = createSheetAndInit(book, labels, props, fileName, sheetIdx, dataIdx);
|
||||
sheet = sheetStruct.getSheet();
|
||||
sheetIdx = sheetStruct.getSheetIdx();
|
||||
dataIdx = sheetStruct.getDataIdx();
|
||||
}
|
||||
dataIdx = writeDataToSheet(sheet, wcf2, labels, props, data, dataIdx);
|
||||
} catch (WriteException e) {
|
||||
logger().error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return ExportExcel.<T>builder()
|
||||
.sheetIdx(sheetIdx)
|
||||
.dataIdx(dataIdx)
|
||||
.build();
|
||||
});
|
||||
book.write();
|
||||
book.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新的工作表并且设置好表头以及列信息
|
||||
* @param book 工作区
|
||||
* @param labels 表格显示列信息同 {@link ExportExcelHelper#writeToBook(WritableWorkbook, List, List, String)}
|
||||
* @param props 表格字段信息同 {@link ExportExcelHelper#writeToBook(WritableWorkbook, List, List, String)}
|
||||
* @param fileName 表头信息同 {@link ExportExcelHelper#writeToBook(WritableWorkbook, List, List, String)}
|
||||
* @param sheetIdx 当前工作表索引
|
||||
* @param dataIdx 当前工作表数据行索引
|
||||
* @return 已经初始化的工作表
|
||||
* @throws WriteException 写入异常
|
||||
*/
|
||||
default ExcelSheetStruct createSheetAndInit(WritableWorkbook book, List<String> labels, List<String> props, String fileName, Integer sheetIdx, Integer dataIdx) throws WriteException {
|
||||
int dIdx = dataIdx;
|
||||
//表头
|
||||
WritableFont wf = new WritableFont(WritableFont.createFont("宋体"), 15, WritableFont.BOLD);
|
||||
WritableCellFormat wcf = new WritableCellFormat(wf);
|
||||
wcf.setAlignment(Alignment.CENTRE);
|
||||
wcf.setBorder(Border.ALL, BorderLineStyle.THIN);
|
||||
//标题
|
||||
WritableFont wf1 = new WritableFont(WritableFont.createFont("宋体"), 10, WritableFont.BOLD);
|
||||
WritableCellFormat wcf1 = new WritableCellFormat(wf1);
|
||||
wcf1.setAlignment(Alignment.CENTRE);
|
||||
wcf1.setVerticalAlignment(VerticalAlignment.CENTRE);
|
||||
|
||||
|
||||
WritableSheet sheet = book.createSheet("第" + (sheetIdx + 1) + "页", sheetIdx);
|
||||
|
||||
sheet.mergeCells(0, 0, labels.size() - 1, 0);
|
||||
//表标题(第一行)(列号,行号,值,样式)
|
||||
Label label = new Label(0, 0, fileName, wcf);
|
||||
sheet.addCell(label);
|
||||
dIdx += 1;
|
||||
//表头(第二行)(列号,行号,值,样式)
|
||||
IntStream.range(0, labels.size()).forEach(n -> {
|
||||
try {
|
||||
sheet.addCell(new Label(n, 1, labels.get(n), wcf1));
|
||||
} catch (WriteException e) {
|
||||
logger().error(e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
dIdx += 1;
|
||||
|
||||
return ExcelSheetStruct.builder().dataIdx(dIdx).sheet(sheet).sheetIdx(sheetIdx + 1).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 把生成器数据集写入工作区
|
||||
* @param dataMap 生成器数据集
|
||||
* @param dataIdx 当前工作表数据索引
|
||||
* @param sheetIdx 当前工作表索引
|
||||
* @param limit 工作表最大数据限制
|
||||
* @param dataConsumer 把数据写入工作表的参数 {@link ExportExcel} 返回 {@link ExportExcel}的方法
|
||||
*/
|
||||
default void writeDataMapToBook(Map<List<T>, Supplier<?>> dataMap, Integer dataIdx, Integer sheetIdx, Integer limit, Function<ExportExcel<T>, ExportExcel<T>> dataConsumer) {
|
||||
dataMap.entrySet().stream().findFirst().ifPresent(e -> {
|
||||
List<T> data = e.getKey();
|
||||
ExportExcel<T> result = null;
|
||||
int processNumber = 0, dataIndex = dataIdx, sheetIndex = sheetIdx;
|
||||
while (processNumber < data.size()) {
|
||||
List<T> slice = data.stream().skip(processNumber).limit(limit).collect(Collectors.toList());
|
||||
result = dataConsumer.apply(ExportExcel.<T>builder()
|
||||
.data(slice)
|
||||
.dataIdx(dataIndex)
|
||||
.sheetIdx(sheetIndex)
|
||||
.build());
|
||||
dataIndex = result.getDataIdx();
|
||||
sheetIndex = result.getSheetIdx();
|
||||
processNumber += slice.size();
|
||||
}
|
||||
// 数据呈现为链式,可通过自我递归调用,完成数据处理
|
||||
Supplier<Map<List<T>, Supplier<?>>> next = (Supplier<Map<List<T>, Supplier<?>>>) e.getValue();
|
||||
if (next != null) {
|
||||
assert result != null;
|
||||
writeDataMapToBook(next.get(), dataIndex, sheetIndex, limit, dataConsumer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入数据到工作表
|
||||
* @param sheet 工作表
|
||||
* @param wcf 数据格式
|
||||
* @param labels {@link ExportExcelHelper#writeToBook(WritableWorkbook, List, List, String)}
|
||||
* @param props {@link ExportExcelHelper#writeToBook(WritableWorkbook, List, List, String)}
|
||||
* @param data 数据信息
|
||||
* @param dataIdx 当前工作表数据索引
|
||||
* @return 数据写入后的数据索引
|
||||
* @throws WriteException 写入异常
|
||||
*/
|
||||
default int writeDataToSheet(WritableSheet sheet, WritableCellFormat wcf, List<String> labels, List<String> props, List<T> data, Integer dataIdx) throws WriteException {
|
||||
//内容(列号,行号,值,样式)
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
Object obj = data.get(i);
|
||||
for (int n = 0; n < labels.size(); n++) {
|
||||
String value = InvokeUtility.getFieldValueByName(props.get(n), obj) == null ? "" : InvokeUtility.getFieldValueByName(props.get(n), obj).toString();
|
||||
sheet.addCell(new Label(n, i + dataIdx, value, wcf));
|
||||
}
|
||||
}
|
||||
|
||||
// 设置列宽度
|
||||
IntStream.range(0, labels.size()).mapToObj(colIdx -> new Integer[]{colIdx, Arrays.stream(sheet.getColumn(colIdx)).map(c -> ExportExcelUtil.length(c.getContents()))
|
||||
.max(Comparator.comparingInt(a -> a)).orElse(0)})
|
||||
.forEach(c -> sheet.setColumnView(c[0], c[1] + 2));
|
||||
|
||||
return data.size()+ dataIdx;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.aisino.iles.common.iface;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
public interface Logger {
|
||||
/**
|
||||
* 默认日志
|
||||
* @return 日志对象
|
||||
*/
|
||||
default Log logger() {
|
||||
return LogFactory.getLog(this.getClass());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@ToString
|
||||
@ConfigurationProperties(prefix = "custom.aws")
|
||||
@Data
|
||||
public class AwsConfig {
|
||||
private String accessKey;
|
||||
private String secretKey;
|
||||
private String bucket;
|
||||
private String endpoint;
|
||||
private String filePath;
|
||||
/**
|
||||
* 用于大文件本地临时存放(多节点运行时需要配置为共享目录)
|
||||
*/
|
||||
private String tempPath = "files";
|
||||
/**
|
||||
* 修改http访问时的url中的endpoint部分
|
||||
*/
|
||||
private String alterHttp;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
public interface CreatedWire {
|
||||
void wiring();
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import jxl.write.WritableSheet;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class ExcelSheetStruct {
|
||||
private Integer dataIdx;
|
||||
private Integer sheetIdx;
|
||||
private WritableSheet sheet;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 导出excel的结构
|
||||
* @author firefoxmmx2
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
public class ExportExcel<T> {
|
||||
/**
|
||||
* 需要导出的数据
|
||||
*/
|
||||
private List<T> data;
|
||||
/**
|
||||
* 当前工作表里的数据索引
|
||||
*/
|
||||
private Integer dataIdx;
|
||||
/**
|
||||
* 当前工作表索引
|
||||
*/
|
||||
private Integer sheetIdx;
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Builder
|
||||
@Data
|
||||
public class ExportExcelParam<T> {
|
||||
private List<String> labels;
|
||||
private List<String> props;
|
||||
private String fileName;
|
||||
private Map<String, Object> params;
|
||||
private T obj;
|
||||
|
||||
public ExportExcelParam() {
|
||||
|
||||
}
|
||||
|
||||
public ExportExcelParam(List<String> labels, List<String> props, String fileName, Map<String, Object> params, T obj) {
|
||||
this.labels = labels;
|
||||
this.props = props;
|
||||
this.fileName = fileName;
|
||||
this.params = params;
|
||||
this.obj = obj;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class FaceValid implements Serializable {
|
||||
private Float score;
|
||||
private Integer threshold;
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import com.aisino.iles.common.util.Constants;
|
||||
|
||||
public class Fail<T> extends Result<T> {
|
||||
public Fail(String message, int code) {
|
||||
super();
|
||||
this.success = false;
|
||||
this.msg = message;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Fail(String message) {
|
||||
super();
|
||||
this.success = false;
|
||||
this.msg = message;
|
||||
this.code = Constants.Exceptions.default_error;
|
||||
}
|
||||
|
||||
public Fail() {
|
||||
super();
|
||||
this.success = false;
|
||||
this.code = Constants.Exceptions.default_error;
|
||||
this.msg = "错误";
|
||||
}
|
||||
public static <T> Fail<T> of() {
|
||||
return new Fail<T>();
|
||||
}
|
||||
public static <T> Fail<T> of(String message) {
|
||||
return new Fail<T>(message);
|
||||
}
|
||||
public static <T> Fail<T> of(String message, int code) {
|
||||
return new Fail<T>(message, code);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
/**
|
||||
* 提供给JsonView工具使用的试图
|
||||
*/
|
||||
public interface ForJsonView {
|
||||
interface View {}
|
||||
interface PageView extends View {}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Message {
|
||||
/**
|
||||
* 消息键
|
||||
*/
|
||||
private Object key;
|
||||
/**
|
||||
* 消息值
|
||||
*/
|
||||
private Object value;
|
||||
/**
|
||||
* 消息主题(支持多个)
|
||||
*/
|
||||
private String[] topics;
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties("messages")
|
||||
@Data
|
||||
public class MessageProperties {
|
||||
private String loginUrl;
|
||||
private String sendUrl;
|
||||
private String username;
|
||||
private String password;
|
||||
private Topics topic;
|
||||
|
||||
|
||||
@Data
|
||||
public static class Topics {
|
||||
private String earlyWarning;
|
||||
private String alarmInfo;
|
||||
private String carInfo;
|
||||
private String hotelAdditionalProcess;
|
||||
private String onlineHouseInfo;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OcrValid implements Serializable {
|
||||
private Integer cardType;
|
||||
private List<CardInfo> items = new ArrayList<CardInfo>();
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public static final class CardInfo implements Serializable {
|
||||
private String desc;
|
||||
private String content;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class OcrValidResult implements Serializable {
|
||||
/**
|
||||
* 所选择的比对证件类型
|
||||
*/
|
||||
private String cardType;
|
||||
/**
|
||||
* 证件号码
|
||||
*/
|
||||
private String zjhm;
|
||||
/**
|
||||
* 护照号码
|
||||
*/
|
||||
private String hzhm;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String tx;
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
private String xb;
|
||||
/**
|
||||
* 性别代码
|
||||
*/
|
||||
private String xbdm;
|
||||
/**
|
||||
* 民族
|
||||
*/
|
||||
private String mz;
|
||||
/**
|
||||
* 民族代码
|
||||
*/
|
||||
private String mzdm;
|
||||
/**
|
||||
* 国籍代码
|
||||
*/
|
||||
private String gjdm;
|
||||
/**
|
||||
* 户籍地址
|
||||
*/
|
||||
private String hjdz;
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
private String xm;
|
||||
/**
|
||||
* 出生日期
|
||||
*/
|
||||
private String csrq;
|
||||
/**
|
||||
* 签发国代码
|
||||
*/
|
||||
private String qfgdm;
|
||||
/**
|
||||
* 英文姓
|
||||
*/
|
||||
private String ywx;
|
||||
/**
|
||||
* 英文名
|
||||
*/
|
||||
private String ywm;
|
||||
/**
|
||||
* 英文姓名
|
||||
*/
|
||||
private String ywxm;
|
||||
/**
|
||||
* 签发地点
|
||||
*/
|
||||
private String qfdd;
|
||||
/**
|
||||
* 签发机关
|
||||
*/
|
||||
private String qfjg;
|
||||
/**
|
||||
* 行政区划
|
||||
*/
|
||||
private String xzqh;
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class Ok<T> extends Result<T> {
|
||||
public Ok() {
|
||||
this.code = 0;
|
||||
this.success = true;
|
||||
}
|
||||
public Ok(T data) {
|
||||
super(data);
|
||||
this.code = 0;
|
||||
this.success = true;
|
||||
}
|
||||
|
||||
public Ok(String msg, int code){
|
||||
super(code, true, msg, null);
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.success = true;
|
||||
}
|
||||
|
||||
public Ok(T data, String msg){
|
||||
this(data);
|
||||
this.msg=msg;
|
||||
}
|
||||
|
||||
public static <T> Ok<T> of() {
|
||||
return new Ok<>();
|
||||
}
|
||||
|
||||
public static <T> Ok<T> of(T data) {
|
||||
return new Ok<>(data);
|
||||
}
|
||||
|
||||
public static <T> Ok<T> of(T data, String msg) {
|
||||
return new Ok<>(data, msg);
|
||||
}
|
||||
|
||||
public static <T> Ok<T> of(String msg, int code) {
|
||||
return new Ok<>(msg, code);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import com.aisino.iles.common.util.Constants;
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 分页结果集
|
||||
*
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class PageResult<T> extends Result<Collection<T>> {
|
||||
/**
|
||||
* 总记录数
|
||||
*/
|
||||
@JsonView(ForJsonView.PageView.class)
|
||||
Long total;
|
||||
/**
|
||||
* 当前页
|
||||
*/
|
||||
@JsonView(ForJsonView.PageView.class)
|
||||
Integer page;
|
||||
/**
|
||||
* 每页记录数
|
||||
*/
|
||||
@JsonView(ForJsonView.PageView.class)
|
||||
Integer limit;
|
||||
/**
|
||||
* 总页数
|
||||
*/
|
||||
@JsonView(ForJsonView.PageView.class)
|
||||
Integer totalPages;
|
||||
/**
|
||||
* 数量关系
|
||||
*/
|
||||
@JsonView(ForJsonView.PageView.class)
|
||||
String relation;
|
||||
|
||||
public PageResult(Page<T> page) {
|
||||
super();
|
||||
total = page.getTotalElements();
|
||||
totalPages = page.getTotalPages();
|
||||
limit = page.getSize();
|
||||
this.page = page.getNumber();
|
||||
|
||||
this.code = 0;
|
||||
this.data = page.getContent();
|
||||
this.success = true;
|
||||
if (total < Constants.GLOBAL_QUERY_LIMIT || total > Constants.GLOBAL_QUERY_LIMIT) {
|
||||
// 等于
|
||||
this.relation = "eq";
|
||||
} else {
|
||||
// 大于或者等于
|
||||
this.relation = "gte";
|
||||
}
|
||||
}
|
||||
|
||||
public PageResult(Long totalrow, Integer totalpages, Integer limitrow, Integer curpage, Collection<T> datares) {
|
||||
super();
|
||||
total = totalrow;
|
||||
totalPages = totalpages;
|
||||
limit = limitrow;
|
||||
this.page = curpage;
|
||||
|
||||
this.code = 0;
|
||||
this.data = datares;
|
||||
this.success = true;
|
||||
}
|
||||
|
||||
public static <T> PageResult<T> of(Page<T> page) {
|
||||
return new PageResult<>(page);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 文员忘证数据
|
||||
* @author hx
|
||||
* @since 2024-01-30
|
||||
*/
|
||||
@Data
|
||||
public class PersonnelForgotIdentifyDto {
|
||||
/**
|
||||
* 人员唯一标识
|
||||
*/
|
||||
@NotBlank(message = "人员唯一标识不能为空")
|
||||
private String personId;
|
||||
/**
|
||||
* 人员姓名
|
||||
*/
|
||||
@NotBlank(message = "人员姓名不能为空")
|
||||
@Size(message = "人员姓名长度不能超过20",max = 20)
|
||||
private String name;
|
||||
/**
|
||||
* 身份证号码
|
||||
*/
|
||||
@NotBlank(message = "身份证号码不能为空")
|
||||
@Size(message = "身份证号码不能超过18",max = 18)
|
||||
private String certNumber;
|
||||
/**
|
||||
* 现场照片base64
|
||||
*/
|
||||
@NotBlank(message = "现场照片不能为空")
|
||||
private String photoStr;
|
||||
|
||||
/**
|
||||
* mqtt客户端id
|
||||
*/
|
||||
@NotBlank(message = "mqtt客户端id不能为空")
|
||||
@Size(message = "mqtt客户端id不能超过20",max = 20)
|
||||
private String mqttClientId;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import com.aisino.iles.common.util.BeanUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.Arrays;
|
||||
|
||||
public interface PkgDto<T> {
|
||||
|
||||
Serializable id();
|
||||
|
||||
T toEntity();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
default T simpleData() {
|
||||
return Arrays.stream(this.getClass().getGenericInterfaces())
|
||||
.filter(type -> type.getTypeName().startsWith(PkgDto.class.getName()))
|
||||
.map(i -> (ParameterizedType) i)
|
||||
.map(p -> (Class<T>) p.getActualTypeArguments()[0])
|
||||
.findFirst()
|
||||
.map(tp -> {
|
||||
try {
|
||||
return tp.newInstance();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}).map(t -> {
|
||||
BeanUtils.copyNoNullProperties(this, t);
|
||||
return t;
|
||||
}).orElseThrow(() -> new RuntimeException("类型转换错误:" + id()));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
@Data
|
||||
public class Result<T> {
|
||||
@JsonView(ForJsonView.View.class)
|
||||
Integer code;
|
||||
@JsonView(ForJsonView.View.class)
|
||||
Boolean success;
|
||||
@JsonView(ForJsonView.View.class)
|
||||
String msg;
|
||||
@JsonView(ForJsonView.View.class)
|
||||
T data;
|
||||
@JsonView(ForJsonView.View.class)
|
||||
Exception exception;
|
||||
|
||||
public Result(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Result() {
|
||||
}
|
||||
|
||||
public Result(Integer code, Boolean success, String msg, T data, Exception exception) {
|
||||
this(code, success, msg, exception);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Result(Integer code, Boolean success, String msg, Exception exception) {
|
||||
this.code = code;
|
||||
this.success = success;
|
||||
this.msg = msg;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
public static <T> Result<T> of(T data) {
|
||||
return new Result<>(data);
|
||||
}
|
||||
|
||||
public static <T> Result<T> of(Integer code, Boolean success, String msg, T data, Exception exception) {
|
||||
return new Result<>(code, success, msg, data, exception);
|
||||
}
|
||||
|
||||
public static <T> Result<T> of(Integer code, Boolean success, String msg, Exception exception) {
|
||||
return new Result<>(code, success, msg, exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Configuration
|
||||
public class SmsEnvConfig {
|
||||
|
||||
@ConfigurationProperties(prefix = "sms.outer")
|
||||
@Bean(name = "outerSms")
|
||||
public SmsData outerSmsData(){
|
||||
return new SmsData();
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "sms.inner")
|
||||
@Bean(name = "innerSms")
|
||||
public SmsData innerSmsData(){
|
||||
return new SmsData();
|
||||
}
|
||||
|
||||
@Component
|
||||
@Data
|
||||
public static class SmsData {
|
||||
private String userCode; //用户编码
|
||||
private String interfaceCode; //接口编码
|
||||
private String dataSecret; //数据公钥
|
||||
private String interfaceParamsSecret; //接口参数公钥
|
||||
private String url;//接口路径
|
||||
private String ftpLocalPath; // ftp服务的本地路径
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.aisino.iles.common.model;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 上传文件封装类型
|
||||
*
|
||||
* @author hx
|
||||
* @since 2022-03-16
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(of = "fileName")
|
||||
public class UploadFile {
|
||||
/** 文件名 */
|
||||
@NotEmpty(message="文件名不能为空")
|
||||
@NotNull(message = "文件名不能为空")
|
||||
private String fileName;
|
||||
/** 文件保存 */
|
||||
private String path;
|
||||
/** 文件访问路径 */
|
||||
private String accessUrl;
|
||||
/** 文件后缀名 */
|
||||
private String ext;
|
||||
/** 文件内容 base64 */
|
||||
private String contentStr;
|
||||
/** 文件内容 流 */
|
||||
private InputStream content;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package com.aisino.iles.common.model.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 文件类型
|
||||
*/
|
||||
public enum ContentType {
|
||||
wmv("video/x-ms-wmv", ".wmv"),
|
||||
mpeg("video/mpg", ".mpeg"),
|
||||
mpg("video/mpg", ".mpg"),
|
||||
avi("video/avi", ".avi"),
|
||||
mp4("video/mpeg4", ".mp4"),
|
||||
pptx("application/vnd.openxmlformats-officedocument.presentationml.presentation", ".pptx"),
|
||||
ppt("application/vnd.ms-powerpoint", ".ppt"),
|
||||
xlsx("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", ".xlsx"),
|
||||
doc("application/msword", ".doc"),
|
||||
docx("application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".docx"),
|
||||
gzip("application/gzip", ".gzip"),
|
||||
zip("application/zip", ".zip"),
|
||||
pdf("application/pdf", ".pdf"),
|
||||
xls("application/vnd.ms-excel", ".xls"),
|
||||
png("image/png", ".png"),
|
||||
jpg("image/jpeg", ".jpg"),
|
||||
jpe("image/jpeg", ".jpe"),
|
||||
ico("image/x-icon", ".ico"),
|
||||
jpeg("image/jpeg", ".jpeg");
|
||||
|
||||
private String contentTypeName;
|
||||
private String fileExtendName;
|
||||
|
||||
ContentType(String contentTypeName, String fileExtendName) {
|
||||
this.contentTypeName = contentTypeName;
|
||||
this.fileExtendName = fileExtendName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据扩展名获取ContentType
|
||||
*
|
||||
* @param fileExtendName
|
||||
* @return
|
||||
*/
|
||||
public static String getContentTypeNameByFileExtendName(String fileExtendName) {
|
||||
return Arrays.stream(ContentType.values())
|
||||
.filter(c -> c.fileExtendName.equalsIgnoreCase(fileExtendName))
|
||||
.map(ContentType::getContentTypeName)
|
||||
.findFirst()
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
public String getContentTypeName() {
|
||||
return contentTypeName;
|
||||
}
|
||||
|
||||
public void setContentTypeName(String contentTypeName) {
|
||||
this.contentTypeName = contentTypeName;
|
||||
}
|
||||
|
||||
public String getFileExtendName() {
|
||||
return fileExtendName;
|
||||
}
|
||||
|
||||
public void setFileExtendName(String fileExtendName) {
|
||||
this.fileExtendName = fileExtendName;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.aisino.iles.common.model.enums;
|
||||
|
||||
/**
|
||||
* 行业类别
|
||||
*/
|
||||
public enum IndustryCategoryForFile {
|
||||
pub("公共模块", "PUB"),
|
||||
portal("门户", "PORTAL"),
|
||||
hotel("旅馆行业", "A"),
|
||||
wyf("网约房行业", "A06"),
|
||||
seal("印章业", "B"),
|
||||
vehiclerecycling("报废机动车", "C01"),
|
||||
mechanicalrepair("机修业", "C02"),
|
||||
printing("印刷业", "D"),
|
||||
usedcar("二手车交易业", "E01"),
|
||||
usedgoods("旧货交易业", "E02"),
|
||||
pawn("典当行业", "E03"),
|
||||
scrapmetal("废旧金属收购业", "E04"),
|
||||
consignment("委托寄卖业", "E07"),
|
||||
carrental("汽车租赁业", "F02"),
|
||||
entertainment("营业性娱乐场所", "J"),
|
||||
additivemanufacturing("增材制造业", "M"),
|
||||
publictransittransport("公共交通运输行业", "P"),
|
||||
security("保安业", "T"),
|
||||
unlock("开锁业", "Y01"),
|
||||
bigfile("大文件", "BF");
|
||||
|
||||
private String name;
|
||||
private String code;
|
||||
|
||||
IndustryCategoryForFile(String name, String code) {
|
||||
this.name = name;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static IndustryCategoryForFile getIndustryCategoryByCode(String code) {
|
||||
if (code != null) {
|
||||
for (IndustryCategoryForFile ic : IndustryCategoryForFile.values()) {
|
||||
if (ic.getCode().equals(code)) {
|
||||
return ic;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.aisino.iles.common.model.enums;
|
||||
|
||||
/**
|
||||
* 通用删除标志
|
||||
*
|
||||
* @author huxin
|
||||
* @since 2020-07-03
|
||||
*/
|
||||
public enum WhetherFlag {
|
||||
yes("1"), // 是
|
||||
no("0"); // 否
|
||||
private String value;
|
||||
|
||||
WhetherFlag(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
package com.aisino.iles.common.repository;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 公共自定义分页查询组件
|
||||
* <p>
|
||||
* 这个组件在查询上面做了优化分页统计,针对数据数量比较的查询来说,
|
||||
* 每一次统计总条数的开销都是巨大的。组件只为第一页数据查询的时候提供统计总条数服务,
|
||||
* 之后的查询不在去统计。统计和查询使用了异步的方式处理,这样可以减少一些查询等待时间。
|
||||
*
|
||||
* @param <T> 领域实例类型
|
||||
* @author huxin
|
||||
* @since 2019-10-29
|
||||
*/
|
||||
@NoRepositoryBean
|
||||
public interface PagingAndSortingSpecificationRepository<T> {
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param specification 动态查询条件
|
||||
* @param pager 分页器
|
||||
* @param total 总数据数
|
||||
* @param returnClass 返回类型
|
||||
* @param <R> 返回类型站位符
|
||||
* @return 分页数据
|
||||
*/
|
||||
<R> Page<R> findAll(@Nullable Specification<T> specification, Pageable pager, long total, Class<R> returnClass);
|
||||
|
||||
/**
|
||||
* 分页查询,多提供一个设置查询提示的功能
|
||||
*
|
||||
* @param specification 动态查询条件
|
||||
* @param pager 分页器
|
||||
* @param total 总数据数
|
||||
* @param returnClass 返回类型
|
||||
* @param queryHints 查询提示
|
||||
* @param <R> 返回类型占位符
|
||||
* @return 分页数据
|
||||
*/
|
||||
<R> Page<R> findAll(@Nullable Specification<T> specification, Pageable pager, long total, Class<R> returnClass, Map<String, Object> queryHints);
|
||||
|
||||
/**
|
||||
* 列表查询
|
||||
* {@link PagingAndSortingSpecificationRepository#findAll }
|
||||
*
|
||||
* @return 返回列表数据
|
||||
*/
|
||||
<R> List<R> findAll(@Nullable Specification<T> specification, Class<R> returnClass);
|
||||
|
||||
/**
|
||||
* {@link PagingAndSortingSpecificationRepository#findAll }
|
||||
* return 返回列表数据
|
||||
*/
|
||||
<R> List<R> findAll(@Nullable Specification<T> specification, Class<R> returnClass, Map<String, Object> queryHints);
|
||||
|
||||
/**
|
||||
* 统计查询总数据数
|
||||
*
|
||||
* @param specification 动态查询条件
|
||||
* @return 总数据数
|
||||
*/
|
||||
long count(@Nullable Specification<T> specification);
|
||||
|
||||
/**
|
||||
* 带实体图的列表查询,可提高查询性能 (实体图的作用是可以管理,查询需要的属性和实体,而不是默认的字段属性)
|
||||
*
|
||||
* @param specification 动态查询条件
|
||||
* @param entityGraph 实体图名称
|
||||
* @return 列表数据
|
||||
*/
|
||||
List<T> findAll(@Nullable Specification<T> specification, String entityGraph);
|
||||
|
||||
/**
|
||||
* 查询实体列表,提供自定义查询提示优化查询(查询提示包含实体图)
|
||||
*
|
||||
* @param specification 动态查询条件
|
||||
* @param queryHints 查询提示
|
||||
* @return 列表数据
|
||||
*/
|
||||
List<T> findAll(@Nullable Specification<T> specification, Map<String, Object> queryHints);
|
||||
|
||||
/**
|
||||
* 带实体图的分页查询,可提高查询性能 (实体图的作用是可以管理,查询需要的属性和实体,而不是默认的字段属性)
|
||||
*
|
||||
* @param specification 动态查询条件
|
||||
* @param page 分页信息
|
||||
* @param entityGraph 实体图名称 实体图使用的抓取模式是fetch
|
||||
* @return 分页数据
|
||||
*/
|
||||
Page<T> findAll(@Nullable Specification<T> specification, Pageable page, String entityGraph);
|
||||
|
||||
/**
|
||||
* 分页查询,提供查询提示功能
|
||||
*
|
||||
* @param specification 动态查询条件
|
||||
* @param page 分页信息
|
||||
* @param queryHints 查询提示
|
||||
* @return 分页数据
|
||||
*/
|
||||
Page<T> findAll(@Nullable Specification<T> specification, Pageable page, Map<String, Object> queryHints);
|
||||
|
||||
/**
|
||||
* 带实体图和数据总量的分页查询,不会触发重复统计提高查询性能 (实体图的作用是可以管理,查询需要的属性和实体,而不是默认的字段属性)
|
||||
*
|
||||
* @param specification 动态查询条件
|
||||
* @param page 分页信息
|
||||
* @param total 数据总数
|
||||
* @param entityGraph 实体图名称
|
||||
* @return 分页数据
|
||||
*/
|
||||
Page<T> findAll(@Nullable Specification<T> specification, Pageable page, long total, String entityGraph);
|
||||
|
||||
/**
|
||||
* 分页查询,提供数据总数(总数不为0不会重新去统计数量,提高查询速度),提供查询提示功能
|
||||
*
|
||||
* @param specification 动态查询条件
|
||||
* @param page 分页信息
|
||||
* @param total 数据总数
|
||||
* @param queryHints 查询提示
|
||||
* @return 分页数据
|
||||
*/
|
||||
Page<T> findAll(@Nullable Specification<T> specification, Pageable page, long total, Map<String, Object> queryHints);
|
||||
|
||||
/**
|
||||
* 带实体图和排序的列表查询 (实体图的作用是可以管理,查询需要的属性和实体,而不是默认的字段属性)
|
||||
*
|
||||
* @param specification 动态查询条件
|
||||
* @param sort 分页信息
|
||||
* @param entityGraph 实体图名称
|
||||
* @return 列表数据
|
||||
*/
|
||||
List<T> findAll(@Nullable Specification<T> specification, Sort sort, String entityGraph);
|
||||
|
||||
/**
|
||||
* 列表查询,提供排序和查询提示功能
|
||||
*
|
||||
* @param specification 动态分页条件
|
||||
* @param sort 排序
|
||||
* @param queryHints 查询提示
|
||||
* @return 列表数据
|
||||
*/
|
||||
List<T> findAll(@Nullable Specification<T> specification, Sort sort, Map<String, Object> queryHints);
|
||||
|
||||
/**
|
||||
* 统计查询总数据数
|
||||
*
|
||||
* @param specification 动态查询条件
|
||||
* @param limit 是否强制限制查询数量
|
||||
* @return 总数据数
|
||||
*/
|
||||
long count(@Nullable Specification<T> specification, boolean limit);
|
||||
|
||||
Page<T> findAll(@Nullable Specification<T> specification, Pageable page, long total, boolean limit);
|
||||
|
||||
<R> Page<R> findAll(@Nullable Specification<T> specification, Pageable page, long total, boolean limit, Class<R> returnClass, Map<String, Object> queryHints);
|
||||
Page<T> findAll(@Nullable Specification<T> specification, Pageable page, long total, boolean limit, String entityGraph);
|
||||
<R> Page<R> findAll(@Nullable Specification<T> specification, Pageable page, long total, boolean limit, Class<R> returnClass);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.aisino.iles.common.repository;
|
||||
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@NoRepositoryBean
|
||||
public interface SingleResultRepository<T, ID> {
|
||||
/**
|
||||
* 带实体图通过ID获取数据信息
|
||||
*
|
||||
* @param id 主键
|
||||
* @param entityGraph 实体图名称
|
||||
* @return 一个可能为空的数据信息(可能通过id无法查询到信息)
|
||||
*/
|
||||
Optional<T> findById(@NonNull ID id, @Nullable String entityGraph);
|
||||
}
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
package com.aisino.iles.common.repository.impl;
|
||||
|
||||
import com.aisino.iles.common.util.Constants;
|
||||
import com.aisino.iles.common.repository.PagingAndSortingSpecificationRepository;
|
||||
import com.aisino.iles.common.repository.SingleResultRepository;
|
||||
import com.aisino.iles.common.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hibernate.CacheMode;
|
||||
import org.hibernate.jpa.QueryHints;
|
||||
import org.hibernate.query.Query;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.query.QueryUtils;
|
||||
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
|
||||
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import jakarta.persistence.EntityGraph;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* 数据访问层抽象公共实现
|
||||
* 作用于基于spring-data-jpa实现repository数据接口,例如SocialcollectDataSource之类
|
||||
* <p>
|
||||
* @author hx
|
||||
* @since 2020-08-10
|
||||
* @param <T> 实体类型
|
||||
* @param <ID> 主键
|
||||
*/
|
||||
@Slf4j
|
||||
public class CommonRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements PagingAndSortingSpecificationRepository<T>, SingleResultRepository<T, ID> {
|
||||
private final EntityManager entityManager;
|
||||
private final JpaEntityInformation<T, ID> entityInformation;
|
||||
|
||||
public CommonRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
|
||||
super(entityInformation, entityManager);
|
||||
this.entityManager = entityManager;
|
||||
this.entityInformation = entityInformation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> Page<R> findAll(Specification<T> specification, Pageable pager, long totalNumber, Class<R> returnClass) {
|
||||
return findAll(specification, pager, totalNumber, false, returnClass, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> Page<R> findAll(Specification<T> specification, Pageable pager, long total, Class<R> returnClass, Map<String, Object> queryHints) {
|
||||
return findAll(specification, pager, total,false, returnClass , queryHints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> List<R> findAll(Specification<T> specification, Class<R> returnClass) {
|
||||
return findAll(specification, Pageable.unpaged(), 0, returnClass).getContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> List<R> findAll(Specification<T> specification, Class<R> returnClass, Map<String, Object> queryHints) {
|
||||
return findAll(specification, Pageable.unpaged(), 0L, returnClass, queryHints).getContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(Specification<T> specification) {
|
||||
return this.getCountQuery(specification, this.getDomainClass()).getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> findAll(Specification<T> specification, String entityGraph) {
|
||||
return this.getQuery(specification, this.getDomainClass(), Sort.unsorted(), entityGraph).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> findAll(Specification<T> specification, Map<String, Object> queryHints) {
|
||||
return findAll(specification, Sort.unsorted(), queryHints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> findAll(Specification<T> specification, Pageable page, String entityGraph) {
|
||||
TypedQuery<T> query = this.getQuery(specification, this.getDomainClass(), page.getSort(), entityGraph);
|
||||
return page.isUnpaged() ? new PageImpl<>(query.getResultList()) : page(query, page, specification, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> findAll(Specification<T> specification, Pageable page, Map<String, Object> queryHints) {
|
||||
return findAll(specification, page, 0L, this.getDomainClass(), queryHints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> findAll(Specification<T> specification, Pageable page, long total, String entityGraph) {
|
||||
TypedQuery<T> query = this.getQuery(specification, this.getDomainClass(), page.getSort(), entityGraph);
|
||||
return page.isUnpaged() ? new PageImpl<>(query.getResultList()) : page(query, page, specification, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> findAll(Specification<T> specification, Pageable page, long total, Map<String, Object> queryHints) {
|
||||
return findAll(specification, page, total, this.getDomainClass(), queryHints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> findAll(Specification<T> specification, Sort sort, String entityGraph) {
|
||||
return this.getQuery(specification, this.getDomainClass(), sort, entityGraph).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> findAll(Specification<T> specification, Sort sort, Map<String, Object> queryHints) {
|
||||
return this.getQuery(specification, this.getDomainClass(), sort, null, this.getDomainClass(), queryHints).getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(Specification<T> specification, boolean limit) {
|
||||
return limit ? this.getCountQueryLimit(specification,this.getDomainClass()).getResultStream().count() : this.getCountQuery(specification,this.getDomainClass()).getSingleResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> findAll(Specification<T> specification, Pageable page, long total, boolean limit) {
|
||||
return findAll(specification, page, total, limit, this.getDomainClass(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> Page<R> findAll(Specification<T> specification, Pageable page, long total, boolean limit, Class<R> returnClass, Map<String, Object> queryHints) {
|
||||
TypedQuery<R> query = this.getQuery(specification, this.getDomainClass(), page.getSort(), null, returnClass, queryHints);
|
||||
return page.isUnpaged() ? new PageImpl<>(query.getResultList()) : page(query, page, specification, total,limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<T> findAll(Specification<T> specification, Pageable page, long total, boolean limit, String entityGraph) {
|
||||
TypedQuery<T> query = this.getQuery(specification, this.getDomainClass(), page.getSort(), entityGraph);
|
||||
return page.isUnpaged() ? new PageImpl<>(query.getResultList()) : page(query, page, specification, total,limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> Page<R> findAll(Specification<T> specification, Pageable page, long total, boolean limit, Class<R> returnClass) {
|
||||
return findAll(specification, page, total, limit, returnClass, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull List<T> findAll(Specification<T> spec) {
|
||||
return findAll(spec, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<T> findById(@NonNull ID id, String entityGraph) {
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
Optional.ofNullable(entityGraph).filter(StringUtils::isNotEmpty).map(this.entityManager::getEntityGraph).ifPresent(eg -> properties.put(QueryHints.HINT_FETCHGRAPH, eg));
|
||||
return Optional.ofNullable(this.entityManager.find(this.getDomainClass(), id, properties));
|
||||
}
|
||||
|
||||
|
||||
protected <R> PageImpl<R> page(TypedQuery<R> query, Pageable page, Specification<T> specification, long count) {
|
||||
return page(query, page, specification, count, false);
|
||||
}
|
||||
|
||||
protected <R> PageImpl<R> page(TypedQuery<R> query, Pageable page, Specification<T> specification, long count, boolean limit) {
|
||||
long total = count;
|
||||
if (total == 0)
|
||||
total = this.count(specification,limit);
|
||||
if (page.isPaged()) {
|
||||
query.setFirstResult(((int) (page.getOffset())));
|
||||
query.setMaxResults(page.getPageSize());
|
||||
}
|
||||
return new PageImpl<>(query.getResultList(), page, total);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected <S extends T> TypedQuery<Long> getCountQuery(@Nullable Specification<S> spec, @NonNull Class<S> domainClass) {
|
||||
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> query = builder.createQuery(Long.class);
|
||||
Root<S> root = this.applySpecificationToCriteria(spec, domainClass, query);
|
||||
if(query.isDistinct()) {
|
||||
query.select(builder.countDistinct(root));
|
||||
} else {
|
||||
query.select(builder.count(root));
|
||||
}
|
||||
query.orderBy(Collections.emptyList());
|
||||
return this.entityManager.createQuery(query);
|
||||
}
|
||||
protected <S extends T> TypedQuery<ID> getCountQueryLimit(@Nullable Specification<S> spec,@NonNull Class<S> domainClass) {
|
||||
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ID> query = builder.createQuery(this.entityInformation.getIdType());
|
||||
Root<S> root = this.applySpecificationToCriteria(spec, domainClass, query);
|
||||
query.multiselect(root.get(this.entityInformation.getIdAttribute()));
|
||||
query.orderBy(Collections.emptyList());
|
||||
return this.entityManager.createQuery(query).setMaxResults(Constants.GLOBAL_QUERY_LIMIT);
|
||||
}
|
||||
|
||||
protected <S extends T> TypedQuery<S> getQuery(@Nullable Specification<S> spec, Class<S> domainClass, Sort sort, String entityGraph) {
|
||||
return this.getQuery(spec, domainClass, sort, entityGraph, domainClass, null);
|
||||
}
|
||||
|
||||
protected <S extends T, R> TypedQuery<R> getQuery(@Nullable Specification<S> spec, Class<S> domainClass, Sort sort, String entityGraph, Class<R> resultClass, @Nullable Map<String, Object> queryHints) {
|
||||
CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<R> query = cb.createQuery(resultClass);
|
||||
Root<S> root = this.applySpecificationToCriteria(spec, domainClass, query);
|
||||
if (sort.isSorted()) {
|
||||
query.orderBy(QueryUtils.toOrders(sort, root, cb));
|
||||
}
|
||||
TypedQuery<R> typedQuery = this.entityManager.createQuery(query);
|
||||
// 处理实体图
|
||||
applyEntityGraph(typedQuery, entityGraph);
|
||||
// 处理查询提示
|
||||
applyQueryHints(typedQuery, queryHints);
|
||||
// 一般列表查询不缓存数据,提高查询效率
|
||||
typedQuery.setHint(QueryHints.HINT_CACHE_MODE, CacheMode.IGNORE);
|
||||
return typedQuery;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Page<T> findAll(@NonNull Pageable pageable) {
|
||||
return this.findAll(null, pageable, (String) null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Page<T> findAll(Specification<T> spec, @NonNull Pageable pageable) {
|
||||
return this.findAll(spec, pageable, (String) null);
|
||||
}
|
||||
|
||||
private <S, U extends T> Root<U> applySpecificationToCriteria(@Nullable Specification<U> spec, Class<U> domainClass, CriteriaQuery<S> query) {
|
||||
Assert.notNull(domainClass, "Domain class must not be null!");
|
||||
Assert.notNull(query, "CriteriaQuery must not be null!");
|
||||
Root<U> root = query.from(domainClass);
|
||||
if (spec == null) {
|
||||
return root;
|
||||
} else {
|
||||
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
|
||||
Predicate predicate = spec.toPredicate(root, query, builder);
|
||||
if (predicate != null) {
|
||||
query.where(predicate);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理实体图
|
||||
*
|
||||
* @param query 查询
|
||||
* @param entityGraph 实体图名称
|
||||
* @param <S> 类型
|
||||
*/
|
||||
private <S> void applyEntityGraph(TypedQuery<S> query, String entityGraph) {
|
||||
if (!StringUtils.isEmpty(entityGraph)) {
|
||||
EntityGraph<?> eg = this.entityManager.getEntityGraph(entityGraph);
|
||||
query.setHint(QueryHints.HINT_FETCHGRAPH, eg); // 使用fetch模式抓取
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理查询提示
|
||||
*
|
||||
* @param query 类型安全查询
|
||||
* @param queryHints 查询提示
|
||||
* @param <S> 类型占位符
|
||||
*/
|
||||
private <S> void applyQueryHints(@NonNull TypedQuery<S> query, @Nullable Map<String, Object> queryHints) {
|
||||
Optional.ofNullable(queryHints)
|
||||
.filter(hints -> !hints.isEmpty())
|
||||
.map(Map::entrySet)
|
||||
.map(Set::stream)
|
||||
.ifPresent(es -> es.forEach(e -> {
|
||||
if (e.getKey().equals(QueryHints.HINT_FETCHGRAPH)) {
|
||||
// 这里没有处理他本身为实体图的情况,通常情况下能拿到实体图需要entityManager的帮助,在service里面直接注入
|
||||
// 或者使用entityManager不是很符合封装思路
|
||||
applyEntityGraph(query, (String) e.getValue());
|
||||
} else if (e.getKey().equals(Constants.QUERY_HINT_DB_HINT)) {
|
||||
// 这里使用hibernate的query,目前只考虑了这一种jpa实现,如果之后需要考虑多重jpa实现支持
|
||||
// 会在这里再做一个抽象
|
||||
Query<S> unwrap = query.unwrap(Query.class);
|
||||
if (String.class.isAssignableFrom(e.getValue().getClass())) {
|
||||
unwrap.addQueryHint(e.getValue().toString());
|
||||
} else if (Collection.class.isAssignableFrom(e.getValue().getClass())) {
|
||||
((Collection<String>) e.getValue()).forEach(unwrap::addQueryHint);
|
||||
}
|
||||
} else {
|
||||
query.setHint(e.getKey(), e.getValue());
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,242 @@
|
|||
package com.aisino.iles.common.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.aisino.iles.common.model.AwsConfig;
|
||||
import com.aisino.iles.common.util.DateUtil;
|
||||
import com.aisino.iles.common.util.StringUtils;
|
||||
import com.amazonaws.ClientConfiguration;
|
||||
import com.amazonaws.Protocol;
|
||||
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.client.builder.AwsClientBuilder;
|
||||
import com.amazonaws.regions.Regions;
|
||||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
|
||||
import com.amazonaws.services.s3.model.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.conn.socket.ConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.TrustAllStrategy;
|
||||
import org.apache.http.ssl.SSLContextBuilder;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class AwsS3Service implements InitializingBean {
|
||||
private final AwsConfig awsConfig;
|
||||
// 新key样式正则匹配,旧key样式正则匹配
|
||||
// 用来提取出路径里面的日(月中日, 1-31)
|
||||
private final Pattern keyPattern;
|
||||
private AmazonS3 client;
|
||||
|
||||
public AwsS3Service(AwsConfig awsConfig) {
|
||||
this.awsConfig = awsConfig;
|
||||
keyPattern = Pattern.compile("\\w*[/_]\\d{6}(\\d{2})[/_].*");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (awsConfig.getAccessKey() != null && awsConfig.getSecretKey() != null && awsConfig.getEndpoint() != null) {
|
||||
ClientConfiguration config = new ClientConfiguration();
|
||||
config.disableSocketProxy();
|
||||
if (awsConfig.getEndpoint().startsWith("https")) {
|
||||
config.setProtocol(Protocol.HTTPS);
|
||||
SSLContext context =
|
||||
new SSLContextBuilder().loadTrustMaterial(null,
|
||||
new TrustAllStrategy()).build();
|
||||
ConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
|
||||
context, NoopHostnameVerifier.INSTANCE
|
||||
);
|
||||
config.getApacheHttpClientConfig().withSslSocketFactory(sslSocketFactory);
|
||||
} else {
|
||||
config.setProtocol(Protocol.HTTP);
|
||||
}
|
||||
this.client = AmazonS3ClientBuilder
|
||||
.standard()
|
||||
.withClientConfiguration(config)
|
||||
.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(awsConfig.getAccessKey(), awsConfig.getSecretKey())))
|
||||
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(awsConfig.getEndpoint(), Regions.CN_NORTH_1.getName()))
|
||||
.enablePathStyleAccess()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行文件上传
|
||||
*
|
||||
* @param file 要上传的文件的路径
|
||||
* @param key 存储文件的路径
|
||||
* @return 文件路径
|
||||
*/
|
||||
public String upload(File file, String key) {
|
||||
try (InputStream stream = Files.newInputStream(file.toPath())) {
|
||||
return upload(stream, key);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String upload(InputStream stream, String key) throws IOException {
|
||||
return upload(stream, null, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param input 文件流
|
||||
* @param contentType metaType 例:image/jpeg
|
||||
* @param key 对象key
|
||||
* @return 上传成功后文件路径
|
||||
*/
|
||||
public String upload(InputStream input, String contentType, String key) {
|
||||
Date expireDate = DateUtil.asDate(LocalDateTime.now().plusMonths(1));
|
||||
ObjectMetadata metadata = new ObjectMetadata();
|
||||
metadata.setHttpExpiresDate(expireDate);
|
||||
Optional.ofNullable(contentType)
|
||||
.map(StrUtil::trimToNull)
|
||||
.ifPresent(metadata::setContentType);
|
||||
try {
|
||||
metadata.setContentLength(input.available());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("上传文件时设置内容长度出错:", e);
|
||||
}
|
||||
String bucket = getBucket(key);
|
||||
client.putObject(new PutObjectRequest(bucket, key, input, metadata)
|
||||
.withCannedAcl(CannedAccessControlList.Private));
|
||||
GeneratePresignedUrlRequest urlRequest = new GeneratePresignedUrlRequest(bucket, key);
|
||||
URL url = client.generatePresignedUrl(urlRequest);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象完整访问地址
|
||||
*
|
||||
* @param key 对象名称
|
||||
* @return http(s)访问URL
|
||||
*/
|
||||
public String getUrl(String key) {
|
||||
return getUrl(key, null);
|
||||
}
|
||||
|
||||
public String getUrl(String key, Date expired) {
|
||||
if(StringUtils.isEmpty(key)) // 为了兼容性,如果key为空,则返回null
|
||||
return null;
|
||||
AtomicReference<String> urlRef = new AtomicReference<>();
|
||||
checkExists(key, true, (bucket, k) -> {
|
||||
GeneratePresignedUrlRequest urlRequest = new GeneratePresignedUrlRequest(bucket, k);
|
||||
urlRequest.setExpiration(expired);
|
||||
URL url = client.generatePresignedUrl(urlRequest);
|
||||
urlRef.set(url.toString());
|
||||
});
|
||||
return urlRef.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象流
|
||||
*
|
||||
* @param key 对象名称
|
||||
* @return 对象内容数据流
|
||||
*/
|
||||
public InputStream getObjectStream(String key) {
|
||||
AtomicReference<InputStream> stream = new AtomicReference<>();
|
||||
checkExists(key, true, (bucket, k) -> {
|
||||
S3Object object = client.getObject(new GetObjectRequest(bucket, k));
|
||||
stream.set(object.getObjectContent());
|
||||
});
|
||||
return stream.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除对象
|
||||
*
|
||||
* @param key 对象名称
|
||||
*/
|
||||
public void delete(String key) {
|
||||
checkExists(key, true, (bucket, k) -> client.deleteObject(bucket, k));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据桶,只按照新规则
|
||||
*/
|
||||
private String getBucket(String key) {
|
||||
return getBucket(key, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据桶
|
||||
*
|
||||
* @param key 对象名称
|
||||
* @param fallback 是否启用回滚模式,从返回分桶之前的数据桶
|
||||
* @return 数据桶
|
||||
*/
|
||||
private String getBucket(String key, boolean fallback) {
|
||||
// 识别新key和旧key两种模式选择对应的数据桶
|
||||
// BF/20220507/f3f987e49e53494ca86b664e880b19be.rar -> debug
|
||||
StringBuilder bucketBuilder = new StringBuilder(awsConfig.getBucket());
|
||||
if (!fallback) {
|
||||
Matcher matcher = keyPattern.matcher(key);
|
||||
if (matcher.find()) {
|
||||
log.debug("newkey group 1: {}", matcher.group(1));
|
||||
bucketBuilder.append(matcher.group(1));
|
||||
}
|
||||
}
|
||||
log.debug("bucket: {}", bucketBuilder);
|
||||
return bucketBuilder.toString();
|
||||
}
|
||||
|
||||
public void rename(String srcKey, String destKey) {
|
||||
String srcBkt = getBucket(srcKey, true);
|
||||
String destBkt = getBucket(destKey, true);
|
||||
CopyObjectRequest copy = new CopyObjectRequest(srcBkt, srcKey, destBkt, destKey);
|
||||
client.copyObject(copy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key判断桶里的内容是否存在
|
||||
*/
|
||||
public boolean exists(String key) {
|
||||
AtomicBoolean exists = new AtomicBoolean(false);
|
||||
checkExists(key, true, (bkt, k1) -> exists.set(true));
|
||||
return exists.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key 文件名
|
||||
* @param changeKeyMode 是否要在默认查找方式未找到对象时,进行“_”替换为"/"后补充查找
|
||||
* @param found 当最终查找到时执行函数。参数1: 找到时所在的桶 参数2: 找到的key(可能是替换后的key)
|
||||
*/
|
||||
private void checkExists(String key, boolean changeKeyMode, BiConsumer<String, String> found) {
|
||||
String bucket = getBucket(key), splitBkt = bucket;
|
||||
// 分桶查询
|
||||
if (client.doesObjectExist(bucket, key)) {
|
||||
found.accept(bucket, key);
|
||||
} else {
|
||||
bucket = getBucket(key, true);
|
||||
// 旧桶查找
|
||||
if (client.doesObjectExist(bucket, key)) {
|
||||
found.accept(bucket, key);
|
||||
} else if (changeKeyMode) {
|
||||
// key中的_替换为/后在分桶中查询
|
||||
String newKey = key.replace("_", "/");
|
||||
if (client.doesObjectExist(splitBkt, newKey)) {
|
||||
found.accept(splitBkt, newKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.aisino.iles.common.service;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* 模块间数据传输和调用
|
||||
* 用法:在自己需要传输或者调用的模块继承这个接口,需要
|
||||
*
|
||||
* @author huxin
|
||||
* @since 2020-08-19
|
||||
*/
|
||||
public interface DataTransfer<T, R> extends InitializingBean {
|
||||
/**
|
||||
* 获取传输数据
|
||||
*
|
||||
* @param source 来源数据
|
||||
* @return 返回数据
|
||||
*/
|
||||
R getTransData(T source);
|
||||
|
||||
/**
|
||||
* 注册传输对象的名称或者键
|
||||
* @return 注册传输对象的名称或者键
|
||||
*/
|
||||
String registerDataTransKey();
|
||||
|
||||
@Override
|
||||
default void afterPropertiesSet() throws Exception {
|
||||
// 启动自动注册类型
|
||||
DataTransferContext.INSTANCE.registerDataTransfer(registerDataTransKey(), this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.aisino.iles.common.service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* 传输对象管理上下文
|
||||
*/
|
||||
public class DataTransferContext {
|
||||
private final Map<String,DataTransfer> DATA_TRANSFER_MAP = new HashMap<>();
|
||||
public static final DataTransferContext INSTANCE = new DataTransferContext();
|
||||
|
||||
|
||||
/**
|
||||
* 获取传输对象,通过键或者名称
|
||||
*
|
||||
* @param key 传输对象键、名称
|
||||
* @return 传输对象
|
||||
*/
|
||||
public <R, T> DataTransfer<R, T> getDataTransfer(String key) {
|
||||
if (!containDataTransferKey(key))
|
||||
throw new RuntimeException("没有找到注册名称为[" + key + "]的传输对象");
|
||||
return DATA_TRANSFER_MAP.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册传输对象服务
|
||||
*
|
||||
* @param key 传输对象键、名称
|
||||
* @param dataTransfer 传输对象
|
||||
*/
|
||||
public void registerDataTransfer(String key, DataTransfer dataTransfer) {
|
||||
DATA_TRANSFER_MAP.put(key, dataTransfer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断数据传输对象键是否存在
|
||||
*
|
||||
* @param key 传输对象键、名称
|
||||
* @return 是否存在
|
||||
*/
|
||||
public boolean containDataTransferKey(String key) {
|
||||
return DATA_TRANSFER_MAP.containsKey(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,376 @@
|
|||
package com.aisino.iles.common.service;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.aisino.iles.common.model.AwsConfig;
|
||||
import com.aisino.iles.common.model.UploadFile;
|
||||
import com.aisino.iles.common.model.enums.ContentType;
|
||||
import com.aisino.iles.common.model.enums.IndustryCategoryForFile;
|
||||
import com.aisino.iles.common.util.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Validated
|
||||
public class FtpService {
|
||||
private final Log log = LogFactory.getLog(this.getClass());
|
||||
private final AwsConfig awsConfig;
|
||||
private final AwsS3Service awsS3Service;
|
||||
|
||||
public FtpService(AwsConfig awsConfig, AwsS3Service awsS3Service) {
|
||||
this.awsConfig = awsConfig;
|
||||
this.awsS3Service = awsS3Service;
|
||||
}
|
||||
|
||||
public static String generateKey(IndustryCategoryForFile ind, LocalDate date, String uuid, String ext) {
|
||||
return StrUtil.format("{}/{}/{}{}", ind.getCode(),
|
||||
date.format(DateTimeFormatter.ofPattern("yyyyMMdd")),
|
||||
uuid.replaceAll("-", ""),
|
||||
ext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件web访问路径
|
||||
*
|
||||
* @param savePathName 对象Key
|
||||
* @return 对象http访问url
|
||||
*/
|
||||
public String getFileUrl(String savePathName) {
|
||||
return getFileUrl(savePathName, false);
|
||||
}
|
||||
|
||||
public String getFileUrl(String key, boolean direct) {
|
||||
return getFileUrl(key, null, direct);
|
||||
}
|
||||
|
||||
public String getFileUrl(String savePathName, Date expired) {
|
||||
return getFileUrl(savePathName, expired, false);
|
||||
}
|
||||
|
||||
public String getFileUrl(String key, Date expired, boolean direct) {
|
||||
String url = awsS3Service.getUrl(key, expired);
|
||||
if (StrUtil.isNotBlank(url) && !direct && StrUtil.isNotBlank(awsConfig.getAlterHttp())) {
|
||||
return url.replaceFirst(awsConfig.getEndpoint(), awsConfig.getAlterHttp());
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件下载路径
|
||||
*
|
||||
* @param savePathName 对象Key
|
||||
* @return 对象http访问url
|
||||
*/
|
||||
public String getFileDownloadUrl(String savePathName) {
|
||||
String url = awsS3Service.getUrl(savePathName);
|
||||
// 当存在替换标记时,替换为替换域名
|
||||
if (StrUtil.isNotBlank(url) && StrUtil.isNotBlank(awsConfig.getAlterHttp())) {
|
||||
return url.replaceFirst(awsConfig.getEndpoint(), awsConfig.getAlterHttp());
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件 同时另存文件系统供同步使用
|
||||
*
|
||||
* @param industryCategoryForFile 行业
|
||||
* @param fileName 文件名
|
||||
* @param fileBase64Str 文件内容base64
|
||||
* @return 文件key
|
||||
*/
|
||||
public String uploadFile(IndustryCategoryForFile industryCategoryForFile, String fileName, String fileBase64Str) {
|
||||
// 将base64字符串转为数组
|
||||
byte[] bytes = Base64.decode(fileBase64Str);
|
||||
// 转换为输入流
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
|
||||
try {
|
||||
return uploadTempFile(industryCategoryForFile, fileName, inputStream);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件到临时目录
|
||||
*
|
||||
* @param fileName 文件名称
|
||||
* @param inputStream 流对象
|
||||
* @return 对象Key
|
||||
*/
|
||||
public String uploadTempFile(IndustryCategoryForFile industryCategoryForFile,
|
||||
String fileName,
|
||||
InputStream inputStream) throws IOException {
|
||||
return internalUpload(industryCategoryForFile, fileName, inputStream, true);
|
||||
}
|
||||
|
||||
private String internalUpload(IndustryCategoryForFile industryCategoryForFile,
|
||||
String fileName,
|
||||
InputStream inputStream,
|
||||
boolean save
|
||||
)
|
||||
throws IOException {
|
||||
byte[] data = StreamUtils.copyToByteArray(inputStream);
|
||||
String fileExtendName = Optional.ofNullable(fileName)
|
||||
.map(StrUtil::trimToNull)
|
||||
.map(FileNameUtil::extName)
|
||||
.filter(s -> s.length() <= 4)
|
||||
.map(StrUtil::trimToNull)
|
||||
.map("."::concat)
|
||||
.orElse("");
|
||||
String key = generateKey(industryCategoryForFile, fileExtendName);
|
||||
String contentType = ContentType.getContentTypeNameByFileExtendName(fileExtendName);
|
||||
awsS3Service.upload(new ByteArrayInputStream(data), contentType, key);
|
||||
if (save) {
|
||||
saveFile(key, new ByteArrayInputStream(data));
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
private String generateKey(IndustryCategoryForFile industryCategoryForFile, String extendName) {
|
||||
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
|
||||
return generateKey(industryCategoryForFile, LocalDate.now(), uuid, extendName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用 {@code fileName} 作为key存储文件到OSS,本地不保存副本(用于不需要同步图片的场景)
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @param inputStream 流内容
|
||||
* @return 文件访问URL
|
||||
* @throws IOException 网络错误
|
||||
*/
|
||||
public String uploadFileWithFileName(String fileName, InputStream inputStream) throws IOException {
|
||||
return uploadFileWithFileName(fileName, inputStream, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用 {@code fileName} 作为key存储文件到OSS
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @param inputStream 流内容
|
||||
* @param save 指定是否需要本地保存文件副本。true表示要保存副本,false不保存
|
||||
* @return 文件访问URL
|
||||
* @throws IOException 网络错误
|
||||
*/
|
||||
public String uploadFileWithFileName(String fileName, InputStream inputStream, boolean save) throws IOException {
|
||||
byte[] data = StreamUtils.copyToByteArray(inputStream);
|
||||
String fileExtendName = Optional.ofNullable(fileName)
|
||||
.map(StrUtil::trimToNull)
|
||||
.map(FileNameUtil::extName)
|
||||
.filter(s -> s.length() <= 4)
|
||||
.map(StrUtil::trimToNull)
|
||||
.map("."::concat)
|
||||
.orElse("");
|
||||
String contentType = ContentType.getContentTypeNameByFileExtendName(fileExtendName);
|
||||
String url = awsS3Service.upload(new ByteArrayInputStream(data), contentType, fileName);
|
||||
if (save) {
|
||||
saveFile(fileName, data);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件到oss不保存临时文件
|
||||
* @param industryCategoryForFile 行业类别
|
||||
* @param fileName 文件名
|
||||
* @param fileBase64Str 文件内容base64
|
||||
* @return 文件key
|
||||
* @throws IOException 文件或者网络错误
|
||||
*/
|
||||
public String uploadFileWithBase64NoSave(IndustryCategoryForFile industryCategoryForFile, String fileName, String fileBase64Str) throws IOException {
|
||||
return internalUpload(industryCategoryForFile, fileName, new ByteArrayInputStream(Base64.decode(fileBase64Str)), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param industryCategoryForFile 行业类别
|
||||
* @param fileName 文件名
|
||||
* @param fileBase64Str 文件内容base64
|
||||
* @return 文件key
|
||||
* @throws IOException 文件或者网络错误
|
||||
*/
|
||||
public String updateFileWithBase64(IndustryCategoryForFile industryCategoryForFile, String fileName, String fileBase64Str) throws IOException {
|
||||
return internalUpload(industryCategoryForFile, fileName, new ByteArrayInputStream(Base64.decode(fileBase64Str)), false);
|
||||
}
|
||||
|
||||
|
||||
public boolean deletePathFile(String nameAsKey, boolean createDeleteFile) {
|
||||
awsS3Service.delete(nameAsKey);
|
||||
if (createDeleteFile) {
|
||||
createDeleteFile(nameAsKey);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean deletePathFile(String nameAsKey) {
|
||||
return deletePathFile(nameAsKey, true);
|
||||
}
|
||||
|
||||
private void createDeleteFile(String key) {
|
||||
String fileName = key.replaceAll("/", "_");
|
||||
try {
|
||||
if (StrUtil.isNotBlank(awsConfig.getFilePath())) {
|
||||
String filePathName = awsConfig.getFilePath() + File.separator + fileName + ".delete";
|
||||
Files.write(Paths.get(filePathName), fileName.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(fileName + "存储到删除文件异常:", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveFile(String fileName, String content) {
|
||||
saveFile(fileName, content.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private void saveFile(String fileName, byte[] content) {
|
||||
saveFile(fileName, new ByteArrayInputStream(content));
|
||||
}
|
||||
|
||||
private void saveFile(String key, InputStream input) {
|
||||
String fileName = key.replaceAll("/", "_");
|
||||
try (InputStream input1 = input) {
|
||||
if (StrUtil.isNotBlank(awsConfig.getFilePath())) {
|
||||
String suffix = ".tmp";
|
||||
String filePathName = awsConfig.getFilePath() + File.separator + fileName + suffix;
|
||||
File file = new File(filePathName);
|
||||
Files.copy(input1, file.toPath());
|
||||
FileUtil.rename(file, StrUtil.removeSuffix(filePathName, suffix), true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(fileName + "存储到文件异常:", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 多文件上传base64版本
|
||||
*
|
||||
* @param updateFiles 上传文件列表
|
||||
* @param ic 上传行业类别
|
||||
* @return 上传成功的文件列表
|
||||
*/
|
||||
@Validated
|
||||
public List<UploadFile> uploadFilesWithBase64(@Valid List<@Valid UploadFile> updateFiles, IndustryCategoryForFile ic) {
|
||||
return updateFiles.stream()
|
||||
.filter(uf -> StringUtils.isNotEmpty(uf.getContentStr()))
|
||||
.peek(uf -> {
|
||||
uf.setPath(uploadFile(ic, uf.getFileName(), uf.getContentStr()));
|
||||
uf.setAccessUrl(getFileUrl(uf.getPath()));
|
||||
uf.setContent(null);
|
||||
uf.setContentStr(null);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过Key获取对应文件的字节内容
|
||||
*
|
||||
* @param key 文件key
|
||||
* @return 文件字节内容或者null
|
||||
*/
|
||||
public byte[] getFileAsBytes(String key) {
|
||||
return Optional.ofNullable(this.getFileAsStream(key))
|
||||
.map(s -> IoUtil.readBytes(s, true))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过Key获取对应文件流格式的文件内容
|
||||
*
|
||||
* @param key 文件key
|
||||
* @return 文件流内容或者null
|
||||
*/
|
||||
public InputStream getFileAsStream(String key) {
|
||||
return Optional.ofNullable(key)
|
||||
.filter(awsS3Service::exists)
|
||||
.map(StrUtil::trimToNull)
|
||||
.map(awsS3Service::getObjectStream)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过Key获取对应文件的base64文件内容
|
||||
*
|
||||
* @param key 文件key(名称如:A/20220501/xxxx.txt)
|
||||
* @return base64编码的文件内容或者null
|
||||
*/
|
||||
public String getFileAsBase64(String key) {
|
||||
return Optional.ofNullable(key).map(this::getFileAsStream)
|
||||
.map(cn.hutool.core.codec.Base64::encode)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名文件,且不保存副本。 example: A/12345678/abc.jpg = > A/9876541/def.doc
|
||||
*
|
||||
* @param srcKey 原名称
|
||||
* @param destKey 新名称
|
||||
*/
|
||||
public void renameFile(String srcKey, String destKey) {
|
||||
renameFile(srcKey, destKey, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名文件 example: A/12345678/abc.jpg = > A/9876541/def.doc
|
||||
*
|
||||
* @param srcKey 原名称
|
||||
* @param destKey 新名称
|
||||
* @param saveCopy 是否保存副本到本地用于同步。true保存副本,false不保存
|
||||
*/
|
||||
public void renameFile(String srcKey, String destKey, boolean saveCopy) {
|
||||
String srcName = Optional.ofNullable(srcKey)
|
||||
.map(StrUtil::trimToNull)
|
||||
.orElseThrow(() -> new RuntimeException("原名称不能为空"));
|
||||
String destName = Optional.ofNullable(destKey)
|
||||
.map(StrUtil::trimToNull)
|
||||
.orElseThrow(() -> new RuntimeException("新名称不能为空"));
|
||||
if (srcName.equals(destName)) {
|
||||
throw new RuntimeException("名称一样不能重命名");
|
||||
}
|
||||
awsS3Service.rename(srcName, destName);
|
||||
if (saveCopy) {
|
||||
createDeleteFile(srcName);
|
||||
saveFile(destName, awsS3Service.getObjectStream(destName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅用于保安附件数据遗漏补传
|
||||
*
|
||||
* @param key 名称
|
||||
*/
|
||||
public void touchFile(String key) {
|
||||
String srcName = Optional.ofNullable(key)
|
||||
.map(StrUtil::trimToNull)
|
||||
.orElseThrow(() -> new RuntimeException("名称不能为空"));
|
||||
Optional.ofNullable(getFileAsBytes(srcName))
|
||||
.map(ByteArrayInputStream::new)
|
||||
.ifPresent(is -> {
|
||||
try (InputStream stream = is) {
|
||||
uploadFileWithFileName(key, stream, true);
|
||||
} catch (IOException e) {
|
||||
log.error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,194 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import org.apache.commons.beanutils.ConvertUtils;
|
||||
import org.apache.commons.beanutils.PropertyUtils;
|
||||
import org.apache.commons.beanutils.converters.DateConverter;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class BeanUtils extends org.springframework.beans.BeanUtils {
|
||||
|
||||
/**
|
||||
* 功能 : 只复制source对象的非空属性到target对象上
|
||||
* */
|
||||
public static void copyNoNullProperties(Object source, Object target) throws BeansException {
|
||||
copyNoNullProperties(source,target,null, (String[]) null);
|
||||
}
|
||||
|
||||
public static void copyNoNullProperties(Object source, Object target,String... ignoreProperties){
|
||||
copyNoNullProperties(source,target,null,ignoreProperties);
|
||||
}
|
||||
|
||||
private static void copyNoNullProperties(Object source, Object target, Class<?> editable, String... ignoreProperties) throws BeansException {
|
||||
Assert.notNull(source, "Source must not be null");
|
||||
Assert.notNull(target, "Target must not be null");
|
||||
Class<?> actualEditable = target.getClass();
|
||||
if (editable != null) {
|
||||
if (!editable.isInstance(target)) {
|
||||
throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
|
||||
}
|
||||
|
||||
actualEditable = editable;
|
||||
}
|
||||
|
||||
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
|
||||
List<String> ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;
|
||||
|
||||
for (PropertyDescriptor targetPd : targetPds) {
|
||||
Method writeMethod = targetPd.getWriteMethod();
|
||||
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
|
||||
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
|
||||
if (sourcePd != null) {
|
||||
Method readMethod = sourcePd.getReadMethod();
|
||||
if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
|
||||
try {
|
||||
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
|
||||
readMethod.setAccessible(true);
|
||||
}
|
||||
|
||||
Object value = readMethod.invoke(source);
|
||||
if (value != null) {
|
||||
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
|
||||
writeMethod.setAccessible(true);
|
||||
}
|
||||
|
||||
writeMethod.invoke(target, value);
|
||||
}
|
||||
} catch (Throwable var15) {
|
||||
throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* map转为bean
|
||||
* @param map
|
||||
* @param clazz bean类型
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> T mapToBean(Map<String, Object> map, Class<T> clazz) {
|
||||
T bean = null;
|
||||
try {
|
||||
bean = clazz.newInstance();
|
||||
ConvertUtils.register(new DateConverter(null), Date.class);
|
||||
org.apache.commons.beanutils.BeanUtils.populate(bean, map);
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InstantiationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* map中的key大写转为小写
|
||||
* @param orgMap
|
||||
* @return
|
||||
*/
|
||||
public static Map<String,Object> mapKeyToLower(Map<String,Object> orgMap){
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
if (orgMap == null || orgMap.isEmpty()) {
|
||||
return resultMap;
|
||||
}
|
||||
Set<String> keySet = orgMap.keySet();
|
||||
for (String key : keySet) {
|
||||
String newKey = key.toLowerCase();
|
||||
// newKey = newKey.replace("_", "");
|
||||
resultMap.put(newKey, orgMap.get(key));
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mapList 源
|
||||
* @param t 实体类型
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> transformList(List<Map<String,Object>> mapList,Class<T> t){
|
||||
List<T> list=new ArrayList<>();
|
||||
if (CollectionUtils.isNotEmpty(mapList)) {
|
||||
mapList.forEach(o->{
|
||||
if (MapUtils.isNotEmpty(o)) {
|
||||
Map<String,Object> map=mapKeyToLower(o);
|
||||
T rst = mapToBean(map,t);
|
||||
if (rst!=null){
|
||||
list.add(rst);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* map转为List
|
||||
* @param map
|
||||
* @param t
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> transformMapToList(Map<String, Object> map, Class<T> t) {
|
||||
List<T> list = new ArrayList<>();
|
||||
if (MapUtils.isNotEmpty(map)) {
|
||||
map = mapKeyToLower(map);
|
||||
T rst = mapToBean(map, t);
|
||||
if (rst != null) {
|
||||
list.add(rst);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝对象属性 (只拷贝指定的属性)
|
||||
*
|
||||
* @param source 源
|
||||
* @param target 目标
|
||||
* @param targetProps 要拷贝的属性
|
||||
*/
|
||||
public static void copyPropertiesWithProp(Object source, Object target, String... targetProps) {
|
||||
Assert.notNull(source, "Source must not be null");
|
||||
Assert.notNull(target, "Target must not be null");
|
||||
|
||||
PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(target);
|
||||
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
|
||||
String propertyName = propertyDescriptor.getName();
|
||||
Method readMethod = propertyDescriptor.getReadMethod();
|
||||
Method writeMethod = propertyDescriptor.getWriteMethod();
|
||||
if (!ArrayUtils.contains(targetProps, propertyName) || readMethod == null || writeMethod == null) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
Object sourceValue = readMethod.invoke(source);
|
||||
writeMethod.invoke(target, sourceValue);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Slf4j
|
||||
public class BufferedBlockingQueue<T> {
|
||||
private final Consumer<List<T>> consumer;
|
||||
private final LinkedBlockingQueue<T> queue;
|
||||
|
||||
public BufferedBlockingQueue(int capacity, Consumer<List<T>> consumer) {
|
||||
queue = new LinkedBlockingQueue<>(capacity);
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
public void put(@NotNull T t) {
|
||||
boolean success;
|
||||
do {
|
||||
success=queue.offer(t);
|
||||
if (queue.remainingCapacity() <= 0) {
|
||||
log.trace("队列已满,正在排空...");
|
||||
this.flush();
|
||||
}
|
||||
}while (!success);
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
List<T> all = new ArrayList<>();
|
||||
queue.drainTo(all);
|
||||
if (!all.isEmpty()) {
|
||||
consumer.accept(all);
|
||||
log.trace("队列已排空。");
|
||||
} else {
|
||||
log.trace("没有元素用于排空。");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Component
|
||||
public class CaptchaUtils {
|
||||
@Autowired
|
||||
private CacheManager cacheManager;
|
||||
|
||||
public Boolean checkCaptchaCode(String captchaKey,String captchaCode) {
|
||||
assert !StringUtils.isEmpty(captchaKey);
|
||||
assert !StringUtils.isEmpty(captchaCode);
|
||||
|
||||
final String code = cacheManager.getCache("captcha").get(captchaKey, String.class);
|
||||
if(captchaCode.equals(code)){
|
||||
cacheManager.getCache("captcha").evict(captchaKey);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public void putCaptchaCode(String key, String value) {
|
||||
cacheManager.getCache("captcha").put(key, value);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,294 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.*;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Created by Administrator on 2017/7/10.
|
||||
*/
|
||||
public class DateUtil {
|
||||
/**
|
||||
* 获得当天零时零分零秒
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Date getCurDate() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(new Date());
|
||||
calendar.set(Calendar.HOUR, 0);
|
||||
calendar.set(Calendar.MINUTE, 0);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
public static Date str2Date(String dateStr, String format) {
|
||||
if (dateStr == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(format);
|
||||
return sdf.parse(dateStr);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getCurDateTime() {
|
||||
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
return f.format(new Date());
|
||||
}
|
||||
|
||||
public static String getDateStr(Date date) {
|
||||
if (date == null) {
|
||||
return "";
|
||||
}
|
||||
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
|
||||
return f.format(date);
|
||||
}
|
||||
|
||||
public static String date2Str(Date date, String type) {
|
||||
if (date == null || type == null) {
|
||||
return "";
|
||||
}
|
||||
SimpleDateFormat f = new SimpleDateFormat(type);
|
||||
return f.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最后nday天的时间范围
|
||||
*
|
||||
* @param nday 如1天
|
||||
* @return 2017-10-01, 2017-10-02 (今天)
|
||||
*/
|
||||
public static LocalDate[] getNDayToNow1(int nday, LocalDate[] dates) {
|
||||
LocalDate to = LocalDate.now();
|
||||
dates[1] = to;
|
||||
dates[0] = to.minusDays(Long.valueOf(nday));
|
||||
return dates;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最后nday天的时间范围
|
||||
*
|
||||
* @param nday 如1天
|
||||
* @return 2017-10-01 00:00:00, 2017-10-02 23:59:59(今天)
|
||||
*/
|
||||
public static LocalDateTime[] getNDayToNow2(int nday, LocalDateTime[] dates) {
|
||||
LocalDateTime to = LocalDateTime.now();
|
||||
dates[1] = to;
|
||||
dates[0] = to.minusDays(Long.valueOf(nday));
|
||||
return dates;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最后nday天的时间范围
|
||||
*
|
||||
* @param nday 如1天
|
||||
* @return 2017-10-01 00:00:00, 2017-10-02 23:59:59(今天)
|
||||
*/
|
||||
public static Date[] getNDayToNow(int nday) {
|
||||
Date[] dates = new Date[2];
|
||||
Date now = new Date();
|
||||
String todayDate = getDateStr(now);
|
||||
String toStr = todayDate + " 23:59:59";
|
||||
dates[1] = str2Date(toStr, "yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(now);
|
||||
cal.add(Calendar.DATE, -1 * nday);
|
||||
Date fromDate = cal.getTime();
|
||||
String fromdayDate = getDateStr(fromDate);
|
||||
String fromStr = fromdayDate + " 00:00:00";
|
||||
dates[0] = str2Date(fromStr, "yyyy-MM-dd HH:mm:ss");
|
||||
return dates;
|
||||
}
|
||||
|
||||
public static Date[] getYesterdayRange() {
|
||||
Date[] dates = new Date[2];
|
||||
Date now = new Date();
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(now);
|
||||
cal.add(Calendar.DATE, -1);
|
||||
Date fromDate = cal.getTime();
|
||||
String fromdayDate = getDateStr(fromDate);
|
||||
String fromStr = fromdayDate + " 00:00:00";
|
||||
dates[0] = str2Date(fromStr, "yyyy-MM-dd HH:mm:ss");
|
||||
String toStr = fromdayDate + " 23:59:59";
|
||||
dates[1] = str2Date(toStr, "yyyy-MM-dd HH:mm:ss");
|
||||
return dates;
|
||||
}
|
||||
|
||||
public static String[] getYesterdayRangeStr() {
|
||||
String[] dates = new String[2];
|
||||
Date now = new Date();
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(now);
|
||||
cal.add(Calendar.DATE, -1);
|
||||
Date fromDate = cal.getTime();
|
||||
String fromdayDate = getDateStr(fromDate);
|
||||
String fromStr = fromdayDate + " 00:00:00";
|
||||
dates[0] = fromStr;
|
||||
String toStr = fromdayDate + " 23:59:59";
|
||||
dates[1] = toStr;
|
||||
return dates;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义格式获取当前时间
|
||||
*
|
||||
* @param format
|
||||
* @return
|
||||
*/
|
||||
public static String getCurrentDate(String format) {
|
||||
return new SimpleDateFormat(format).format(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 自定义格式获取三年前的那一年开始时间
|
||||
* @Author Jnno
|
||||
* @Version 1.0
|
||||
**/
|
||||
public static String getThreadDate(String format) {
|
||||
return convertDateToString(getCurrYearFirst(getThredYear()), format);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 获取三年前的年份
|
||||
* @Author Jnno
|
||||
* @Version 1.0
|
||||
**/
|
||||
public static int getThredYear() {
|
||||
Calendar date = Calendar.getInstance();
|
||||
|
||||
int year = date.get(Calendar.YEAR);
|
||||
|
||||
return year - 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某年第一天日期
|
||||
*
|
||||
* @param year 年份
|
||||
* @return Date
|
||||
*/
|
||||
public static Date getCurrYearFirst(int year) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.clear();
|
||||
calendar.set(Calendar.YEAR, year);
|
||||
Date currYearFirst = calendar.getTime();
|
||||
return currYearFirst;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某年最后一天日期
|
||||
*
|
||||
* @param year 年份
|
||||
* @return Date
|
||||
*/
|
||||
public static Date getCurrYearLast(int year) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.clear();
|
||||
calendar.set(Calendar.YEAR, year);
|
||||
calendar.roll(Calendar.DAY_OF_YEAR, -1);
|
||||
Date currYearLast = calendar.getTime();
|
||||
|
||||
return currYearLast;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description 根据时间转换成制定格式的字符串
|
||||
* @Author Jnno
|
||||
* @Version 1.0
|
||||
**/
|
||||
public static String convertDateToString(Date date, String format) {
|
||||
try {
|
||||
return new SimpleDateFormat(format).format(date);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//获得当前日期字符串
|
||||
//String style : 返回形式 例如"yyyy-MM-dd"、"yyyy-MM-dd HH:mm:ss"、"yyyy-MM-dd hh:mm:ss"
|
||||
public static String getCurrentDateString(String style) {
|
||||
return new SimpleDateFormat(style).format(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照给定的日期,转换成格式 "yyyy-MM-dd" 的字符串.
|
||||
*
|
||||
* @param date java.util.Date
|
||||
* @return short format datetime
|
||||
*/
|
||||
public static String shortFmt(Date date) {
|
||||
String strDate = "";
|
||||
if (date != null) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
strDate = sdf.format(date);
|
||||
}
|
||||
return strDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前时间转换成格式 "yyyy-MM-dd HH:mm:ss" 的字符串.
|
||||
*
|
||||
* @param date java.util.Date
|
||||
* @return short format datetime
|
||||
*/
|
||||
|
||||
public static String shortFmt(java.sql.Timestamp date) {
|
||||
String strDate = "";
|
||||
if (date != null) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
strDate = sdf.format(date);
|
||||
}
|
||||
return strDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* localDateTime的日期转Date类型
|
||||
*
|
||||
* @param localDateTime
|
||||
* @return
|
||||
*/
|
||||
public static Date asDate(LocalDateTime localDateTime) {
|
||||
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* Date的日期转localDateTime类型
|
||||
*
|
||||
* @param date
|
||||
* @return
|
||||
*/
|
||||
public static LocalDateTime asLocalDateTime(Date date) {
|
||||
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算两个日期之间的天数
|
||||
*
|
||||
* @param start 开始日期
|
||||
* @param end 结束日期
|
||||
* @return 天数
|
||||
*/
|
||||
public static long days(LocalDate start, LocalDate end) {
|
||||
return end.toEpochDay() - start.toEpochDay();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两个日期年份差
|
||||
*
|
||||
* @param start 开始日期
|
||||
* @param end 结束日期
|
||||
* @return 布尔值,超过或等于years则返回true
|
||||
*/
|
||||
public static boolean compareDate(LocalDate start, LocalDate end, Integer years) {
|
||||
return end.toEpochDay()-start.plusYears(years).toEpochDay() < 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
public class DepartmentUtil {
|
||||
/**
|
||||
* 采集点用户根据不同省市处理部门编码
|
||||
*
|
||||
* @param deptCode
|
||||
* @return
|
||||
*/
|
||||
public static String doWithDepartmentCode(String deptCode) {
|
||||
deptCode = StringUtils.trimEven0(deptCode);
|
||||
if (deptCode != null) {
|
||||
if (deptCode.indexOf("CJD") != -1) {
|
||||
if (deptCode.substring(3).length() <= 4)
|
||||
deptCode = deptCode.substring(3);
|
||||
else {
|
||||
//deptCode = StringUtil.add0(deptCode.substring(3), 4).substring(0,4);
|
||||
if (!deptCode.substring(3).substring(0, 2).equals("11")//不属于四个直辖市
|
||||
&& !deptCode.substring(3).substring(0, 2).equals("12")
|
||||
&& !deptCode.substring(3).substring(0, 2).equals("31")
|
||||
&& !deptCode.substring(3).substring(0, 2).equals("50")) {
|
||||
deptCode = deptCode.substring(3).substring(0, 4);
|
||||
} else//属于四个直辖市时,取前6位 20101018mds
|
||||
{
|
||||
if (deptCode.substring(3).length() < 6) {
|
||||
deptCode = deptCode.substring(3).substring(0, 2);
|
||||
} else {
|
||||
deptCode = deptCode.substring(3).substring(0, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return deptCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验机构是否是 直辖市
|
||||
*/
|
||||
public static boolean departIsZxs(String deptCode) {
|
||||
boolean iszxs = false;
|
||||
if (deptCode.startsWith("11") || deptCode.startsWith("12")
|
||||
|| deptCode.startsWith("31") || deptCode.startsWith("50")) {
|
||||
iszxs = true;
|
||||
}
|
||||
return iszxs;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,345 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import com.aisino.iles.common.iface.ExportExcelHelper;
|
||||
import com.aisino.iles.common.model.ExportExcelParam;
|
||||
import jxl.CellType;
|
||||
import jxl.DateCell;
|
||||
import jxl.Sheet;
|
||||
import jxl.Workbook;
|
||||
import jxl.write.*;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
@Slf4j
|
||||
public class ExportExcelUtil {
|
||||
|
||||
public static ByteArrayOutputStream createExcel(List<String> labels, List<String> props, String fileName, List dataList) throws Exception {
|
||||
if (labels.get(0) == null && props.get(0) == null) {
|
||||
labels = labels.subList(1, labels.size());
|
||||
props = props.subList(1, props.size());
|
||||
}
|
||||
//创建输出流
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
WritableWorkbook book = Workbook.createWorkbook(os);
|
||||
//表头
|
||||
WritableFont wf = new WritableFont(WritableFont.createFont("宋体"), 15, WritableFont.BOLD);
|
||||
WritableCellFormat wcf = new WritableCellFormat(wf);
|
||||
wcf.setAlignment(jxl.format.Alignment.CENTRE);
|
||||
wcf.setBorder(jxl.format.Border.ALL, jxl.format.BorderLineStyle.THIN);
|
||||
//标题
|
||||
WritableFont wf1 = new WritableFont(WritableFont.createFont("宋体"), 10, WritableFont.BOLD);
|
||||
WritableCellFormat wcf1 = new WritableCellFormat(wf1);
|
||||
wcf1.setAlignment(jxl.format.Alignment.CENTRE);
|
||||
wcf1.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
|
||||
//内容
|
||||
WritableFont wf2 = new WritableFont(WritableFont.createFont("宋体"), 10);
|
||||
WritableCellFormat wcf2 = new WritableCellFormat(wf2);
|
||||
wcf2.setAlignment(jxl.format.Alignment.CENTRE);
|
||||
wcf2.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
|
||||
//获得Excel表的第一页
|
||||
WritableSheet sheet = book.createSheet("第一页", 0);
|
||||
sheet.mergeCells(0, 0, labels.size() - 1, 0);
|
||||
//表标题(第一行)(列号,行号,值,样式)
|
||||
Label label = new Label(0, 0, fileName, wcf);
|
||||
sheet.addCell(label);
|
||||
|
||||
int[] maxlengthArr = new int[labels.size()];
|
||||
//表头(第二行)(列号,行号,值,样式)
|
||||
for (int n = 0; n < labels.size(); n++) {
|
||||
sheet.addCell(new Label(n, 1, labels.get(n), wcf1));
|
||||
if (labels.get(n) != null) {
|
||||
maxlengthArr[n] = length(labels.get(n));
|
||||
} else {
|
||||
maxlengthArr[n] = 0;
|
||||
}
|
||||
}
|
||||
//内容(列号,行号,值,样式)
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
Object obj = dataList.get(i);
|
||||
for (int n = 0; n < labels.size(); n++) {
|
||||
String value = InvokeUtility.getFieldValueByName(props.get(n), obj) == null ? "" : InvokeUtility.getFieldValueByName(props.get(n), obj).toString();
|
||||
sheet.addCell(new Label(n, i + 2, value, wcf2));
|
||||
if (value != null) {
|
||||
maxlengthArr[n] = maxlengthArr[n] < length(value) ? length(value) : maxlengthArr[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
//设置列宽度
|
||||
for (int i = 0; i < maxlengthArr.length; i++) {
|
||||
sheet.setColumnView(i, maxlengthArr[i] + 2); //设置col显示样式
|
||||
}
|
||||
|
||||
book.write();
|
||||
book.close();
|
||||
return os; //book写入到输出流中关闭book将此输出流输入到输入流中返回给jsp页面一个输入流供客户端下载。
|
||||
}
|
||||
|
||||
public static <T> ByteArrayOutputStream createExcel(List<String> labels, List<String> props, String fileName, ExportExcelHelper<T> exportExcelHelper) throws WriteException, IOException {
|
||||
if (labels.get(0) == null && props.get(0) == null) {
|
||||
labels = labels.subList(1, labels.size());
|
||||
props = props.subList(1, props.size());
|
||||
}
|
||||
//创建输出流
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
WritableWorkbook book = Workbook.createWorkbook(os);
|
||||
exportExcelHelper.writeToBook(book, labels, props, fileName);
|
||||
return os; //book写入到输出流中关闭book将此输出流输入到输入流中返回给jsp页面一个输入流供客户端下载。
|
||||
}
|
||||
|
||||
public static <T, R> void exportExcel(HttpServletResponse response, ExportExcelParam<T> excelParam, ExportExcelHelper<R> exportExcelHelper) throws IOException, WriteException {
|
||||
ByteArrayOutputStream excelOutStream = createExcel(excelParam.getLabels(), excelParam.getProps(), excelParam.getFileName(), exportExcelHelper);
|
||||
if (excelOutStream != null) {
|
||||
response.setContentType("application/ms-excel;charset=UTF-8");
|
||||
response.setHeader("Content-Disposition", "attachment;filename="
|
||||
.concat(String.valueOf(URLEncoder.encode(excelParam.getFileName() + ".xls", "UTF-8"))));
|
||||
int contentLength = excelOutStream.size();
|
||||
response.setHeader("content-length", contentLength + "");
|
||||
excelOutStream.writeTo(response.getOutputStream());
|
||||
excelOutStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void exportExcel(HttpServletResponse response, ExportExcelParam excelParam, List domesticPassengerList) {
|
||||
try {
|
||||
// 尝试加密数据
|
||||
// domesticPassengerList = (List) domesticPassengerList.stream().peek(SM4Util::objectSm4Encrypt).collect(Collectors.toList());
|
||||
ByteArrayOutputStream tmp = createExcel(excelParam.getLabels(), excelParam.getProps(), excelParam.getFileName(), domesticPassengerList);
|
||||
OutputStream out = response.getOutputStream();
|
||||
response.setContentType("application/ms-excel;charset=UTF-8");
|
||||
response.setHeader("Content-Disposition", "attachment;filename="
|
||||
.concat(String.valueOf(URLEncoder.encode(excelParam.getFileName() + ".xls", "UTF-8"))));
|
||||
Integer contentLength = tmp.size();
|
||||
response.setHeader("content-length", contentLength + "");
|
||||
out.write(tmp.toByteArray());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static int length(String s) {
|
||||
if (s == null)
|
||||
return 0;
|
||||
char[] c = s.toCharArray();
|
||||
int len = 0;
|
||||
for (int i = 0; i < c.length; i++) {
|
||||
len++;
|
||||
if (!isLetter(c[i])) {
|
||||
len++;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
public static boolean isLetter(char c) {
|
||||
int k = 0x80;
|
||||
return c / k == 0 ? true : false;
|
||||
}
|
||||
|
||||
public static List<Object[]> parseExcelToList(InputStream stream, String fileName) throws Exception {
|
||||
if (fileName.endsWith(".xlsx"))
|
||||
return parseExcelToListByXlsx(stream);
|
||||
else
|
||||
return parseExcelToListByXls(stream);
|
||||
}
|
||||
|
||||
private static List<Object[]> parseExcelToListByXls(InputStream stream) {
|
||||
List<Object[]> list = new ArrayList<>();
|
||||
try {
|
||||
//获取Excel文件对象
|
||||
Workbook rwb = Workbook.getWorkbook(stream);
|
||||
//获取文件的指定工作表 默认的第一个
|
||||
Sheet sheet = rwb.getSheet(0);
|
||||
// 获取表头行,用于确定列数
|
||||
int columnCount = sheet.getColumns();
|
||||
jxl.Cell cell = null;
|
||||
//行数(表头的目录不需要,从1开始)
|
||||
for (int i = 0; i < sheet.getRows(); i++) {
|
||||
//创建一个数组 用来存储每一列的值,根据表头列数创建数组
|
||||
Object[] str = new Object[columnCount];
|
||||
//列数
|
||||
for (int j = 0; j < columnCount; j++) {
|
||||
//获取第i行,第j列的值
|
||||
cell = sheet.getCell(j, i);
|
||||
if (cell.getType() == CellType.DATE) {
|
||||
DateCell dc = (DateCell) cell;
|
||||
str[j] = dc.getDate();
|
||||
} else {
|
||||
str[j] = StringUtils.isNotEmpty(cell.getContents()) ? cell.getContents().trim() : "";
|
||||
}
|
||||
}
|
||||
//把刚获取的列存入list
|
||||
list.add(str);
|
||||
}
|
||||
return list;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("解析excel文件异常,message=" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出excel,目标数据的类型为Map
|
||||
*
|
||||
* @param response
|
||||
* @param excelParam
|
||||
* @param domesticPassengerList
|
||||
*/
|
||||
public static void exportExcelTheDataIsMap(HttpServletResponse response, ExportExcelParam excelParam, List domesticPassengerList) {
|
||||
try {
|
||||
// 尝试加密数据
|
||||
domesticPassengerList = (List) domesticPassengerList.stream().collect(Collectors.toList());
|
||||
ByteArrayOutputStream tmp = createExcelTheDataIsMap(excelParam.getLabels(), excelParam.getProps(), excelParam.getFileName(), domesticPassengerList);
|
||||
OutputStream out = response.getOutputStream();
|
||||
response.setContentType("application/ms-excel;charset=UTF-8");
|
||||
response.setHeader("Content-Disposition", "attachment;filename="
|
||||
.concat(String.valueOf(URLEncoder.encode(excelParam.getFileName() + ".xls", "UTF-8"))));
|
||||
Integer contentLength = tmp.size();
|
||||
response.setHeader("content-length", contentLength + "");
|
||||
out.write(tmp.toByteArray());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param labels
|
||||
* @param props
|
||||
* @param fileName
|
||||
* @param dataList map类型的数据
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static ByteArrayOutputStream createExcelTheDataIsMap(List<String> labels, List<String> props, String fileName, List dataList) throws Exception {
|
||||
if (labels.get(0) == null && props.get(0) == null) {
|
||||
labels = labels.subList(1, labels.size());
|
||||
props = props.subList(1, props.size());
|
||||
}
|
||||
//创建输出流
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
WritableWorkbook book = Workbook.createWorkbook(os);
|
||||
//表头
|
||||
WritableFont wf = new WritableFont(WritableFont.createFont("宋体"), 15, WritableFont.BOLD);
|
||||
WritableCellFormat wcf = new WritableCellFormat(wf);
|
||||
wcf.setAlignment(jxl.format.Alignment.CENTRE);
|
||||
wcf.setBorder(jxl.format.Border.ALL, jxl.format.BorderLineStyle.THIN);
|
||||
//标题
|
||||
WritableFont wf1 = new WritableFont(WritableFont.createFont("宋体"), 10, WritableFont.BOLD);
|
||||
WritableCellFormat wcf1 = new WritableCellFormat(wf1);
|
||||
wcf1.setAlignment(jxl.format.Alignment.CENTRE);
|
||||
wcf1.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
|
||||
//内容
|
||||
WritableFont wf2 = new WritableFont(WritableFont.createFont("宋体"), 10);
|
||||
WritableCellFormat wcf2 = new WritableCellFormat(wf2);
|
||||
wcf2.setAlignment(jxl.format.Alignment.CENTRE);
|
||||
wcf2.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);
|
||||
//获得Excel表的第一页
|
||||
WritableSheet sheet = book.createSheet("第一页", 0);
|
||||
sheet.mergeCells(0, 0, labels.size() - 1, 0);
|
||||
//表标题(第一行)(列号,行号,值,样式)
|
||||
Label label = new Label(0, 0, fileName, wcf);
|
||||
sheet.addCell(label);
|
||||
|
||||
int[] maxlengthArr = new int[labels.size()];
|
||||
//表头(第二行)(列号,行号,值,样式)
|
||||
for (int n = 0; n < labels.size(); n++) {
|
||||
sheet.addCell(new Label(n, 1, labels.get(n), wcf1));
|
||||
if (labels.get(n) != null) {
|
||||
maxlengthArr[n] = length(labels.get(n));
|
||||
} else {
|
||||
maxlengthArr[n] = 0;
|
||||
}
|
||||
}
|
||||
//内容(列号,行号,值,样式)
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
Map<String, Object> map = (Map<String, Object>) dataList.get(i);
|
||||
|
||||
for (int n = 0; n < labels.size(); n++) {
|
||||
String value = map.get(props.get(n)) == null ? "" : map.get(props.get(n)).toString();
|
||||
// String value = InvokeUtility.getFieldValueByName(props.get(n), obj) == null ? "" : InvokeUtility.getFieldValueByName(props.get(n), obj).toString();
|
||||
sheet.addCell(new Label(n, i + 2, value, wcf2));
|
||||
if (value != null) {
|
||||
maxlengthArr[n] = maxlengthArr[n] < length(value) ? length(value) : maxlengthArr[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
//设置列宽度
|
||||
for (int i = 0; i < maxlengthArr.length; i++) {
|
||||
sheet.setColumnView(i, maxlengthArr[i] + 2); //设置col显示样式
|
||||
}
|
||||
|
||||
book.write();
|
||||
book.close();
|
||||
return os; //book写入到输出流中关闭book将此输出流输入到输入流中返回给jsp页面一个输入流供客户端下载。
|
||||
}
|
||||
|
||||
public static List<Object[]> parseExcelToListByXlsx(InputStream stream) throws Exception {
|
||||
List<Object[]> list = new ArrayList<>();
|
||||
org.apache.poi.util.IOUtils.setByteArrayMaxOverride(200000000);
|
||||
try {
|
||||
org.apache.poi.ss.usermodel.Workbook workbook;
|
||||
try {
|
||||
workbook = new XSSFWorkbook(stream);
|
||||
} catch (Exception e) {
|
||||
log.error("解析excel文件异常,message=" + e.getMessage());
|
||||
workbook = new HSSFWorkbook(stream);
|
||||
}
|
||||
org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);
|
||||
// 获取表头行,用于确定列数
|
||||
org.apache.poi.ss.usermodel.Row headerRow = sheet.getRow(0);
|
||||
int columnCount = headerRow != null ? headerRow.getLastCellNum() : 0;
|
||||
for (org.apache.poi.ss.usermodel.Row row : sheet) {
|
||||
// 根据表头列数创建数组,确保所有行都有相同的列数
|
||||
Object[] rowData = new Object[columnCount];
|
||||
for (int j = 0; j < columnCount; j++) {
|
||||
org.apache.poi.ss.usermodel.Cell cell = row.getCell(j);
|
||||
if (cell != null) {
|
||||
switch (cell.getCellType()) {
|
||||
case STRING:
|
||||
rowData[j] = cell.getStringCellValue().trim();
|
||||
break;
|
||||
case NUMERIC:
|
||||
if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) {
|
||||
rowData[j] = cell.getDateCellValue();
|
||||
} else {
|
||||
rowData[j] = cell.getNumericCellValue();
|
||||
}
|
||||
break;
|
||||
case BOOLEAN:
|
||||
rowData[j] = cell.getBooleanCellValue();
|
||||
break;
|
||||
case FORMULA:
|
||||
rowData[j] = cell.getCellFormula();
|
||||
break;
|
||||
default:
|
||||
rowData[j] = null;
|
||||
}
|
||||
} else {
|
||||
// 确保空单元格被正确设置为null,避免列位置错位
|
||||
rowData[j] = "";
|
||||
}
|
||||
}
|
||||
list.add(rowData);
|
||||
}
|
||||
workbook.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("解析excel文件异常: " + e.getMessage(), e);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@Slf4j
|
||||
public class HttpHeaderUtil {
|
||||
public static String getIpAddr(HttpServletRequest request) {
|
||||
String ip = request.getHeader("x-forwarded-for");
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("x-forwarded-for ip: {}", ip);
|
||||
}
|
||||
if (StringUtils.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
|
||||
// 多次反向代理后会有多个ip值,第一个ip才是真实ip
|
||||
if (ip.contains(",")) {
|
||||
ip = ip.split(",")[0];
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Proxy-Client-IP ip: {}", ip);
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("WL-Proxy-Client-IP ip: {}", ip);
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("HTTP_CLIENT_IP ip: {}", ip);
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("HTTP_X_FORWARDED_FOR ip: {}", ip);
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("X-Real-IP ip: {}", ip);
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("getRemoteAddr ip: {}", ip);
|
||||
}
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("获取客户端ip: {}", ip);
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* ip等网络地址工具
|
||||
*
|
||||
* @author huxin
|
||||
* @since 2020-07-28
|
||||
*/
|
||||
public class InetAddressUtil {
|
||||
private static final Pattern IPV4_PATTERN =
|
||||
Pattern.compile(
|
||||
"^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
|
||||
|
||||
private static final Pattern IPV6_STD_PATTERN =
|
||||
Pattern.compile(
|
||||
"^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$");
|
||||
|
||||
private static final Pattern IPV6_HEX_COMPRESSED_PATTERN =
|
||||
Pattern.compile(
|
||||
"^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)$");
|
||||
/**
|
||||
* 判断是否是ipv4地址
|
||||
*/
|
||||
public static boolean isIPv4Address(final String input) {
|
||||
return IPV4_PATTERN.matcher(input).matches();
|
||||
}
|
||||
/**
|
||||
* 判断是否是标准ipv6地址
|
||||
*/
|
||||
public static boolean isIPv6StdAddress(final String input) {
|
||||
return IPV6_STD_PATTERN.matcher(input).matches();
|
||||
}
|
||||
/**
|
||||
* 判断是否是ipv6压缩地址
|
||||
*/
|
||||
public static boolean isIPv6HexCompressedAddress(final String input) {
|
||||
return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
|
||||
}
|
||||
/**
|
||||
* 判断是否ipv6地址
|
||||
*/
|
||||
public static boolean isIPv6Address(final String input) {
|
||||
return isIPv6StdAddress(input) || isIPv6HexCompressedAddress(input);
|
||||
}
|
||||
/**
|
||||
* 获取客户端ip地址
|
||||
* @param request 请求
|
||||
* @return ip地址
|
||||
*/
|
||||
public static String getIpAddress(HttpServletRequest request) {
|
||||
return Optional.ofNullable(request.getHeader(Constants.Headers.X_REAL_IP))
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.orElseGet(request::getRemoteAddr);
|
||||
}
|
||||
/**
|
||||
* 获取客户端mac地址
|
||||
* @param request 请求
|
||||
* @return mac地址
|
||||
*/
|
||||
public static String getMacAddress(HttpServletRequest request) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
public class InvokeUtility {
|
||||
private static DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
/**
|
||||
* 根据属性名获取属性值
|
||||
* */
|
||||
public static Object getFieldValueByName(String fieldName, Object o) {
|
||||
try {
|
||||
return Arrays.stream(fieldName.split("\\."))
|
||||
.reduce(o, (obj,property)->{
|
||||
String firstLetter = property.substring(0, 1).toUpperCase();
|
||||
String getter = "get" + firstLetter + property.substring(1);
|
||||
Object value = null;
|
||||
try {
|
||||
Method method = obj.getClass().getMethod(getter, new Class[] {});
|
||||
value = method.invoke(obj, new Object[] {});
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
if (value != null && value instanceof LocalDateTime) {
|
||||
return df.format((LocalDateTime) value);
|
||||
}
|
||||
return value;
|
||||
},(o1, o2) -> o2);
|
||||
// String firstLetter = fieldName.substring(0, 1).toUpperCase();
|
||||
// String getter = "get" + firstLetter + fieldName.substring(1);
|
||||
// Method method = o.getClass().getMethod(getter, new Class[] {});
|
||||
// Object value = method.invoke(o, new Object[] {});
|
||||
// return value;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性名数组
|
||||
* */
|
||||
public static String[] getFiledName(Object o){
|
||||
Field[] fields=o.getClass().getDeclaredFields();
|
||||
String[] fieldNames=new String[fields.length];
|
||||
for(int i=0;i<fields.length;i++){
|
||||
System.out.println(fields[i].getType());
|
||||
fieldNames[i]=fields[i].getName();
|
||||
}
|
||||
return fieldNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性类型(type),属性名(name),属性值(value)的map组成的list
|
||||
* */
|
||||
public static List getFiledsInfo(Object o){
|
||||
Field[] fields=o.getClass().getDeclaredFields();
|
||||
String[] fieldNames=new String[fields.length];
|
||||
List list = new ArrayList();
|
||||
Map infoMap=null;
|
||||
for(int i=0;i<fields.length;i++){
|
||||
infoMap = new HashMap();
|
||||
infoMap.put("type", fields[i].getType().toString());
|
||||
infoMap.put("name", fields[i].getName());
|
||||
infoMap.put("value", getFieldValueByName(fields[i].getName(), o));
|
||||
list.add(infoMap);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的所有属性值,返回一个对象数组
|
||||
* */
|
||||
public Object[] getFiledValues(Object o){
|
||||
String[] fieldNames=this.getFiledName(o);
|
||||
Object[] value=new Object[fieldNames.length];
|
||||
for(int i=0;i<fieldNames.length;i++){
|
||||
value[i]=this.getFieldValueByName(fieldNames[i], o);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.lionsoul.ip2region.xdb.Searcher;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
public class IpRegionUtil {
|
||||
static Searcher searcher;
|
||||
|
||||
static {
|
||||
try {
|
||||
|
||||
InputStream ins = IpRegionUtil.class.getClassLoader().getResourceAsStream("ip2region.xdb");
|
||||
byte[] bytes = IoUtil.readBytes(ins);
|
||||
|
||||
searcher = Searcher.newWithBuffer( bytes);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询IP属地信息
|
||||
*
|
||||
* @param ip IP地址
|
||||
* @return 格式如: 2430|中国|0|重庆|重庆市|电信|89684
|
||||
*/
|
||||
public static String queryIp(String ip) {
|
||||
String search = null;
|
||||
try {
|
||||
search = searcher.search(ip);
|
||||
log.debug("ip:{},details:{}", ip, search);
|
||||
} catch (Exception e) {
|
||||
log.error("查询IP出错:", e);
|
||||
}
|
||||
return search;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测IP是否属于某地
|
||||
*
|
||||
* @param ip
|
||||
* @param name 省/市/县名等
|
||||
* @return 属于返回true否则false
|
||||
*/
|
||||
public static boolean belong(String ip, String name) {
|
||||
return Optional.ofNullable(queryIp(ip))
|
||||
.map(StrUtil::trimToNull)
|
||||
.filter(s -> s.contains(name))
|
||||
.isPresent();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package com.aisino.iles.common.util;
|
||||
import com.koal.kms.sdk.ed.KmsSdkException;
|
||||
import com.koal.kms.sdk.ed.KmsUtil;
|
||||
import com.koal.kms.sdk.ed.Mode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
public class KmsServer {
|
||||
|
||||
/**
|
||||
* 数据加密
|
||||
* originData 待加密数据
|
||||
*/
|
||||
public static String kmsEncrypt(String originData){
|
||||
log.info("原始数据={}",originData);
|
||||
if(originData!=null && !originData.isEmpty()){
|
||||
|
||||
try {
|
||||
byte[] encryptData = KmsUtil.encrypt(originData.getBytes(), Mode.CBC);
|
||||
// 若要保存密文,使用Base64做编码处理
|
||||
String saveEncryptData = Base64.getEncoder().encodeToString(encryptData);
|
||||
log.info("加密数据={}",saveEncryptData);
|
||||
return saveEncryptData;
|
||||
} catch (KmsSdkException e) {
|
||||
log.error("加密失败={}",e.getMessage());
|
||||
return "";
|
||||
}
|
||||
}else{
|
||||
log.info("待加密数据为空串");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据解密
|
||||
* encryptData 加密后的数据
|
||||
*/
|
||||
public static String kmsDencrypt(String encryptData) {
|
||||
log.info("待解密数据={}",encryptData);
|
||||
if(encryptData!=null && !encryptData.isEmpty()){
|
||||
try {
|
||||
// 解密前使用Base64做解码处理
|
||||
byte[] decryptData = Base64.getDecoder().decode(encryptData);
|
||||
try {
|
||||
byte[] decryptResult = KmsUtil.decrypt(decryptData, Mode.CBC);
|
||||
String result=new String(decryptResult);
|
||||
log.info("解密后={}",result);
|
||||
return result;
|
||||
} catch (KmsSdkException e) {
|
||||
log.error("解密失败={}",e.getMessage());
|
||||
return "";
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error("Base64转换失败={}",e.getMessage());
|
||||
return encryptData;
|
||||
}
|
||||
}else {
|
||||
log.info("待解密数据为空串");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据校验
|
||||
* originData 加密前的数据原文
|
||||
*/
|
||||
public static String kmsSign(String originData) {
|
||||
log.info("校验原始数据={}",originData);
|
||||
if(originData!=null && !originData.isEmpty()){
|
||||
try{
|
||||
// 解密前使用Base64做解码处理
|
||||
byte[] originByte = Base64.getDecoder().decode(originData);
|
||||
try {
|
||||
byte[] signByteData = KmsUtil.hmacSM3Encrypt(originByte);
|
||||
// String sginData=new String(signByteData);
|
||||
String sginData=Base64.getEncoder().encodeToString(signByteData);
|
||||
log.info("校验后返回签名={}",sginData);
|
||||
return sginData;
|
||||
} catch (KmsSdkException e) {
|
||||
log.error("校验失败={}",e.getMessage());
|
||||
return "";
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error("Base64转换失败={}",e.getMessage());
|
||||
return originData;
|
||||
}
|
||||
}else{
|
||||
log.info("校验原始数据为空串");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* ****************************************************
|
||||
*
|
||||
* @author shishaobo
|
||||
* @ClassName: Md5Utils
|
||||
* @Description: TODO(MD5加密 工具类)
|
||||
* @date 2018年9月12日 下午3:04:01
|
||||
* <p>
|
||||
* ****************************************************
|
||||
*/
|
||||
|
||||
public class Md5Utils {
|
||||
|
||||
/**
|
||||
* @param string 待加密字符串
|
||||
* @return String MD5加密后的字符串
|
||||
* @throws
|
||||
* @Title: getMd5Str
|
||||
* @Description: TODO(对字符串进行MD5加密)
|
||||
*/
|
||||
public static String getMd5Str(String string) {
|
||||
try {
|
||||
//拿到一个MD5转换器(如果想要SHA1加密参数换成"SHA1")
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
|
||||
//输入的字符串转换成字节数组
|
||||
byte[] inputByteArray = string.getBytes();
|
||||
//inputByteArray是输入字符串转换得到的字节数组
|
||||
messageDigest.update(inputByteArray);
|
||||
//转换并返回结果,也是字节数组,包含16个元素
|
||||
byte[] resultByteArray = messageDigest.digest();
|
||||
//字符数组转换成字符串返回
|
||||
return byteArrayToHex(resultByteArray);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param byteArray 需要转换的字符数组
|
||||
* @return String 转换完成的字符串
|
||||
* @throws
|
||||
* @Title: byteArrayToHex
|
||||
* @Description: TODO(字符数组转换成字符串)
|
||||
*/
|
||||
public static String byteArrayToHex(byte[] byteArray) {
|
||||
//首先初始化一个字符数组,用来存放每个16进制字符
|
||||
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
//new一个字符数组,这个就是用来组成结果字符串的(解释一下:一个byte是八位二进制,也就是2位十六进制字符)
|
||||
char[] resultCharArray = new char[byteArray.length * 2];
|
||||
//遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去
|
||||
int index = 0;
|
||||
for (byte b : byteArray) {
|
||||
resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
|
||||
resultCharArray[index++] = hexDigits[b & 0xf];
|
||||
}
|
||||
|
||||
//字符数组组合成字符串返回
|
||||
return new String(resultCharArray);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.http.Header;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.ssl.TrustAnyHostnameVerifier;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MigrationUtil {
|
||||
|
||||
private static String base;
|
||||
|
||||
private MigrationUtil() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static String gab_url() {
|
||||
return base + "/migration/qg_gabczrk";
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static JsonNode getGabrkxx(String zjhm) {
|
||||
String body;
|
||||
JsonNode ret;
|
||||
try {
|
||||
body = HttpUtil.createGet(gab_url() + "?sfzh=" + zjhm)
|
||||
.setHostnameVerifier(new TrustAnyHostnameVerifier())
|
||||
.header(Header.ACCEPT, ContentType.JSON.getValue())
|
||||
.contentType(ContentType.JSON.getValue())
|
||||
.disableCache()
|
||||
.execute()
|
||||
.body();
|
||||
if (StrUtil.isBlank(body)) {
|
||||
throw new RuntimeException("公安部人口库接口无返回");
|
||||
}
|
||||
ret = new ObjectMapper().readTree(body);
|
||||
return ret;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void initBase(@Value("http://151.50.3.163:9095") String base) {
|
||||
MigrationUtil.base = base;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*
|
||||
* @Author: yj
|
||||
* @Date: 2019/10/21 0021 9:50
|
||||
*/
|
||||
public class PageBean {
|
||||
|
||||
private Integer page;
|
||||
private Integer pageSize;
|
||||
|
||||
private Integer startWith;
|
||||
private Integer offset;
|
||||
private Integer totalPage;
|
||||
private Integer count;
|
||||
|
||||
|
||||
public static PageBean buildPageBean(Integer page, Integer pageSize, Integer count) {
|
||||
return new PageBean(page, pageSize, count);
|
||||
}
|
||||
|
||||
public PageBean() {
|
||||
}
|
||||
|
||||
public PageBean(Integer page, Integer pageSize, Integer count) {
|
||||
this.page = page;
|
||||
this.pageSize = pageSize;
|
||||
this.count = count;
|
||||
this.totalPage = count % pageSize == 0 ? count / pageSize : count / pageSize + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置页码
|
||||
*/
|
||||
public void setPage(Integer page) {
|
||||
if (page > totalPage) {
|
||||
this.page = 0;
|
||||
} else {
|
||||
this.page = page;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setOffset(Integer offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置偏移量
|
||||
*/
|
||||
public void setOffset() {
|
||||
Integer offsets = this.page * this.pageSize;
|
||||
if (offsets > count) {
|
||||
this.offset = count - offsets;
|
||||
} else {
|
||||
this.offset = this.pageSize;
|
||||
}
|
||||
}
|
||||
|
||||
public void setStartWith() {
|
||||
this.startWith = this.page * this.pageSize;
|
||||
}
|
||||
|
||||
public Integer getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
public Integer getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
public void setPageSize(Integer pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
public Integer getStartWith() {
|
||||
return startWith;
|
||||
}
|
||||
|
||||
public Integer getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public Integer getTotalPage() {
|
||||
return totalPage;
|
||||
}
|
||||
|
||||
public void setTotalPage(Integer totalPage) {
|
||||
this.totalPage = totalPage;
|
||||
}
|
||||
|
||||
public Integer getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(Integer count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 用于生成分页数据集的小工具。
|
||||
*
|
||||
* @author huxin
|
||||
*/
|
||||
public class PageableHelper {
|
||||
/**
|
||||
* 生成分页数据
|
||||
*
|
||||
* @param page 页码 (1开始)
|
||||
* @param limit 每页显示数(默认20)
|
||||
* @param sort 排序列
|
||||
* @param dir 排序方式 升降序
|
||||
* @return 分页数据
|
||||
*/
|
||||
public static Pageable buildPageRequest(Integer page, Integer limit, String sort, String dir) {
|
||||
return PageRequest.of(
|
||||
Optional.ofNullable(page).filter(p -> p > 0).map(p -> p - 1).orElse(0),
|
||||
Optional.ofNullable(limit).filter(l -> l > 0).orElse(20),
|
||||
Optional.ofNullable(sort).filter(StringUtils::isNotEmpty)
|
||||
.map(s -> Sort.by(
|
||||
Optional.ofNullable(dir)
|
||||
.filter(StringUtils::isNotEmpty)
|
||||
.flatMap(Sort.Direction::fromOptionalString)
|
||||
.orElse(Sort.Direction.ASC),
|
||||
s.split(","))
|
||||
).orElse(Sort.unsorted())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成分页数据
|
||||
*
|
||||
* @param page 页码 (1开始)
|
||||
* @param limit 每页显示数(默认20)
|
||||
* @return 分页数据
|
||||
*/
|
||||
public static Pageable buildPageRequest(Integer page, Integer limit) {
|
||||
return buildPageRequest(page, limit, null, null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,365 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import com.aisino.iles.lawenforcement.model.Document;
|
||||
import org.apache.pdfbox.multipdf.PDFMergerUtility;
|
||||
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||
import org.apache.pdfbox.pdmodel.PDPage;
|
||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType0Font;
|
||||
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PdfMerger {
|
||||
private static final Logger log = LoggerFactory.getLogger(PdfMerger.class);
|
||||
|
||||
// 定义输入文件结构
|
||||
public static class PdfInput {
|
||||
private final String fileName;
|
||||
private final Document document;
|
||||
private final byte[] data; // 存储字节数据而非流
|
||||
|
||||
public PdfInput(String fileName,Document document, byte[] data) throws IOException {
|
||||
this.fileName = fileName;
|
||||
this.document = document;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public InputStream getStream() {
|
||||
return new ByteArrayInputStream(data);
|
||||
}
|
||||
}
|
||||
|
||||
public static void mergePDFsWithPageNumbers(List<PdfInput> inputs, String outputFile) throws IOException {
|
||||
PDDocument mergedDocument = new PDDocument();
|
||||
List<FileInfo> fileInfos = new ArrayList<>();
|
||||
int totalPages = 1; // 包含目录页
|
||||
|
||||
// 第一阶段:收集文档信息
|
||||
for (PdfInput input : inputs) {// 获取新流
|
||||
PDDocument srcDoc = PDDocument.load(input.data);
|
||||
int pageCount = srcDoc.getNumberOfPages();
|
||||
int endPage = 0;
|
||||
if (pageCount>1)
|
||||
endPage = totalPages + pageCount;
|
||||
fileInfos.add(new FileInfo(input.fileName,input.document, pageCount, totalPages + 1,endPage));
|
||||
totalPages += pageCount;
|
||||
}
|
||||
// 添加目录页
|
||||
// addTableOfContents(mergedDocument, fileInfos);
|
||||
addTableOfContentsWithPage(mergedDocument, fileInfos);
|
||||
// 使用PDFMergerUtility来正确合并文档内容
|
||||
PDFMergerUtility merger = new PDFMergerUtility();
|
||||
for (PdfInput input : inputs ) {
|
||||
try (PDDocument doc = PDDocument.load(input.data)) {
|
||||
merger.appendDocument(mergedDocument, doc);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加页码到合并后的文档
|
||||
addPageNumbersToDocument(mergedDocument);
|
||||
|
||||
// 保存合并后的文档
|
||||
mergedDocument.save(outputFile);
|
||||
mergedDocument.close();
|
||||
}
|
||||
|
||||
|
||||
// 添加表格目录页实现
|
||||
private static void addTableOfContentsWithPage(PDDocument doc, List<FileInfo> fileInfos) throws IOException {
|
||||
List<PDPage> tocPages = new ArrayList<>();
|
||||
PDPage currentTocPage = new PDPage(PDRectangle.A4);
|
||||
tocPages.add(currentTocPage);
|
||||
doc.addPage(currentTocPage);
|
||||
|
||||
float yPosition = 750;
|
||||
|
||||
PDFont font = PDType0Font.load(doc, PdfMerger.class.getResourceAsStream("/fonts/simsun.ttf"));
|
||||
try (PDPageContentStream headerStream = new PDPageContentStream(doc, currentTocPage)) {
|
||||
headerStream.beginText();
|
||||
headerStream.setFont(font, 24);
|
||||
headerStream.newLineAtOffset(230, yPosition);
|
||||
headerStream.showText("卷 内 目 录");
|
||||
headerStream.endText();
|
||||
}
|
||||
yPosition -= 30; // 下移标题位置
|
||||
|
||||
List<String[]> data = new ArrayList<>();
|
||||
data.add(new String[]{"序号", "文件名称及编号", "日期", "页号", "备注"});
|
||||
DateTimeFormatter formatter= DateTimeFormatter.ofPattern("yyyy年MM月dd日");
|
||||
for (FileInfo info : fileInfos) {
|
||||
int startPage = info.startPage;
|
||||
int endPage = info.endPage;
|
||||
String pageStr = endPage>0? startPage +"-"+ endPage : String.valueOf(startPage);
|
||||
|
||||
String title;
|
||||
if(info.document.getDocumentCode()!=null&&!"".equals(info.document.getDocumentCode()))
|
||||
title = "《"+info.document.getDocumentName()+"》"+info.document.getDocumentCode();
|
||||
else
|
||||
title = "《"+info.document.getDocumentName()+"》" ;
|
||||
data.add(new String[]{info.document.getDocumentNo().toString(), title, info.document.getDocumentDate().format(formatter), pageStr,""});
|
||||
}
|
||||
try (PDPageContentStream entryStream = new PDPageContentStream(doc, currentTocPage, PDPageContentStream.AppendMode.APPEND, true)) {
|
||||
//drawTable(entryStream,50,yPosition,data,font);
|
||||
drawTableWithWrappedText(entryStream, 30, yPosition, data,
|
||||
new float[]{30f, 350f, 80f, 40f,30f}, 20f,font);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 绘制带自动换行的表格
|
||||
* @param contentStream 内容流
|
||||
* @param startX 起始X坐标
|
||||
* @param startY 起始Y坐标
|
||||
* @param data 表格数据
|
||||
* @param colWidths 每列宽度
|
||||
* @param rowHeight 行高
|
||||
* @param rowHeight 行高
|
||||
*/
|
||||
private static void drawTableWithWrappedText( PDPageContentStream contentStream,
|
||||
float startX, float startY, List<String[]> data,
|
||||
float[] colWidths, float rowHeight,PDFont font) throws IOException {
|
||||
|
||||
final int rows = data.size();
|
||||
final int cols = data.get(0).length;
|
||||
float tableWidth = 0;
|
||||
for (float width : colWidths) {
|
||||
tableWidth += width;
|
||||
}
|
||||
|
||||
// 绘制表格边框
|
||||
contentStream.setLineWidth(1f);
|
||||
drawTableBorders(contentStream, startX, startY, tableWidth, rows, colWidths, rowHeight);
|
||||
|
||||
// 设置字体
|
||||
contentStream.setFont(font, 10);
|
||||
float textStartX = startX + 5;
|
||||
float textStartY = startY - 15;
|
||||
|
||||
// 处理每行数据
|
||||
for (int i = 0; i < rows; i++) {
|
||||
String[] rowData = data.get(i);
|
||||
float maxHeightInRow = rowHeight; // 记录当前行最大高度
|
||||
|
||||
// 处理每列数据
|
||||
for (int j = 0; j < cols; j++) {
|
||||
String cellText = rowData[j];
|
||||
float colWidth = colWidths[j] - 10; // 留出边距
|
||||
|
||||
// 获取自动换行后的文本行
|
||||
List<String> lines = wrapText(cellText, colWidth, font, 16);
|
||||
|
||||
// 计算当前单元格需要的高度
|
||||
float cellHeight = lines.size() * 12; // 每行文本高度12
|
||||
if (cellHeight > maxHeightInRow) {
|
||||
maxHeightInRow = cellHeight;
|
||||
}
|
||||
|
||||
// 绘制文本
|
||||
float currentY = textStartY;
|
||||
for (String line : lines) {
|
||||
contentStream.beginText();
|
||||
contentStream.newLineAtOffset(textStartX, currentY);
|
||||
if(i==0){
|
||||
// 设置描边颜色(与填充色相同)
|
||||
contentStream.setStrokingColor(Color.BLACK);
|
||||
}
|
||||
contentStream.showText(line);
|
||||
contentStream.endText();
|
||||
currentY -= 12; // 移动到下一行
|
||||
}
|
||||
|
||||
textStartX += colWidths[j];
|
||||
}
|
||||
|
||||
// 调整下一行的起始位置
|
||||
textStartY -= maxHeightInRow;
|
||||
textStartX = startX + 5;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制表格边框
|
||||
*/
|
||||
private static void drawTableBorders(PDPageContentStream contentStream, float startX, float startY,
|
||||
float tableWidth, int rows, float[] colWidths, float rowHeight)
|
||||
throws IOException {
|
||||
// 绘制水平线
|
||||
float nextY = startY;
|
||||
for (int i = 0; i <= rows; i++) {
|
||||
contentStream.moveTo(startX, nextY);
|
||||
contentStream.lineTo(startX + tableWidth, nextY);
|
||||
contentStream.stroke();
|
||||
nextY -= rowHeight;
|
||||
}
|
||||
|
||||
// 绘制垂直线
|
||||
float nextX = startX;
|
||||
contentStream.moveTo(nextX, startY);
|
||||
contentStream.lineTo(nextX, startY - rows * rowHeight);
|
||||
contentStream.stroke();
|
||||
|
||||
for (float width : colWidths) {
|
||||
nextX += width;
|
||||
contentStream.moveTo(nextX, startY);
|
||||
contentStream.lineTo(nextX, startY - rows * rowHeight);
|
||||
contentStream.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本自动换行处理
|
||||
* @param text 原始文本
|
||||
* @param maxWidth 最大宽度
|
||||
* @param font 字体
|
||||
* @param fontSize 字体大小
|
||||
* @return 换行后的文本列表
|
||||
*/
|
||||
private static List<String> wrapText(String text, float maxWidth, PDFont font, int fontSize) throws IOException {
|
||||
List<String> lines = new ArrayList<>();
|
||||
String[] words = text.split(" ");
|
||||
StringBuilder currentLine = new StringBuilder();
|
||||
|
||||
for (String word : words) {
|
||||
// 测试添加单词后是否超出宽度
|
||||
String testLine = currentLine.length() > 0 ?
|
||||
currentLine.toString() + " " + word : word;
|
||||
float testWidth = font.getStringWidth(testLine) / 1000 * fontSize;
|
||||
|
||||
if (testWidth <= maxWidth) {
|
||||
currentLine.append(currentLine.length() > 0 ? " " + word : word);
|
||||
} else {
|
||||
// 当前行已满,添加到结果
|
||||
if (currentLine.length() > 0) {
|
||||
lines.add(currentLine.toString());
|
||||
currentLine = new StringBuilder(word);
|
||||
} else {
|
||||
// 单个单词就超过宽度,强制分割
|
||||
lines.add(word);
|
||||
currentLine = new StringBuilder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加最后一行
|
||||
if (currentLine.length() > 0) {
|
||||
lines.add(currentLine.toString());
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
||||
// 添加目录页实现
|
||||
private static void addTableOfContents(PDDocument doc, List<FileInfo> fileInfos) throws IOException {
|
||||
List<PDPage> tocPages = new ArrayList<>();
|
||||
PDPage currentTocPage = new PDPage(PDRectangle.A4);
|
||||
tocPages.add(currentTocPage);
|
||||
doc.addPage(currentTocPage);
|
||||
|
||||
float yPosition = 750;
|
||||
boolean isFirstPage = true;
|
||||
|
||||
PDFont font = PDType0Font.load(doc, PdfMerger.class.getResourceAsStream("/fonts/simsun.ttf"));
|
||||
for (FileInfo info : fileInfos) {
|
||||
// 处理新页面的标题(仅第一页)
|
||||
if (isFirstPage) {
|
||||
try (PDPageContentStream headerStream = new PDPageContentStream(doc, currentTocPage)) {
|
||||
headerStream.beginText();
|
||||
headerStream.setFont(font, 24);
|
||||
headerStream.newLineAtOffset(230, yPosition);
|
||||
headerStream.showText("卷 内 目 录");
|
||||
headerStream.endText();
|
||||
}
|
||||
yPosition -= 50; // 下移标题位置
|
||||
isFirstPage = false;
|
||||
}
|
||||
|
||||
// 添加目录条目
|
||||
try (PDPageContentStream entryStream = new PDPageContentStream(doc, currentTocPage, PDPageContentStream.AppendMode.APPEND, true)) {
|
||||
entryStream.beginText();
|
||||
entryStream.setFont(font, 14);
|
||||
entryStream.newLineAtOffset(50, yPosition);
|
||||
entryStream.showText(info.fileName.replace("pdf","").replace("PDF","") +"..................................."+ info.startPage);
|
||||
entryStream.endText();
|
||||
}
|
||||
|
||||
yPosition -= 30;
|
||||
|
||||
// 分页逻辑
|
||||
if (yPosition < 50 && fileInfos.indexOf(info) < fileInfos.size()-1) {
|
||||
currentTocPage = new PDPage(PDRectangle.A4);
|
||||
doc.addPage(currentTocPage);
|
||||
tocPages.add(currentTocPage);
|
||||
yPosition = 750;
|
||||
isFirstPage = true; // 新页面需要重新添加标题
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 文件信息记录类
|
||||
private static class FileInfo {
|
||||
final String fileName;
|
||||
final Document document;
|
||||
final int pageCount;
|
||||
final int startPage;
|
||||
final int endPage;
|
||||
|
||||
FileInfo(String fileName,Document document, int pageCount, int startPage, int endPage) {
|
||||
this.fileName = fileName;
|
||||
this.document = document;
|
||||
this.pageCount = pageCount;
|
||||
this.startPage = startPage;
|
||||
this.endPage = endPage;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void addPageNumbersToDocument(PDDocument document) throws IOException {
|
||||
int totalPages = document.getNumberOfPages();
|
||||
|
||||
for (int i = 0; i < totalPages; i++) {
|
||||
PDPage page = document.getPage(i);
|
||||
|
||||
// 获取页面尺寸
|
||||
PDRectangle pageSize = page.getMediaBox();
|
||||
float pageWidth = pageSize.getWidth();
|
||||
PDFont font = PDType0Font.load(document, PdfMerger.class.getResourceAsStream("/fonts/simsun.ttf"));
|
||||
// 添加页码
|
||||
try (PDPageContentStream contentStream = new PDPageContentStream(
|
||||
document,
|
||||
page,
|
||||
PDPageContentStream.AppendMode.APPEND,
|
||||
true,
|
||||
true)) {
|
||||
|
||||
// 设置字体和字号
|
||||
contentStream.setFont(font, 10);
|
||||
|
||||
// 页码文本
|
||||
String pageText = String.format("%d / %d", i + 1, totalPages);
|
||||
|
||||
// 计算文本宽度以居中显示
|
||||
float textWidth = font.getStringWidth(pageText) / 1000 * 10;
|
||||
float startX = (pageWidth - textWidth) / 2;
|
||||
|
||||
// 在页面底部添加页码
|
||||
contentStream.beginText();
|
||||
contentStream.newLineAtOffset(startX, 20); // 20是距底部的距离
|
||||
contentStream.showText(pageText);
|
||||
contentStream.endText();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.pinyin.PinyinUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
public class PinYinUtils {
|
||||
|
||||
/**
|
||||
* 汉字转为拼音
|
||||
*
|
||||
* @param chinese 中文
|
||||
* @return 拼音
|
||||
*/
|
||||
public static String toPinyin(String chinese) {
|
||||
return Optional.ofNullable(chinese)
|
||||
.map(StrUtil::trimToNull)
|
||||
.map(s -> PinyinUtil.getPinyin(s, ""))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 汉字转换简拼
|
||||
*
|
||||
* @param chinese 中文
|
||||
* @return 简拼
|
||||
*/
|
||||
public static String toSimplePinyin(String chinese) {
|
||||
return Optional.ofNullable(chinese)
|
||||
.map(StrUtil::trimToNull)
|
||||
.map(s -> PinyinUtil.getFirstLetter(s, ""))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为中文
|
||||
*
|
||||
* @param chineseWord 中文
|
||||
* @return true为中文, false 不是中文
|
||||
*/
|
||||
public static boolean isChinese(char chineseWord) {
|
||||
return PinyinUtil.isChinese(chineseWord);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author yunfei
|
||||
* @ClassName: RSAUtil
|
||||
* @Description: RSA加密 工具类
|
||||
* @date 2021年03月26日 下午3:04:01
|
||||
* <p>
|
||||
*/
|
||||
public class RSAUtil {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param input 待加密的字符串
|
||||
* @param rsaPublicKey 密钥
|
||||
* @return String RSA加密后的字符串
|
||||
* @Description: 对字符串进行RSA加密
|
||||
*/
|
||||
public static String encrypt(String input, String rsaPublicKey) {
|
||||
String result = "";
|
||||
try {
|
||||
// 将Base64编码后的公钥转换成PublicKey对象
|
||||
byte[] buffer = Base64.decodeBase64(rsaPublicKey);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
|
||||
PublicKey publicKey = keyFactory.generatePublic(keySpec);
|
||||
// 加密
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
byte[] inputArray = input.getBytes();
|
||||
int inputLength = inputArray.length;
|
||||
// 最大加密字节数,超出最大字节数需要分组加密
|
||||
int MAX_ENCRYPT_BLOCK = 117;
|
||||
// 标识
|
||||
int offSet = 0;
|
||||
byte[] resultBytes = {};
|
||||
byte[] cache = {};
|
||||
while (inputLength - offSet > 0) {
|
||||
if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
|
||||
cache = cipher.doFinal(inputArray, offSet, MAX_ENCRYPT_BLOCK);
|
||||
offSet += MAX_ENCRYPT_BLOCK;
|
||||
} else {
|
||||
cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
|
||||
offSet = inputLength;
|
||||
}
|
||||
resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
|
||||
System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
|
||||
}
|
||||
result = Base64.encodeBase64String(resultBytes);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.http.Header;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.ssl.TrustAnyHostnameVerifier;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.aisino.iles.common.model.FaceValid;
|
||||
import com.aisino.iles.common.model.OcrValid;
|
||||
import com.aisino.iles.common.model.Ok;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ReconUtil {
|
||||
|
||||
private static String base;
|
||||
|
||||
private ReconUtil() {
|
||||
|
||||
}
|
||||
|
||||
private static String recon_url() {
|
||||
return base + "/recon-api/ocr";
|
||||
}
|
||||
|
||||
private static String face_url() {
|
||||
return base + "/recon-api/face";
|
||||
}
|
||||
|
||||
private static <T> Ok<T> failure(Integer code, String msg) {
|
||||
Ok<T> ok = Ok.of();
|
||||
ok.setMsg(msg);
|
||||
ok.setCode(code);
|
||||
ok.setSuccess(false);
|
||||
return ok;
|
||||
}
|
||||
|
||||
private static String trimBase64(final String base64) {
|
||||
if (StringUtils.isNotBlank(base64)) {
|
||||
String s = StringUtils.trimToEmpty(base64);
|
||||
return s.substring(s.indexOf(",") + 1);
|
||||
}
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* OCR识别 证件 护照等
|
||||
*
|
||||
* @param base64Img 待识别的图片内容
|
||||
* @param imgType 图片类型 jpg,png,bmp
|
||||
* @param reconType 识别类型
|
||||
* 二代身份证正面 2<br />
|
||||
* 二代身份证证背面 3<br />
|
||||
* 银行卡 17<br />
|
||||
* 车牌 19<br />
|
||||
* 名片 20<br />
|
||||
* @return 比对结果 Ok.code==0 成功 ok
|
||||
*/
|
||||
public static Ok<OcrValid> reconCards(String base64Img, String imgType, Integer reconType) {
|
||||
String body;
|
||||
try {
|
||||
body = HttpUtil.createPost(recon_url())
|
||||
.setHostnameVerifier(new TrustAnyHostnameVerifier())
|
||||
.disableCache()
|
||||
.header(Header.ACCEPT, ContentType.JSON.getValue())
|
||||
.contentType(ContentType.JSON.getValue())
|
||||
.body(new JSONObject() {{
|
||||
set("img", trimBase64(base64Img));
|
||||
set("imgType", imgType);
|
||||
set("reconType", reconType);
|
||||
}}.toStringPretty())
|
||||
.execute()
|
||||
.body();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return resolveOcrResult(body);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Ok<OcrValid> resolveOcrResult(String body) {
|
||||
if (StringUtils.isBlank(body)) {
|
||||
return failure(1, "OCR识别接口无数据返回");
|
||||
}
|
||||
JsonNode ret;
|
||||
try {
|
||||
ret = new ObjectMapper().readTree(body);
|
||||
} catch (Exception ex) {
|
||||
return failure(1, "OCR识别接口数据格式错误" + ex.getMessage());
|
||||
}
|
||||
return (Ok<OcrValid>) Optional.of(ret)
|
||||
.map(j -> {
|
||||
if (j.path("code").asInt() == 0) {
|
||||
try {
|
||||
return Ok.of(new ObjectMapper().readValue(j.path("data").toString(), OcrValid.class), j.path("msg").asText());
|
||||
} catch (Exception e) {
|
||||
return failure(1, "OCR识别接口数据格式错误" + e.getMessage());
|
||||
}
|
||||
} else {
|
||||
return failure(j.path("code").asInt(), j.path("msg").asText());
|
||||
}
|
||||
}).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* OCR识别 营业执照 不动产登记证 等
|
||||
*
|
||||
* @param base64Img 待识别的图片内容
|
||||
* @param LGId 识别类型
|
||||
* 1:机动车合格证;2:机动车登记证;3:不动产登记证;5:营业执照;6:常住人口登记卡
|
||||
* @return 比对结果 Ok.code==0 成功 ok
|
||||
*/
|
||||
public static Ok<OcrValid> reconLG(String base64Img, String LGId) {
|
||||
String body;
|
||||
try {
|
||||
body = HttpUtil.createPost(recon_url() + "/lg-recon")
|
||||
.setHostnameVerifier(new TrustAnyHostnameVerifier())
|
||||
.header(Header.ACCEPT, ContentType.JSON.getValue())
|
||||
.contentType(ContentType.JSON.getValue())
|
||||
.disableCache()
|
||||
.body(new JSONObject() {
|
||||
{
|
||||
set("img", trimBase64(base64Img));
|
||||
set("LGId", LGId);
|
||||
}
|
||||
}.toStringPretty())
|
||||
.execute()
|
||||
.body();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return resolveOcrResult(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸比对
|
||||
*
|
||||
* @param base64Img1 第1张图片内容的base64格式字符串
|
||||
* @param base64Img2 第2张图片内容的base64格式字符串
|
||||
* @param threshold 比对阈值 结果小于该值 code将不为0
|
||||
* @return 比对结果
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Ok<FaceValid> faceCompare(String base64Img1, String base64Img2, Integer threshold) {
|
||||
String body;
|
||||
try {
|
||||
body = HttpUtil.createPost(face_url())
|
||||
.setHostnameVerifier(new TrustAnyHostnameVerifier())
|
||||
.header(Header.ACCEPT, ContentType.JSON.getValue())
|
||||
.contentType(ContentType.JSON.getValue())
|
||||
.disableCache()
|
||||
.body(new JSONObject() {
|
||||
{
|
||||
set("img1", trimBase64(base64Img1));
|
||||
set("img2", trimBase64(base64Img2));
|
||||
set("threshold", threshold);
|
||||
}
|
||||
}.toStringPretty())
|
||||
.execute()
|
||||
.body();
|
||||
} catch (Exception e) {
|
||||
return failure(1, "请求人脸比对接口出错" + e.getMessage());
|
||||
}
|
||||
if (StringUtils.isBlank(body)) {
|
||||
return failure(1, "人脸比对接口无数据返回");
|
||||
}
|
||||
JsonNode ret;
|
||||
try {
|
||||
ret = new ObjectMapper().readTree(body);
|
||||
} catch (Exception e) {
|
||||
return failure(1, "人脸比对接口数据格式错误" + e.getMessage());
|
||||
}
|
||||
return (Ok<FaceValid>) Optional.of(ret)
|
||||
.map(j -> {
|
||||
FaceValid valid = null;
|
||||
if (j.has("data")) {
|
||||
try {
|
||||
valid = new ObjectMapper().readValue(j.path("data").toString(), FaceValid.class);
|
||||
} catch (JsonProcessingException e) {
|
||||
return failure(1, "人脸比对接口数据格式错误" + e.getMessage());
|
||||
}
|
||||
}
|
||||
if (j.path("code").asInt() == 0) {
|
||||
return Ok.of(valid, j.path("msg").asText());
|
||||
} else {
|
||||
Ok<FaceValid> r = failure(j.path("code").asInt(), j.path("msg").asText());
|
||||
r.setData(valid);
|
||||
return r;
|
||||
}
|
||||
}).get();
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void initBase(@Value("${recon.base:http://localhost:9980}") String base) {
|
||||
ReconUtil.base = base;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,283 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.crypto.Mode;
|
||||
import cn.hutool.crypto.Padding;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.symmetric.SM4;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.beanutils.PropertyUtils;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.security.Key;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.Security;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 国密SM4编码工具
|
||||
*/
|
||||
@Slf4j
|
||||
public class SM4Util {
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
private static final String ENCODING = "UTF-8";
|
||||
public static final String ALGORITHM_NAME = "SM4";
|
||||
// 加密算法/分组加密模式/分组填充方式
|
||||
// PKCS5Padding-以8个字节为一组进行分组加密
|
||||
// 定义分组加密模式使用:PKCS5Padding
|
||||
public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
|
||||
public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";
|
||||
// 128-32位16进制;256-64位16进制
|
||||
public static final int DEFAULT_KEY_SIZE = 128;
|
||||
|
||||
private static final String PROVIDER_NAME = BouncyCastleProvider.PROVIDER_NAME;
|
||||
// private static final String PROVIDER_NAME = "temp";
|
||||
|
||||
/**
|
||||
* 自动生成密钥
|
||||
*
|
||||
* @return
|
||||
* @explain
|
||||
*/
|
||||
public static String generateKey() throws Exception {
|
||||
return new String(Hex.encodeHex(generateKey(DEFAULT_KEY_SIZE), false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param keySize
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @explain
|
||||
*/
|
||||
public static byte[] generateKey(int keySize) throws Exception {
|
||||
KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, PROVIDER_NAME);
|
||||
kg.init(keySize, new SecureRandom());
|
||||
return kg.generateKey().getEncoded();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成ECB暗号
|
||||
*
|
||||
* @param algorithmName 算法名称
|
||||
* @param mode 模式
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @explain ECB模式(电子密码本模式:Electronic codebook)
|
||||
*/
|
||||
private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance(algorithmName, PROVIDER_NAME);
|
||||
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
|
||||
cipher.init(mode, sm4Key);
|
||||
return cipher;
|
||||
}
|
||||
|
||||
/**
|
||||
* sm4加密
|
||||
*
|
||||
* @param hexKey 16进制密钥(忽略大小写)
|
||||
* @param paramStr 待加密字符串
|
||||
* @return 返回16进制的加密字符串
|
||||
* @explain 加密模式:ECB
|
||||
* 密文长度不固定,会随着被加密字符串长度的变化而变化
|
||||
*/
|
||||
public static String encryptEcb(String hexKey, String paramStr) {
|
||||
try {
|
||||
String cipherText = "";
|
||||
// 16进制字符串-->byte[]
|
||||
byte[] keyData = ByteUtils.fromHexString(hexKey);
|
||||
// String-->byte[]
|
||||
byte[] srcData = paramStr.getBytes(ENCODING);
|
||||
// 加密后的数组
|
||||
byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData);
|
||||
// byte[]-->hexString
|
||||
cipherText = ByteUtils.toHexString(cipherArray);
|
||||
return cipherText;
|
||||
} catch (Exception e) {
|
||||
return paramStr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密模式之Ecb
|
||||
*
|
||||
* @param key
|
||||
* @param data
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @explain
|
||||
*/
|
||||
public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) throws Exception {
|
||||
Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
|
||||
return cipher.doFinal(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* sm4解密
|
||||
*
|
||||
* @param hexKey 16进制密钥
|
||||
* @param cipherText 16进制的加密字符串(忽略大小写)
|
||||
* @return 解密后的字符串
|
||||
* @throws Exception
|
||||
* @explain 解密模式:采用ECB
|
||||
*/
|
||||
public static String decryptEcb(String hexKey, String cipherText) throws IllegalBlockSizeException {
|
||||
// 用于接收解密后的字符串
|
||||
String decryptStr = "";
|
||||
// hexString-->byte[]
|
||||
byte[] keyData = ByteUtils.fromHexString(hexKey);
|
||||
// hexString-->byte[]
|
||||
byte[] cipherData = ByteUtils.fromHexString(cipherText);
|
||||
// 解密
|
||||
byte[] srcData = new byte[0];
|
||||
try {
|
||||
srcData = decrypt_Ecb_Padding(keyData, cipherData);
|
||||
// byte[]-->String
|
||||
decryptStr = new String(srcData, ENCODING);
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return decryptStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param key
|
||||
* @param cipherText
|
||||
* @return
|
||||
* @throws Exception
|
||||
* @explain
|
||||
*/
|
||||
public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText) throws Exception {
|
||||
Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
|
||||
return cipher.doFinal(cipherText);
|
||||
}
|
||||
public static byte[] decrypt_Cbc_Padding(byte[] key, byte[] cipherText) throws Exception {
|
||||
Cipher cipher = generateEcbCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key);
|
||||
return cipher.doFinal(cipherText);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验加密前后的字符串是否为同一数据
|
||||
*
|
||||
* @param hexKey 16进制密钥(忽略大小写)
|
||||
* @param cipherText 16进制加密后的字符串
|
||||
* @param paramStr 加密前的字符串
|
||||
* @return 是否为同一数据
|
||||
* @throws Exception
|
||||
* @explain
|
||||
*/
|
||||
public static boolean verifyEcb(String hexKey, String cipherText, String paramStr) throws Exception {
|
||||
// 用于接收校验结果
|
||||
boolean flag = false;
|
||||
// hexString-->byte[]
|
||||
byte[] keyData = ByteUtils.fromHexString(hexKey);
|
||||
// 将16进制字符串转换成数组
|
||||
byte[] cipherData = ByteUtils.fromHexString(cipherText);
|
||||
// 解密
|
||||
byte[] decryptData = decrypt_Ecb_Padding(keyData, cipherData);
|
||||
// 将原字符串转换成byte[]
|
||||
byte[] srcData = paramStr.getBytes(ENCODING);
|
||||
// 判断2个数组是否一致
|
||||
flag = Arrays.equals(decryptData, srcData);
|
||||
return flag;
|
||||
}
|
||||
/**警快办照片数据解谜*/
|
||||
public static String decryptCbc(String encryptedHex){
|
||||
byte[]encryptedBytes = SecureUtil.decode(encryptedHex);
|
||||
byte[] key = SecureUtil.decode("fc20c5aed0a942bcaafc0778b05cc21a");
|
||||
IvParameterSpec ivParameterSpec = new IvParameterSpec(encryptedBytes, 0, 16);
|
||||
SM4 sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, key, ivParameterSpec.getIV());
|
||||
return sm4.decryptStr(ArrayUtil.sub(encryptedBytes, 16, encryptedBytes.length));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
String json = "BF7B6BD7C1204BC4F3C87D235692DE9DBF7B6BD7C1204BC4F3C87D235692DE9DBF7B6BD7C1204BC4F3C87D235692DE9D";
|
||||
System.out.println("加密前源数据————" + json);
|
||||
// 生成32位16进制密钥
|
||||
String key = SM4Util.generateKey();
|
||||
System.out.println(key + "-----生成key");
|
||||
String cipher = SM4Util.encryptEcb(key, json);
|
||||
System.out.println("加密串---" + cipher);
|
||||
System.out.println(SM4Util.verifyEcb(key, cipher, json));
|
||||
json = SM4Util.decryptEcb(key, cipher);
|
||||
System.out.println("解密后数据---" + json);
|
||||
|
||||
String zjhm = "13452511037";
|
||||
System.out.println("SM4加密前字符串" + zjhm);
|
||||
String res = SM4Util.sm4Encrypt(zjhm);
|
||||
System.out.println("SM4加密后的字符串" + res);
|
||||
String result = SM4Util.sm4Decrypt(res);
|
||||
System.out.println("SM4解密后的证件号码" + result);
|
||||
byte[] bytes = ByteUtils.fromHexString("123123");
|
||||
System.out.println(bytes.length);
|
||||
String s = sm4Encrypt("".toString());
|
||||
System.out.println("ss:" + s);
|
||||
String decrypt = sm4Decrypt(s);
|
||||
System.out.println("dd:" + decrypt);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SM4加密后字符串
|
||||
* @Description SM4公用加密方法
|
||||
* @Author Jnno
|
||||
* @Version 1.0
|
||||
* @param: paramStr 待加密字符串
|
||||
* @Date 2020-11-9 14:47
|
||||
**/
|
||||
public static String sm4Encrypt(String paramStr) {
|
||||
return Optional.ofNullable(paramStr).filter(StringUtils::isNotEmpty).map(f -> SM4Util.encryptEcb(Constants.SM4_ENCRYPT_KEY, f)).orElse("");
|
||||
}
|
||||
|
||||
public static String sm4EncryptByKey(String key, String paramStr) {
|
||||
return Optional.ofNullable(paramStr).filter(StringUtils::isNotEmpty).map(f ->
|
||||
SM4Util.encryptEcb(StringUtils.isNotBlank(key)?key:Constants.SM4_ENCRYPT_KEY_JKSQGL, f)).orElse("");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SM4解密后的字符串
|
||||
* @Description
|
||||
* @Author Jnno
|
||||
* @Version 1.0
|
||||
* @param: cipherText 16进制加密后的字符串
|
||||
* @Date 2020-11-9 14:52
|
||||
**/
|
||||
public static String sm4Decrypt(String cipherText) throws IllegalBlockSizeException {
|
||||
if (cipherText == null) {
|
||||
return null;
|
||||
}
|
||||
return SM4Util.decryptEcb(Constants.SM4_ENCRYPT_KEY, cipherText);
|
||||
}
|
||||
|
||||
public static class ByteUtils {
|
||||
|
||||
public static String toHexString(byte[] bytes) {
|
||||
return org.bouncycastle.pqc.math.linearalgebra.ByteUtils.toHexString(bytes);
|
||||
// return null;
|
||||
}
|
||||
|
||||
public static byte[] fromHexString(String string) {
|
||||
return org.bouncycastle.pqc.math.linearalgebra.ByteUtils.fromHexString(string);
|
||||
// return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 字符工具
|
||||
*/
|
||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
/**
|
||||
* 去掉尾部指定字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param trailingString 匹配字符串
|
||||
* @return 去掉以后的字符串
|
||||
*/
|
||||
public static String trimTrailingString(String str, String trailingString) {
|
||||
if (!isNotEmpty(str)) {
|
||||
return str;
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder(str);
|
||||
int length = trailingString.length();
|
||||
while (sb.length() > 0 && sb.substring(sb.length() - length).equals(trailingString)) {
|
||||
sb.delete(sb.length() - length, sb.length());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 不为空
|
||||
// *
|
||||
// * @param str 字符串
|
||||
// * @return 是否不为空
|
||||
// */
|
||||
// public static Boolean isNotEmpty(String str) {
|
||||
// return !isEmpty(str);
|
||||
// }
|
||||
|
||||
/**
|
||||
* 根据西安机构代码规则调整字符串,移除尾部无效的零。
|
||||
* <p>
|
||||
* 处理规则:
|
||||
* 从机构代码字符串的开头开始,每两个字符作为一对进行检查。
|
||||
* 如果一对字符不是 "00",则这对字符及其之前的所有字符被视为有效部分。
|
||||
* 该方法会确定最后一个这样的有效两字符对的位置。
|
||||
* 最终返回的字符串是从原始字符串开始到这最后一个有效对结束的部分。
|
||||
* 所有在此之后的字符(包括后续的 "00" 对、不完整的对或仅由零组成的尾部)都将被移除。
|
||||
* </p>
|
||||
* <p>
|
||||
* 示例:
|
||||
* <ul>
|
||||
* <li>{@code trimEven0("01610112000000000")} 返回 {@code "01610112"}</li>
|
||||
* <li>{@code trimEven0("010010")} 返回 {@code "010010"} (因为 "10" 是最后一个非 "00" 对)</li>
|
||||
* <li>{@code trimEven0("01610000")} 返回 {@code "0161"} (因为 "61" 后的 "00" 对被移除)</li>
|
||||
* <li>{@code trimEven0("000100")} 返回 {@code "0001"} (因为 "01" 是最后一个非 "00" 对)</li>
|
||||
* <li>{@code trimEven0("000000")} 返回 {@code ""} (所有对都是 "00")</li>
|
||||
* <li>{@code trimEven0("01")} 返回 {@code "01"}</li>
|
||||
* <li>{@code trimEven0("0")} 返回 {@code ""} (不足一对有效字符)</li>
|
||||
* <li>{@code trimEven0("010")} 返回 {@code "01"} (最后的 "0" 不构成有效对的一部分,且它跟随在有效对 "01" 之后,所以被移除)</li>
|
||||
* <li>{@code trimEven0(null)} 返回 {@code null}</li>
|
||||
* <li>{@code trimEven0("")} 返回 {@code ""}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param agencyCode 原始机构代码字符串。
|
||||
* @return 处理后的机构代码字符串。如果输入为 {@code null} 或空字符串,则原样返回。
|
||||
*/
|
||||
public static String trimEven0(String agencyCode) {
|
||||
if (agencyCode == null || agencyCode.isEmpty()) {
|
||||
return agencyCode;
|
||||
}
|
||||
|
||||
int effectiveLength = -1; // 记录有效部分的长度,初始化为-1表示尚未找到有效部分
|
||||
|
||||
// 从字符串开头按两个字符一对进行遍历
|
||||
for (int i = 0; i + 1 < agencyCode.length(); i += 2) {
|
||||
String pair = agencyCode.substring(i, i + 2);
|
||||
if (!"00".equals(pair)) {
|
||||
effectiveLength = i + 2; // 更新有效长度到当前这对字符的末尾
|
||||
}
|
||||
}
|
||||
|
||||
if (effectiveLength == -1) {
|
||||
// 没有找到任何非 "00" 的字符对,或者字符串长度小于2 (例如 "0", "1")
|
||||
// 或者所有字符对都是 "00" (例如 "00", "0000")
|
||||
// 按照规则,这些情况应返回空字符串
|
||||
return "";
|
||||
}
|
||||
|
||||
return agencyCode.substring(0, effectiveLength);
|
||||
}
|
||||
|
||||
public static String removeStr(String resouce, String remove) {
|
||||
String result = resouce.replace(remove, "");
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String getPercentage(int fenzi, int fenmu) {
|
||||
// 创建一个数值格式化对象
|
||||
|
||||
NumberFormat numberFormat = NumberFormat.getInstance();
|
||||
|
||||
// 设置精确到小数点后2位
|
||||
|
||||
numberFormat.setMaximumFractionDigits(2);
|
||||
String result = "";
|
||||
if (fenmu == 0) {
|
||||
result = "0";
|
||||
} else {
|
||||
result = numberFormat.format((float) fenzi / (float) fenmu * 100);
|
||||
}
|
||||
return result + "%";
|
||||
}
|
||||
|
||||
/**
|
||||
* 去除字符串尾部所有0(trim0("3206000")=="3206")
|
||||
* 注意:此方法会把尾部所有0去掉,无论是奇数还是偶数个。
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static String trim0(String str) {
|
||||
String returnStr = str;
|
||||
for (int i = str.length(); i > 0; i--) {
|
||||
if (str.substring(i - 1, i).equals("0"))
|
||||
returnStr = str.substring(0, i - 1);
|
||||
else
|
||||
break;
|
||||
}
|
||||
return returnStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* str不足len位补0
|
||||
*
|
||||
* @param str 原字符串
|
||||
* int 字符串需要达到的位数
|
||||
* @return
|
||||
*/
|
||||
public static String add0(String str, int len) {
|
||||
String returnStr = "";
|
||||
returnStr = str;
|
||||
if (str != null) {
|
||||
if (str.length() < len) {
|
||||
for (int i = 0; i < (len - str.length()); i++) {
|
||||
returnStr += "0";
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 15位身份证号码到18位身份证号码的转换
|
||||
*
|
||||
* @param IDCardNO15 15位身份证号码
|
||||
* @return 18位身份证号码
|
||||
*/
|
||||
public static String IDCardNO15To18(String IDCardNO15) {
|
||||
String IDCardNO18 = "";
|
||||
if (IDCardNO15.length() == 15) {
|
||||
String IDCardNO17 = "";
|
||||
if (Integer.parseInt(IDCardNO15.substring(6, 8)) > 20)
|
||||
IDCardNO17 = IDCardNO15.substring(0, 6) + "19" + IDCardNO15.substring(6, 15);
|
||||
else
|
||||
IDCardNO17 = IDCardNO15.substring(0, 6) + "20" + IDCardNO15.substring(6, 15);
|
||||
IDCardNO18 = IDCardNO17 + getVerifyBit(IDCardNO17);
|
||||
} else
|
||||
IDCardNO18 = IDCardNO15;
|
||||
return IDCardNO18;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得18位身份证号码的最后一位
|
||||
* IDCardNO18 17位身份证号码
|
||||
*
|
||||
* @return 18位身份证号码的最后一位
|
||||
*/
|
||||
public static String getVerifyBit(String IDCardNO17) {
|
||||
String lastBit = "";
|
||||
int tempNo = 0;
|
||||
tempNo = (Integer.parseInt(IDCardNO17.substring(0, 1)) * 7 + Integer.parseInt(IDCardNO17.substring(1, 2)) * 9 +
|
||||
Integer.parseInt(IDCardNO17.substring(2, 3)) * 10 + Integer.parseInt(IDCardNO17.substring(3, 4)) * 5 +
|
||||
Integer.parseInt(IDCardNO17.substring(4, 5)) * 8 + Integer.parseInt(IDCardNO17.substring(5, 6)) * 4 +
|
||||
Integer.parseInt(IDCardNO17.substring(6, 7)) * 2 + Integer.parseInt(IDCardNO17.substring(7, 8)) * 1 +
|
||||
Integer.parseInt(IDCardNO17.substring(8, 9)) * 6 +
|
||||
Integer.parseInt(IDCardNO17.substring(9, 10)) * 3 + Integer.parseInt(IDCardNO17.substring(10, 11)) * 7 +
|
||||
Integer.parseInt(IDCardNO17.substring(11, 12)) * 9 + Integer.parseInt(IDCardNO17.substring(12, 13)) * 10 +
|
||||
Integer.parseInt(IDCardNO17.substring(13, 14)) * 5 +
|
||||
Integer.parseInt(IDCardNO17.substring(14, 15)) * 8 + Integer.parseInt(IDCardNO17.substring(15, 16)) * 4 +
|
||||
Integer.parseInt(IDCardNO17.substring(16, 17)) * 2) % 11;
|
||||
|
||||
switch (tempNo) {
|
||||
case 0:
|
||||
lastBit = "1";
|
||||
break;
|
||||
case 1:
|
||||
lastBit = "0";
|
||||
break;
|
||||
case 2:
|
||||
lastBit = "X";
|
||||
break;
|
||||
case 3:
|
||||
lastBit = "9";
|
||||
break;
|
||||
case 4:
|
||||
lastBit = "8";
|
||||
break;
|
||||
case 5:
|
||||
lastBit = "7";
|
||||
break;
|
||||
case 6:
|
||||
lastBit = "6";
|
||||
break;
|
||||
case 7:
|
||||
lastBit = "5";
|
||||
break;
|
||||
case 8:
|
||||
lastBit = "4";
|
||||
break;
|
||||
case 9:
|
||||
lastBit = "3";
|
||||
break;
|
||||
case 10:
|
||||
lastBit = "2";
|
||||
break;
|
||||
}
|
||||
return lastBit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从身份证号码中获取性别代码
|
||||
*
|
||||
* @param idNum 身份证号码
|
||||
* @return 性别代码 (1 男 2 女)
|
||||
*/
|
||||
public static String getGenderFromIdNum(String idNum) {
|
||||
// @todo 校验身份证号码
|
||||
String genderNumber = null;
|
||||
if (idNum.length() == 15) {
|
||||
genderNumber = idNum.substring(13, 14);
|
||||
} else if (idNum.length() == 18) {
|
||||
genderNumber = idNum.substring(16, 17);
|
||||
}
|
||||
return Integer.parseInt(genderNumber) % 2 > 0 ? "1" : "2";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得非null字符串。若str不等于null,则返回str,否则返回空串
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static String getNotnullStr(String str) {
|
||||
String newStr = "";
|
||||
if (str != null)
|
||||
newStr = str;
|
||||
return newStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取n位随机数
|
||||
*
|
||||
* @param num 位数
|
||||
* @return
|
||||
*/
|
||||
public static String getRandom(int num) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int[] random = new int[num];
|
||||
for (int i = 0; i < random.length; i++) {
|
||||
sb.append(random[i]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String parseMsgTemplate(String template, Map<String, Object> params) {
|
||||
if (template == null || template.length() == 0)
|
||||
throw new RuntimeException("解析字符串消息模板需要解析的模板为空");
|
||||
if (params == null)
|
||||
throw new RuntimeException("解析字符串消息模板需要解析的参数为空");
|
||||
String msg = template;
|
||||
for (String key : params.keySet()) {
|
||||
String value = "";
|
||||
Object valueOri = params.get(key);
|
||||
if (valueOri != null && valueOri instanceof Date)
|
||||
try {
|
||||
value = DateUtil.shortFmt(new Timestamp(((Date) valueOri).getTime()));
|
||||
} catch (Exception e) {
|
||||
value = DateUtil.shortFmt(((Date) valueOri));
|
||||
}
|
||||
else
|
||||
value = String.valueOf(valueOri);
|
||||
|
||||
msg = msg.replaceAll("\\$\\{" + key + "\\}", value);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成32位uuid
|
||||
*
|
||||
* @return 32位数uuid(去掉-分隔符)
|
||||
*/
|
||||
public static String getUUID32() {
|
||||
return UUID.randomUUID().toString().replace("-", "").toLowerCase();
|
||||
}
|
||||
|
||||
public static Boolean isNumber(String source) {
|
||||
if(StrUtil.isEmpty(source)){
|
||||
return false;
|
||||
}else{
|
||||
return source.matches("^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
package com.aisino.iles.common.util;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ValidateIdCardUtil {
|
||||
|
||||
|
||||
final static Map<Integer, String> zoneNum = new HashMap<Integer, String>();
|
||||
|
||||
static {
|
||||
zoneNum.put(11, "北京");
|
||||
zoneNum.put(12, "天津");
|
||||
zoneNum.put(13, "河北");
|
||||
zoneNum.put(14, "山西");
|
||||
zoneNum.put(15, "内蒙古");
|
||||
zoneNum.put(21, "辽宁");
|
||||
zoneNum.put(22, "吉林");
|
||||
zoneNum.put(23, "黑龙江");
|
||||
zoneNum.put(31, "上海");
|
||||
zoneNum.put(32, "江苏");
|
||||
zoneNum.put(33, "浙江");
|
||||
zoneNum.put(34, "安徽");
|
||||
zoneNum.put(35, "福建");
|
||||
zoneNum.put(36, "江西");
|
||||
zoneNum.put(37, "山东");
|
||||
zoneNum.put(41, "河南");
|
||||
zoneNum.put(42, "湖北");
|
||||
zoneNum.put(43, "湖南");
|
||||
zoneNum.put(44, "广东");
|
||||
zoneNum.put(45, "广西");
|
||||
zoneNum.put(46, "海南");
|
||||
zoneNum.put(50, "重庆");
|
||||
zoneNum.put(51, "四川");
|
||||
zoneNum.put(52, "贵州");
|
||||
zoneNum.put(53, "云南");
|
||||
zoneNum.put(54, "西藏");
|
||||
zoneNum.put(61, "陕西");
|
||||
zoneNum.put(62, "甘肃");
|
||||
zoneNum.put(63, "青海");
|
||||
zoneNum.put(64, "宁夏");
|
||||
zoneNum.put(65, "新疆");
|
||||
zoneNum.put(71, "台湾");
|
||||
zoneNum.put(81, "香港");
|
||||
zoneNum.put(82, "澳门");
|
||||
zoneNum.put(91, "外国");
|
||||
}
|
||||
|
||||
final static int[] PARITYBIT = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
|
||||
final static int[] POWER_LIST = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10,
|
||||
5, 8, 4, 2};
|
||||
|
||||
/**
|
||||
* 身份证验证
|
||||
*
|
||||
* @param certNo 号码内容
|
||||
* @return 是否有效 null和"" 都是false
|
||||
*/
|
||||
public static boolean isIDCard(String certNo) {
|
||||
if (certNo == null || (certNo.length() != 15 && certNo.length() != 18))
|
||||
return false;
|
||||
final char[] cs = certNo.toUpperCase().toCharArray();
|
||||
//校验位数
|
||||
int power = 0;
|
||||
for (int i = 0; i < cs.length; i++) {
|
||||
if (i == cs.length - 1 && cs[i] == 'X')
|
||||
break;//最后一位可以 是X或x
|
||||
if (cs[i] < '0' || cs[i] > '9')
|
||||
return false;
|
||||
if (i < cs.length - 1) {
|
||||
power += (cs[i] - '0') * POWER_LIST[i];
|
||||
}
|
||||
}
|
||||
|
||||
//校验区位码
|
||||
if (!zoneNum.containsKey(Integer.valueOf(certNo.substring(0, 2)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//校验年份
|
||||
String year = null;
|
||||
year = certNo.length() == 15 ? getIdcardCalendar(certNo) : certNo.substring(6, 10);
|
||||
|
||||
|
||||
final int iyear = Integer.parseInt(year);
|
||||
if (iyear < 1900 || iyear > Calendar.getInstance().get(Calendar.YEAR))
|
||||
return false;//1900年的PASS,超过今年的PASS
|
||||
|
||||
//校验月份
|
||||
String month = certNo.length() == 15 ? certNo.substring(8, 10) : certNo.substring(10, 12);
|
||||
final int imonth = Integer.parseInt(month);
|
||||
if (imonth < 1 || imonth > 12) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//校验天数
|
||||
String day = certNo.length() == 15 ? certNo.substring(10, 12) : certNo.substring(12, 14);
|
||||
final int iday = Integer.parseInt(day);
|
||||
if (iday < 1 || iday > 31)
|
||||
return false;
|
||||
|
||||
//校验"校验码"
|
||||
if (certNo.length() == 15)
|
||||
return true;
|
||||
return cs[cs.length - 1] == PARITYBIT[power % 11];
|
||||
}
|
||||
|
||||
private static String getIdcardCalendar(String certNo) {
|
||||
// 获取出生年月日
|
||||
String birthday = certNo.substring(6, 12);
|
||||
SimpleDateFormat ft = new SimpleDateFormat("yyMMdd");
|
||||
Date birthdate = null;
|
||||
try {
|
||||
birthdate = ft.parse(birthday);
|
||||
} catch (java.text.ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Calendar cday = Calendar.getInstance();
|
||||
cday.setTime(birthdate);
|
||||
String year = String.valueOf(cday.get(Calendar.YEAR));
|
||||
return year;
|
||||
}
|
||||
|
||||
// public static void main(String[] args) {
|
||||
// boolean istrue = isIDCard("330622810725323");
|
||||
// System.out.println(istrue);
|
||||
// }
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.smartlx.sso.client.util;
|
||||
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
public class RestTemplateUtil {
|
||||
public RestTemplateUtil() {
|
||||
}
|
||||
|
||||
public static <T> T get(String url, MultiValueMap<String, Object> params, Class<T> responseType) {
|
||||
RestTemplate restTemplate = getRestTemplate("UTF-8");
|
||||
ResponseEntity<T> responseEntity = restTemplate.exchange(url, HttpMethod.GET, new HttpEntity(params), responseType, new Object[0]);
|
||||
return (T)(responseEntity.getStatusCode() == HttpStatus.OK ? responseEntity.getBody() : null);
|
||||
}
|
||||
|
||||
public static <T> T post(String url, Object body, Map<String, String> headers, Class<T> responseType) {
|
||||
RestTemplate restTemplate = getRestTemplate("UTF-8");
|
||||
HttpHeaders httpHeaders = null;
|
||||
if (!CollectionUtils.isEmpty(headers)) {
|
||||
httpHeaders = new HttpHeaders();
|
||||
|
||||
for(String name : headers.keySet()) {
|
||||
httpHeaders.set(name, (String)headers.get(name));
|
||||
}
|
||||
}
|
||||
|
||||
ResponseEntity<T> responseEntity = restTemplate.exchange(url, HttpMethod.POST, new HttpEntity(body, httpHeaders), responseType, new Object[0]);
|
||||
return (T)(responseEntity.getStatusCode() == HttpStatus.OK ? responseEntity.getBody() : null);
|
||||
}
|
||||
|
||||
private static RestTemplate getRestTemplate(String charset) {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
for(HttpMessageConverter<?> httpMessageConverter : restTemplate.getMessageConverters()) {
|
||||
if (httpMessageConverter instanceof StringHttpMessageConverter) {
|
||||
((StringHttpMessageConverter)httpMessageConverter).setDefaultCharset(Charset.forName(charset));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return restTemplate;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue