diff --git a/yui-base-core/pom.xml b/yui-base-core/pom.xml new file mode 100644 index 0000000..4cbad9d --- /dev/null +++ b/yui-base-core/pom.xml @@ -0,0 +1,107 @@ + + + 4.0.0 + + com.yui + yui-springboot + 0.0.1-SNAPSHOT + + + yui-base-core + + + 15 + 15 + UTF-8 + + + + + + com.feiniaojin + graceful-response + + + + + cn.hutool + hutool-all + + + + + cn.dev33 + sa-token-spring-boot-starter + + + + + cn.dev33 + sa-token-redis-jackson + + + + + com.mysql + mysql-connector-j + runtime + + + + + com.mybatis-flex + mybatis-flex-spring-boot3-starter + + + com.mybatis-flex + mybatis-flex-processor + provided + + + + + com.github.pagehelper + pagehelper + + + + + io.springfox + springfox-swagger2 + + + io.springfox + springfox-swagger-ui + + + + + org.apache.commons + commons-compress + + + + + cn.afterturn + easypoi-base + + + cn.afterturn + easypoi-web + + + cn.afterturn + easypoi-annotation + + + + + com.alibaba + easyexcel + + + + + \ No newline at end of file diff --git a/yui-base-core/src/main/java/com/yui/base/controller/BaseController.java b/yui-base-core/src/main/java/com/yui/base/controller/BaseController.java new file mode 100644 index 0000000..7b49444 --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/base/controller/BaseController.java @@ -0,0 +1,33 @@ +package com.yui.base.controller; + +import cn.hutool.json.JSONUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; + + +/** + * 基础业务接口控制器 + * + * @author yui + */ +@Controller +public class BaseController { + + private static final Logger logger = LoggerFactory.getLogger(BaseController.class); + + @Value("${spring.profiles.active:develop}") + private String profileName; + + /** + * 把实体类转成JSON字符串 + * + * @param obj 实体类 + * @return {@link String} + */ + protected String toString(Object obj) { + return JSONUtil.toJsonStr(obj); + } + +} diff --git a/yui-base-core/src/main/java/com/yui/base/entity/BaseEntity.java b/yui-base-core/src/main/java/com/yui/base/entity/BaseEntity.java new file mode 100644 index 0000000..b7fe56d --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/base/entity/BaseEntity.java @@ -0,0 +1,68 @@ +package com.yui.base.entity; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.mybatisflex.annotation.Column; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 基础实体类 + * + * @author yui + */ +@Data +@ApiModel(value = "基础实体类") +public class BaseEntity implements Serializable { + + /** + * 当前页面数据量 + */ + @ApiModelProperty(value = "当前页面数据量") + @ExcelIgnore + @Column(ignore = true) + private int pageSize; + + /** + * 当前页码 + */ + @ApiModelProperty(value = "当前页码") + @ExcelIgnore + @Column(ignore = true) + private int pageNum; + + /** + * 排序字段 + */ + @ApiModelProperty(value = "排序字段") + @ExcelIgnore + @Column(ignore = true) + private String field = "id"; + + /** + * 排序规则,asc升序,desc降序 + */ + @ApiModelProperty(value = "排序规则") + @ExcelIgnore + @Column(ignore = true) + private String order = "desc"; + + /** + * 自定义排序字符串 + */ + @ApiModelProperty(value = "自定义排序字符串") + @ExcelIgnore + @Column(ignore = true) + private String columnsOrderStr; + + /** + * 快速搜索条件 + */ + @ApiModelProperty(value = "快速搜索条件") + @ExcelIgnore + @Column(ignore = true) + private String searchValue; + +} diff --git a/yui-base-core/src/main/java/com/yui/base/service/impl/BaseServiceImpl.java b/yui-base-core/src/main/java/com/yui/base/service/impl/BaseServiceImpl.java new file mode 100644 index 0000000..54c1fc0 --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/base/service/impl/BaseServiceImpl.java @@ -0,0 +1,13 @@ +package com.yui.base.service.impl; + +import com.mybatisflex.core.BaseMapper; +import com.mybatisflex.spring.service.impl.ServiceImpl; + +/** + * 基础业务服务实现类 + * + * @author yui + */ +public class BaseServiceImpl, T2> extends ServiceImpl { + +} diff --git a/yui-base-core/src/main/java/com/yui/common/session/entity/SessionUser.java b/yui-base-core/src/main/java/com/yui/common/session/entity/SessionUser.java new file mode 100644 index 0000000..a32fe60 --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/session/entity/SessionUser.java @@ -0,0 +1,45 @@ +package com.yui.common.session.entity; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 会话用户 + * + * @author yui + */ +@Data +public class SessionUser implements Serializable { + + /** + * 人员名称 + */ + private String personId; + private String userName; //登录名称 + private String password; //密码 + + private Long departmentId; //部门ID + private String departmentName; //部门名称 + private String positionName; //职位名称 + private String personName; //人员名称 + private String personSex; //人员性别 + private String personBirthday; //人员生日 + + // private String token; //token字符串 + + private List powerList; //权限标识 + + private List roleList; //角色标识或编码 + + private String lastLoginTime; //本次登录时间 + + private String UUID; //本次登录的uuid + + private String Url; //本次登录的url + + private String ip; //本次登录访问ip + + +} diff --git a/yui-base-core/src/main/java/com/yui/common/session/service/ISessionService.java b/yui-base-core/src/main/java/com/yui/common/session/service/ISessionService.java new file mode 100644 index 0000000..41a4871 --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/session/service/ISessionService.java @@ -0,0 +1,97 @@ +package com.yui.common.session.service; + +import cn.dev33.satoken.session.SaSession; +import com.yui.common.session.entity.SessionUser; + +/** + * Session 服务类 + * + * @author yui + */ +public interface ISessionService { + + /** + * 获取 userId + * + * @return {@link String} + */ + String getUserId(); + + /** + * 获取人员姓名 + * + * @return {@link String} + */ + String getPersonName(); + + /** + * 获取 session + * + * @return {@link SaSession} + */ + SaSession getSession(); + + /** + * 获取 token + * + * @return {@link String} + */ + String getToken(); + + /** + * 获取会话用户 + * + * @return {@link SessionUser} + */ + SessionUser getSessionUser(); + + /** + * 使用sa-token进行会话缓存用户信息 + * + * @param sessionUser 会话用户 + */ + void setSessionUser(SessionUser sessionUser); + + /** + * 设置 UUID + * + * @param uuid UUID + */ + void setUuid(String uuid); + + /** + * 获取 UUID + * + * @return {@link String} + */ + String getUuid(); + + /** + * 设置接口地址 + * + * @param url 网址 + */ + void setUrl(String url); + + /** + * 获取接口地址 + * + * @return {@link String} + */ + String getUrl(); + + /** + * 设置登录 IP + * + * @param ip ip + */ + void setIp(String ip); + + /** + * 获取登录 IP + * + * @return {@link String} + */ + String getIp(); + +} diff --git a/yui-base-core/src/main/java/com/yui/common/session/service/impl/SessionServiceImpl.java b/yui-base-core/src/main/java/com/yui/common/session/service/impl/SessionServiceImpl.java new file mode 100644 index 0000000..2c5586c --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/session/service/impl/SessionServiceImpl.java @@ -0,0 +1,159 @@ +package com.yui.common.session.service.impl; + +import cn.dev33.satoken.session.SaSession; +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.util.IdUtil; +import com.yui.common.session.entity.SessionUser; +import com.yui.common.session.service.ISessionService; +import org.springframework.stereotype.Service; + +/** + * 修改为 sa-token 会话方式 + * + * @author yui + */ +@Service +public class SessionServiceImpl implements ISessionService { + + public static final String SESSION_KEY = "$$sessionUserKey$$"; + + private SessionUser sessionUser; + + private String uuid; + + private String url; + + private String ipAddress; + + /** + * 获取sa-token会话中的用户对象信息 + * + * @return {@link SessionUser} + */ + private SessionUser getThisSessionUser() { + if (!StpUtil.isLogin()) { + return null; + } + Object obj = StpUtil.getSession().get(SESSION_KEY); + return obj != null ? (SessionUser) obj : null; + } + + /** + * 获取session对象 + * + * @return {@link SaSession} + */ + @Override + public SaSession getSession() { + return StpUtil.getSession(); + } + + /** + * 使用sa-token进行会话缓存用户信息 + * + * @param sessionUser 会话用户 + */ + @Override + public void setSessionUser(SessionUser sessionUser) { + if (sessionUser != null) { + // 不为空设置本次的UUID + sessionUser.setUUID(IdUtil.simpleUUID()); + } + // 放入sa-token会话中,线程安全放入 + StpUtil.getSession().set(SESSION_KEY, sessionUser); + + } + + @Override + public String getUserId() { + SessionUser thisSessionUser = getThisSessionUser(); + return thisSessionUser != null ? thisSessionUser.getPersonId() : null; + } + + @Override + public String getPersonName() { + SessionUser thisSessionUser = getThisSessionUser(); + if (thisSessionUser == null) { + return "游客"; + } + return thisSessionUser.getPersonName(); + } + + @Override + public String getToken() { + SessionUser thisSessionUser = getThisSessionUser(); + if (thisSessionUser == null) { + return null; + } + // 得到登录后的token + return StpUtil.getTokenInfo().getTokenValue(); + } + + @Override + public SessionUser getSessionUser() { + return getThisSessionUser(); + } + + @Override + public void setUuid(String uuid) { + SessionUser thisSessionUser = getThisSessionUser(); + if (thisSessionUser == null) { + this.uuid = uuid; + } else { + thisSessionUser.setUUID(uuid); + } + } + + /** + * 获取 UUID + * + * @return {@link String} + */ + @Override + public String getUuid() { + SessionUser thisSessionUser = getThisSessionUser(); + if (thisSessionUser == null) { + return this.uuid; + } + return thisSessionUser.getUUID(); + } + + @Override + public void setUrl(String url) { + this.url = url; + } + + /** + * 获取接口地址 + * + * @return {@link String} + */ + @Override + public String getUrl() { + SessionUser thisSessionUser = getThisSessionUser(); + if (thisSessionUser == null) { + return this.url; + } + return thisSessionUser.getUrl(); + } + + @Override + public void setIp(String ip) { + this.ipAddress = ip; + } + + /** + * 获取登录 IP + * + * @return {@link String} + */ + @Override + public String getIp() { + SessionUser thisSessionUser = getThisSessionUser(); + if (thisSessionUser == null) { + return this.ipAddress; + } + return thisSessionUser.getIp(); + } + +} diff --git a/yui-base-core/src/main/java/com/yui/common/util/excel/CsvExportUtil.java b/yui-base-core/src/main/java/com/yui/common/util/excel/CsvExportUtil.java new file mode 100644 index 0000000..e5c540b --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/excel/CsvExportUtil.java @@ -0,0 +1,116 @@ +package com.yui.common.util.excel; + +import cn.afterturn.easypoi.csv.entity.CsvExportParams; +import cn.afterturn.easypoi.csv.export.CsvExportService; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.afterturn.easypoi.handler.inter.IExcelExportServer; +import cn.afterturn.easypoi.handler.inter.IWriter; +import cn.hutool.core.collection.CollectionUtil; +import com.yui.common.util.tools.StrUtil; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * csv 导出工具类 + * + * @author yui + */ +public class CsvExportUtil { + + private CsvExportUtil() { + } + + + /** + * export csv all list + * + * @param params + * @param pojoClass + * @param dataSet + * @param outputStream + */ + public static void exportCsv(CsvExportParams params, Class pojoClass, Collection dataSet, OutputStream outputStream) throws IllegalAccessException { + IWriter writer = new CsvExportService(outputStream, params, pojoClass); + writer.write(processValueForCsv(dataSet)); + writer.close(); + } + + /** + * 导出csv文件 + * + * @param params + * @param pojoClass + * @param dataSet + * @param response + */ + public static void exportCsvUtf8(CsvExportParams params, Class pojoClass, Collection dataSet, HttpServletResponse response) throws IOException, IllegalAccessException { + // 中文乱码:以CSV方式导出的文件中默认不含BOM信息,通过给将要输出的内容设置BOM标识(以 EF BB BF 开头的字节流)即可解决该问题 + OutputStream outputStream = response.getOutputStream(); + outputStream.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}); + exportCsv(params, pojoClass, dataSet, outputStream); + } + + /** + * export csv use server + * + * @param params + * @param pojoClass + * @param server + * @param queryParams + * @param outputStream + */ + public static void exportCsv(CsvExportParams params, Class pojoClass, IExcelExportServer server, + Object queryParams, OutputStream outputStream) { + IWriter writer = new CsvExportService(outputStream, params, pojoClass); + int page = 1; + List dataSet; + while ((dataSet = server.selectListForExcelExport(queryParams, page)) != null && !dataSet.isEmpty()) { + page++; + writer.write(dataSet); + } + writer.close(); + } + + /** + * @param params 表格标题属性 + * @param pojoClass Excel对象Class + */ + public static IWriter exportCsv(CsvExportParams params, Class pojoClass, OutputStream outputStream) { + return new CsvExportService(outputStream, params, pojoClass); + } + + /** + * 根据Map创建对应的Excel + * + * @param params 表格标题属性 + * @param entityList Map对象列表 + */ + public static IWriter exportCsv(CsvExportParams params, List entityList, OutputStream outputStream) { + return new CsvExportService(outputStream, params, entityList); + } + + public static Collection processValueForCsv(Collection dataSet) throws IllegalAccessException { + if (CollectionUtil.isEmpty(dataSet)) { + return new ArrayList<>(); + } + for (Object obj : dataSet) { + for (Field field : obj.getClass().getDeclaredFields()) { + field.setAccessible(true); + if (field.get(obj) instanceof String) { + String value = field.get(obj) == null ? "" : field.get(obj).toString(); + if (value.contains(StrUtil.DOUBLE_QUOTATION_MARKS)) { + value = value.replaceAll(StrUtil.DOUBLE_QUOTATION_MARKS, "\"\""); + } + field.set(obj, value); + } + } + } + return dataSet; + } +} diff --git a/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelExportStatisticStyler.java b/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelExportStatisticStyler.java new file mode 100644 index 0000000..619ccd3 --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelExportStatisticStyler.java @@ -0,0 +1,40 @@ +package com.yui.common.util.excel; + +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.afterturn.easypoi.excel.export.styler.ExcelExportStylerDefaultImpl; +import org.apache.poi.ss.usermodel.*; + +/** + * 处理EasyPoi导出数据类型转数值问题 + * + * @author yui + */ +public class ExcelExportStatisticStyler extends ExcelExportStylerDefaultImpl { + + private CellStyle numberCellStyle; + + public ExcelExportStatisticStyler(Workbook workbook) { + super(workbook); + createNumberCellStyler(); + } + + private void createNumberCellStyler() { + numberCellStyle = workbook.createCellStyle(); + numberCellStyle.setAlignment(HorizontalAlignment.CENTER); + numberCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); + // 这边传的参数格式会影响你导出的数据类型 + // "0.00"是自定义类型可以与数字类型直接进行计算,并不是直接导出数字类型 + // "#,##0.00" 是货币类型 其他参数到源码查看 + numberCellStyle.setDataFormat((short) BuiltinFormats.getBuiltinFormat("0.00")); + numberCellStyle.setWrapText(true); + } + + @Override + public CellStyle getStyles(boolean noneStyler, ExcelExportEntity entity) { + // 自定义Dict转换 + if (entity != null && 10 == entity.getType()) { + return numberCellStyle; + } + return super.getStyles(noneStyler, entity); + } +} diff --git a/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelReader.java b/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelReader.java new file mode 100644 index 0000000..2d38b1b --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelReader.java @@ -0,0 +1,155 @@ +package com.yui.common.util.excel; + +import cn.hutool.core.date.DateUtil; +import com.yui.common.util.tools.StrUtil; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.text.NumberFormat; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Excel 阅读器 + * + * @author lipx + */ +@Component +public class ExcelReader { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExcelReader.class); + private static ExcelReader excelReader; + + private Workbook workBook; + private Map sheetHeaders; + + public ExcelReader() { + super(); + } + + public ExcelReader(String filePath) { + this(new File(filePath)); + } + + public ExcelReader(File file) { + // 解决版本问题,HSSFWorkbook是97-03版本的xls版本,XSSFWorkbook是07版本的xlsx + try { + workBook = new XSSFWorkbook(new FileInputStream(file)); + } catch (Exception e) { + try { + workBook = new HSSFWorkbook(new FileInputStream(file)); + } catch (Exception e1) { + LOGGER.error("Excel格式不正确", e1); + throw new RuntimeException(e1); + } + } + } + + public ExcelReader(InputStream inputStream) { + // 解决版本问题,HSSFWorkbook是97-03版本的xls版本,XSSFWorkbook是07版本的xlsx + try { + workBook = new XSSFWorkbook(inputStream); + } catch (Exception e) { + try { + workBook = new HSSFWorkbook(inputStream); + } catch (Exception e1) { + LOGGER.error("Excel格式不正确", e1); + throw new RuntimeException(e1); + } + } + } + + @PostConstruct + private void init() { + excelReader = this; + excelReader.projectBasicService = this.projectBasicService; + excelReader.personBasicService = this.personBasicService; + } + + /** + * 初始化sheet和表头信息,默认以每个sheet的第一行作为表头 + */ + private void initDefaultSheetHeaders() { + sheetHeaders = new LinkedHashMap<>(); + + if (workBook == null) { + return; + } + + int numberOfSheets = workBook.getNumberOfSheets(); + for (int i = 0; i < numberOfSheets; i++) { + Sheet sheet = workBook.getSheetAt(i); + String sheetName = workBook.getSheetName(i); + LOGGER.debug("sheetName[{}]: {}", i, sheetName); + + // 默认以第一行作为表头 + Row row = sheet.getRow(0); + if (row == null) { + sheetHeaders.put(sheet, new String[]{}); + continue; + } + + String[] headers = new String[]{}; + short lastCellNum = row.getLastCellNum(); + for (int j = 0; j < lastCellNum; j++) { + String cellValue = getCellValue(row.getCell(j)); + headers = ArrayUtils.add(headers, cellValue); + } + sheetHeaders.put(sheet, headers); + } + } + + + /** + * 读取cell的值 + * + * @param cell 需要读取的cell + * @param defaultValue 默认值 + * @return + */ + private String getCellValue(Cell cell) { + if (cell == null) { + return ""; + } + cell.setCellType(CellType.STRING); + return StrUtil.emptyToDefault(cell.getStringCellValue(), ""); + } + + /** + * POI3.15之后的读取方法(建议用这个) + * + * @param cell + * @return + */ + private String getCellStringValue(Cell cell) { + if (cell == null) { + return ""; + } + String cellValue = null; + switch (cell.getCellType()) { + case NUMERIC -> { + if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) { + cellValue = DateUtil.format(cell.getDateCellValue(), DateUtil); + } else { + NumberFormat nf = NumberFormat.getInstance(); + cellValue = String.valueOf(nf.format(cell.getNumericCellValue())).replace(",", ""); + } + } + case STRING -> cellValue = cell.getStringCellValue(); + case BOOLEAN -> cellValue = String.valueOf(cell.getBooleanCellValue()); + case ERROR -> cellValue = "错误类型"; + default -> cellValue = ""; + } + return cellValue.trim(); + } + +} diff --git a/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelSelectListUtil.java b/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelSelectListUtil.java new file mode 100644 index 0000000..efb09ca --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelSelectListUtil.java @@ -0,0 +1,26 @@ +package com.yui.common.util.excel; + +import org.apache.poi.hssf.usermodel.DVConstraint; +import org.apache.poi.hssf.usermodel.HSSFDataValidation; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellRangeAddressList; + +/** + * Excel 选择列表实用程序 + * + * @author lkc + */ +public class ExcelSelectListUtil { + public static void selectList(Workbook workbook, int firstCol, int lastCol, String[] strings) { + Sheet sheet = workbook.getSheetAt(0); + // 生成下拉列表 + // 只对(x,x)单元格有效 + CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(3, 65535, firstCol, lastCol); + // 生成下拉框内容 + DVConstraint dvConstraint = DVConstraint.createExplicitListConstraint(strings); + HSSFDataValidation dataValidation = new HSSFDataValidation(cellRangeAddressList, dvConstraint); + // 对sheet页生效 + sheet.addValidationData(dataValidation); + } +} diff --git a/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelTool.java b/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelTool.java new file mode 100644 index 0000000..4e98ea6 --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelTool.java @@ -0,0 +1,89 @@ +package com.yui.common.util.excel; + +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; + +/** + * 解析Excel文件单元格内容 + * + * @author yui + */ +public class ExcelTool { + public static final String EMPTY = ""; + private static final String POINT = "."; + + /** + * 获得path的后缀名 + * + * @param path 文件路径 + * @return {@link String} 路径的后缀名 + */ + public static String getPostfix(String path) { + if (path == null || EMPTY.equals(path.trim())) { + return EMPTY; + } + if (path.contains(POINT)) { + return path.substring(path.lastIndexOf(POINT) + 1); + } + return EMPTY; + } + + /** + * 解析xls和xlsx不兼容问题 + * + * @param filePath 文件路径 + * @return {@link Workbook} + * @throws IOException io异常 + */ + public static Workbook getWorkBook(String filePath) throws IOException { + Workbook workbook; + String suffix = filePath.substring(filePath.lastIndexOf(".")); + File file = new File(filePath); + if (suffix.equals(ExcelUtils.XLS)) { + POIFSFileSystem pfs = new POIFSFileSystem(file); + workbook = new HSSFWorkbook(pfs); + return workbook; + } else if (suffix.equals(ExcelUtils.XLSX)) { + try { + workbook = new XSSFWorkbook(file); + return workbook; + } catch (IOException | InvalidFormatException e) { + return null; + } + } else { + return null; + } + } + + /** + * 解析xls和xlsx不兼容问题 + * + * @param file + * @return + */ + public static Workbook getWorkBook(MultipartFile file) throws IOException { + Workbook workbook = null; + String filename = file.getOriginalFilename(); + if (filename.endsWith("xls")) { + POIFSFileSystem pfs = new POIFSFileSystem(file.getInputStream()); + workbook = new HSSFWorkbook(pfs); + return workbook; + } else if (filename.endsWith("xlsx")) { + try { + workbook = new XSSFWorkbook(file.getInputStream()); + return workbook; + } catch (IOException e) { + return null; + } + } else { + return null; + } + } +} diff --git a/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelUtils.java b/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelUtils.java new file mode 100644 index 0000000..eb2af41 --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelUtils.java @@ -0,0 +1,253 @@ +package com.yui.common.util.excel; + +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.ExcelImportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.ImportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.afterturn.easypoi.excel.export.ExcelExportService; +import cn.hutool.core.util.StrUtil; +import cn.hutool.poi.excel.ExcelUtil; +import com.yui.common.util.tools.ObjectUtil; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * Excel 实用程序 + * + * @author yui + */ +public class ExcelUtils { + + public static final String XLS = "xls"; + + public static final String XLSX = "xlsx"; + + /** + * 得到Workbook对象 + * + * @param file 文件 + * @return {@link Workbook} + * @throws IOException io异常 + */ + public static Workbook getWorkBook(MultipartFile file) throws IOException { + // 这样写 excel 能兼容03和07 + InputStream is = file.getInputStream(); + Workbook hssfWorkbook; + try { + hssfWorkbook = new HSSFWorkbook(is); + } catch (Exception ex) { + is = file.getInputStream(); + hssfWorkbook = new XSSFWorkbook(is); + } + return hssfWorkbook; + } + + public static void exportExcel(List list, String title, String sheetName, Class pojoClass, String fileName, + boolean isCreateHeader, HttpServletResponse response) { + ExportParams exportParams = new ExportParams(title, sheetName); + exportParams.setCreateHeadRows(isCreateHeader); + defaultExport(list, pojoClass, fileName, response, exportParams); + } + + public static void exportExcel(List list, String title, String sheetName, Class pojoClass, String fileName, + HttpServletResponse response) { + defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName)); + } + + public static void exportExcel(List list, ExportParams exportParams, Class pojoClass, String fileName, + HttpServletResponse response) { + defaultExport(list, pojoClass, fileName, response, exportParams); + } + + /** + * 导出map + */ + public static void exportExcelForMap(List list, String title, String sheetName, String fileName, + List beanList, HttpServletResponse response) { + Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(title, sheetName), beanList, list); + if (workbook != null) { + downLoadExcel(fileName, response, workbook); + } + } + + public static void exportExcelSelect(List list, String title, String sheetName, Class pojoClass, + String fileName, HttpServletResponse response, + List> selectList) { + selectExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName), selectList); + } + + private static void selectExport(List list, Class pojoClass, String fileName, HttpServletResponse response, + ExportParams exportParams, List> selectList) { + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list); + for (Map map : selectList) { + ExcelSelectListUtil.selectList(workbook, Integer.parseInt(map.get("x").toString()), + Integer.parseInt(map.get("y").toString()), (String[]) map.get("select")); + } + if (workbook != null) { + downLoadExcel(fileName, response, workbook); + } + } + + public static void exportExcel(List> list, String fileName, HttpServletResponse response) { + defaultExport(list, fileName, response); + } + + private static void defaultExport(List list, Class pojoClass, String fileName, + HttpServletResponse response, ExportParams exportParams) { + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list); + if (workbook != null) { + downLoadExcel(fileName, response, workbook); + } + } + + public static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) { + try { + response.setCharacterEncoding("UTF-8"); + response.setHeader("content-Type", "application/vnd.ms-excel;charset=GBK"); + String contentDisposition = "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8); + response.setHeader("Content-Disposition", contentDisposition); + workbook.write(response.getOutputStream()); + } catch (IOException e) { + // throw new NormalException(e.getMessage()); + } + } + + public static void downLoadExcelXlsx(String fileName, HttpServletResponse response, Workbook workbook) { + try { + response.setCharacterEncoding("UTF-8"); + response.setHeader("content-Type", ExcelUtil.XLSX_CONTENT_TYPE); + String contentDisposition = "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8); + response.setHeader("Content-Disposition", contentDisposition); + workbook.write(response.getOutputStream()); + } catch (IOException e) { + // throw new NormalException(e.getMessage()); + } + } + + private static void defaultExport(List> list, String fileName, HttpServletResponse response) { + Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF); + if (workbook != null) { + downLoadExcel(fileName, response, workbook); + } + } + + public static List importExcel(String filePath, Integer titleRows, Integer headerRows, Class pojoClass) { + if (StringUtils.isBlank(filePath)) { + return null; + } + ImportParams params = new ImportParams(); + params.setTitleRows(titleRows); + params.setHeadRows(headerRows); + List list = null; + try { + list = ExcelImportUtil.importExcel(new File(filePath), pojoClass, params); + } catch (NoSuchElementException e) { + // throw new NormalException("模板不能为空"); + } catch (Exception e) { + // throw new NormalException(e.getMessage()); + } + return list; + } + + public static List importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class pojoClass) { + if (file == null) { + return null; + } + ImportParams params = new ImportParams(); + params.setTitleRows(titleRows); + params.setHeadRows(headerRows); + List list = null; + try { + list = ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params); + } catch (Exception ignored) { + } + return list; + } + + public static void writeCellValue(Sheet sheet, int rowIndex, int colIndex, int valueType, CellStyle cellStyle, Object value) { + if (sheet == null) { + return; + } + Row row = sheet.getRow(rowIndex); + if (row == null) { + row = sheet.createRow(rowIndex); + } + Cell cell = row.getCell(colIndex); + if (cell == null) { + cell = row.createCell(colIndex); + DataFormat format = sheet.getWorkbook().createDataFormat(); + cellStyle.setDataFormat(format.getFormat("@")); + cell.setCellStyle(cellStyle); + cell.setCellValue(""); + } + writeCellValue(cell, valueType, cellStyle, value); + } + + + public static void writeCellValue(Cell cell, int valueType, CellStyle cellStyle, Object value) { + if (cell == null || value == null) { + return; + } + switch (valueType) { + case 3, 5, 1 -> cell.setCellValue(value.toString()); + case 4 -> cell.setCellValue(Boolean.parseBoolean(value.toString())); + case 0 -> cell.setCellValue(Double.parseDouble(value.toString())); + case 2 -> cell.setCellFormula(value.toString()); + default -> { + } + } + if (cellStyle != null) { + cell.setCellStyle(cellStyle); + } + } + + /*** + * 获取当前SHEET 所有Cell集合 + * + * @param sheet 表 + * @return {@link List}<{@link Cell}> + */ + public static List getAllCell(Sheet sheet) { + List list = new ArrayList<>(); + int maxRow = sheet.getLastRowNum(); + int maxRol; + for (int row = 0; row <= maxRow; row++) { + Row rowObj = sheet.getRow(row); + if (null == rowObj) { + continue; + } + maxRol = rowObj.getLastCellNum(); + for (int rol = 0; rol < maxRol; rol++) { + Cell cell = rowObj.getCell(rol); + if (null == cell || StrUtil.isNotBlank(cell.toString())) { + continue; + } + list.add(cell); + } + } + return list; + } + + public static void exportExcelForMutiSheet(List> list, String fileName, HttpServletResponse response) { + Workbook workbook = new HSSFWorkbook(); + for (Map map : list) { + new ExcelExportService().createSheetForMap(workbook, (ExportParams) map.get("title"), + ObjectUtil.toList(map.get("entity"), ExcelExportEntity.class), + (Collection) map.get("data")); + } + downLoadExcel(fileName, response, workbook); + } +} \ No newline at end of file diff --git a/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelVerifyInfo.java b/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelVerifyInfo.java new file mode 100644 index 0000000..e725f03 --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/excel/ExcelVerifyInfo.java @@ -0,0 +1,45 @@ +package com.yui.common.util.excel; + +import cn.afterturn.easypoi.handler.inter.IExcelDataModel; +import cn.afterturn.easypoi.handler.inter.IExcelModel; +import lombok.Data; + +/** + * easypoi 获取行号和错误信息 + * + * @author yui + */ +@Data +public class ExcelVerifyInfo implements IExcelModel, IExcelDataModel { + + /** + * 错误消息 + */ + private String errorMsg; + + /** + * 行数 + */ + private int rowNum; + + @Override + public Integer getRowNum() { + return rowNum; + } + + @Override + public void setRowNum(Integer rowNum) { + this.rowNum = rowNum; + } + + @Override + public String getErrorMsg() { + return errorMsg; + } + + @Override + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + +} diff --git a/yui-base-core/src/main/java/com/yui/common/util/exception/ProjectException.java b/yui-base-core/src/main/java/com/yui/common/util/exception/ProjectException.java new file mode 100644 index 0000000..9ceb5cb --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/exception/ProjectException.java @@ -0,0 +1,30 @@ +package com.yui.common.util.exception; + +/** + * 项目自定义异常 + * + * @author yui + */ +public class ProjectException extends RuntimeException { + + public ProjectException() { + super(); + } + + public ProjectException(Exception e) { + super(e); + } + + public ProjectException(String message) { + super(message); + } + + public ProjectException(String message, Throwable cause) { + super(message, cause); + } + + public ProjectException(Throwable cause) { + super(cause); + } + +} diff --git a/yui-base-core/src/main/java/com/yui/common/util/tools/BeanCopy.java b/yui-base-core/src/main/java/com/yui/common/util/tools/BeanCopy.java new file mode 100644 index 0000000..a8a6f50 --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/tools/BeanCopy.java @@ -0,0 +1,87 @@ +package com.yui.common.util.tools; + +import cn.hutool.core.bean.BeanUtil; +import com.yui.common.util.exception.ProjectException; + +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + + +/** + * Bean 副本 + * + * @author yui + */ +public class BeanCopy { + + /** + * 复制类 + * + * @param dataList 数据来源 + * @param clazz Class + * @return {@link List}<{@link S}> + */ + public List copyList(List dataList, Class clazz) { + List returnList = new ArrayList<>(); + T from; + S to; + try { + for (T t : dataList) { + from = t; + to = clazz.getDeclaredConstructor().newInstance(); + BeanUtil.copyProperties(to, from); + returnList.add(to); + } + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | + NoSuchMethodException e) { + throw new ProjectException("复制类异常", e); + } + return returnList; + } + + /** + * List深度copy + * + * @param src 要拷贝的list + * @param clazz Class + * @return {@link List}<{@link T}> + * @throws IOException io异常 + * @throws ClassNotFoundException 未找到类异常 + */ + public static List deepCopy(List src, Class clazz) throws IOException, ClassNotFoundException { + try (ObjectInputStream in = getInputStream(src)) { + return ObjectUtil.toList(in.readObject(), clazz); + } + } + + /** + * Map深度copy + * + * @param src 要拷贝的Map + * @param kClass Class + * @param vClass Class + * @return {@link Map}<{@link K}, {@link V}> + * @throws IOException io异常 + * @throws ClassNotFoundException 未找到类异常 + */ + public static Map cloneMap(Map src, Class kClass, Class vClass) + throws IOException, ClassNotFoundException { + try (ObjectInputStream ois = getInputStream(src)) { + return ObjectUtil.toMap(ois.readObject(), kClass, vClass); + } + } + + private static ObjectInputStream getInputStream(Object src) throws IOException { + // 写入字节流 + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ObjectOutputStream obs = new ObjectOutputStream(out); + obs.writeObject(src); + obs.close(); + // 分配内存,写入原始对象,生成新对象 + ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray()); + return new ObjectInputStream(ios); + } +} diff --git a/yui-base-core/src/main/java/com/yui/common/util/tools/ObjectUtil.java b/yui-base-core/src/main/java/com/yui/common/util/tools/ObjectUtil.java new file mode 100644 index 0000000..76e9643 --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/tools/ObjectUtil.java @@ -0,0 +1,48 @@ +package com.yui.common.util.tools; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Object工具类 + * + * @author yui + */ +public class ObjectUtil { + + /** + * 将object转为list + * + * @param obj object + * @param clazz Class + * @return {@link List}<{@link T}> + */ + public static List toList(Object obj, Class clazz) { + List result; + if (obj instanceof Collection) { + return ((Collection) obj).stream().map(clazz::cast).collect(Collectors.toList()); + } + return null; + } + + /** + * 将object转为map + * + * @param obj object + * @param kClass Class + * @param vClass Class + * @return {@link Map}<{@link K}, {@link V}> + */ + public static Map toMap(Object obj, Class kClass, Class vClass) { + Map result = new HashMap<>(5); + if (obj instanceof Map) { + Set> entries = ((Map) obj).entrySet(); + for (Map.Entry entry : entries) { + result.put(kClass.cast(entry.getKey()), vClass.cast(entry.getValue())); + } + return result; + } + return null; + } + +} diff --git a/yui-base-core/src/main/java/com/yui/common/util/tools/SqlUtil.java b/yui-base-core/src/main/java/com/yui/common/util/tools/SqlUtil.java new file mode 100644 index 0000000..19aac92 --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/tools/SqlUtil.java @@ -0,0 +1,33 @@ +package com.yui.common.util.tools; + +import cn.hutool.core.util.StrUtil; +import com.yui.common.util.exception.ProjectException; + +/** + * sql操作工具类 + * + * @author yui + */ +public class SqlUtil { + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + private static final String SQL_PATTERN = "[a-zA-Z0-9_ ,.]+"; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) { + if (StrUtil.isNotEmpty(value) && !isValidOrderBySql(value)) { + throw new ProjectException("参数不符合规范,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) { + return value.matches(SQL_PATTERN); + } +} diff --git a/yui-base-core/src/main/java/com/yui/common/util/tools/StrUtil.java b/yui-base-core/src/main/java/com/yui/common/util/tools/StrUtil.java new file mode 100644 index 0000000..a6f1f68 --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/tools/StrUtil.java @@ -0,0 +1,14 @@ +package com.yui.common.util.tools; + +/** + * 字符串工具类 util + * + * @author yui + */ +public class StrUtil extends cn.hutool.core.util.StrUtil { + + public static final String DOUBLE_QUOTATION_MARKS = "\""; + + public static final String SINGLE_QUOTATION_MARKS = "'"; + +} diff --git a/yui-base-core/src/main/java/com/yui/common/util/tools/SummaryDataUtil.java b/yui-base-core/src/main/java/com/yui/common/util/tools/SummaryDataUtil.java new file mode 100644 index 0000000..3cdb8ba --- /dev/null +++ b/yui-base-core/src/main/java/com/yui/common/util/tools/SummaryDataUtil.java @@ -0,0 +1,55 @@ +package com.yui.common.util.tools; + +import com.mybatisflex.core.paginate.Page; + +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 汇总数据工具类 + * + * @author yui + */ +public class SummaryDataUtil { + + public static SummaryDataRecord getSummaryDataRecord(Page page) { + List records = page.getRecords(); + Map summaryDataMap = null; + try { + summaryDataMap = getSummaryDataMap(records); + } catch (IllegalAccessException ignored) { + + } + return new SummaryDataRecord<>(page.getTotalRow(), summaryDataMap, records); + } + + private static Map getSummaryDataMap(List list) throws IllegalAccessException { + Map summaryData = new HashMap<>(list.get(0).getClass().getDeclaredFields().length); + for (T item : list) { + Field[] fields = item.getClass().getDeclaredFields(); + for (Field field : fields) { + field.setAccessible(true); + if (field.get(item) instanceof BigDecimal) { + BigDecimal a = Optional.ofNullable(summaryData.get(field.getName())).orElse(BigDecimal.ZERO); + BigDecimal b = Optional.ofNullable((BigDecimal) field.get(item)).orElse(BigDecimal.ZERO); + a = a.add(b); + summaryData.put(field.getName(), a); + } + field.setAccessible(false); + } + } + return summaryData; + } + + /** + * 汇总数据记录 + * + * @author yui + */ + public record SummaryDataRecord(Long total, Map summaryData, List data) {} + +}