This commit is contained in:
@@ -0,0 +1,565 @@
|
||||
package jnpf.util;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 常量工具类
|
||||
*
|
||||
* @author yanwenfu
|
||||
* @create 2023-07-21
|
||||
*/
|
||||
public class ConstantUtil {
|
||||
|
||||
/**
|
||||
* 配置key - 空间容量
|
||||
*/
|
||||
public static final String CONFIG_SPACE = "totalSpace";
|
||||
|
||||
/**
|
||||
* 配置key - 垃圾过期
|
||||
*/
|
||||
public static final String CONFIG_RUBBISH_EXPIRE = "informationRecycleBin";
|
||||
|
||||
/**
|
||||
* 配置key - 数据报表
|
||||
*/
|
||||
public static final String CONFIG_DATA_REPORT = "dataReport";
|
||||
|
||||
|
||||
/**
|
||||
* 文件 - 空间
|
||||
*/
|
||||
public static final int FILE_SPACE = 1;
|
||||
|
||||
/**
|
||||
* 文件 - 文件夹
|
||||
*/
|
||||
public static final int FILE_FOLDER = 2;
|
||||
|
||||
/**
|
||||
* 离组
|
||||
*/
|
||||
public static final int NUM_WITHDRAW = 3;
|
||||
/**
|
||||
* 是
|
||||
*/
|
||||
public static final int NUM_TRUE = 1;
|
||||
|
||||
/**
|
||||
* 否
|
||||
*/
|
||||
public static final int NUM_FALSE = 0;
|
||||
|
||||
/**
|
||||
* 结束
|
||||
*/
|
||||
public static final int NUM_FINISH = 2;
|
||||
|
||||
/**
|
||||
* 是
|
||||
*/
|
||||
public static final String STR_TRUE = "1";
|
||||
|
||||
/**
|
||||
* 否
|
||||
*/
|
||||
public static final String STR_FALSE = "0";
|
||||
|
||||
/**
|
||||
* 文件类型 - 文档
|
||||
*/
|
||||
public static final String FILE_TYPE_DOC = "文档";
|
||||
/**
|
||||
* 文件类型 - 表格
|
||||
*/
|
||||
public static final String FILE_TYPE_XLS = "表格";
|
||||
/**
|
||||
* 文件类型 - PPT
|
||||
*/
|
||||
public static final String FILE_TYPE_PPT = "PPT";
|
||||
|
||||
/**
|
||||
* 上传路径
|
||||
*/
|
||||
public static final String UPLOAD_FOLDER = "UserDocument/%s/";
|
||||
|
||||
// 单位
|
||||
public static final String UNIT_B = "B";
|
||||
public static final String UNIT_KB = "KB";
|
||||
public static final String UNIT_MB = "MB";
|
||||
public static final String UNIT_G = "G";
|
||||
|
||||
/**
|
||||
* 类型 - 转发
|
||||
*/
|
||||
public static final int ASSOCIATION_FORWARD = 3;
|
||||
/**
|
||||
* 类型 - 点赞
|
||||
*/
|
||||
public static final int ASSOCIATION_LIKE = 4;
|
||||
|
||||
/**
|
||||
* 点
|
||||
*/
|
||||
public static final String POINT_SYMBOL = ".";
|
||||
public static final String LOG_DATA_FORM = "日志统计";
|
||||
public static final String CULTURE_CLOCK_IN_FORM = "文化打卡统计";
|
||||
/**
|
||||
* 计算方式 - 加
|
||||
*/
|
||||
public static final String CAL_ADD = "add";
|
||||
/**
|
||||
* 计算方式 - 减
|
||||
*/
|
||||
public static final String CAL_REDUCE = "reduce";
|
||||
/**
|
||||
* 计算方式 - 删
|
||||
*/
|
||||
public static final String CAL_DELETE = "delete";
|
||||
|
||||
/**
|
||||
* 上班
|
||||
*/
|
||||
public static final String ON_DUTY = "onDuty";
|
||||
/**
|
||||
* 下班
|
||||
*/
|
||||
public static final String OFF_DUTY = "offDuty";
|
||||
/**
|
||||
* 打卡类型 - 普通打卡
|
||||
*/
|
||||
public static final int CLOCK_TYPE_NORMAL = 1;
|
||||
/**
|
||||
* 打卡类型 - 外勤打卡
|
||||
*/
|
||||
public static final int CLOCK_TYPE_OUTSIDE = 2;
|
||||
/**
|
||||
* 打卡类型 - 补卡
|
||||
*/
|
||||
public static final int CLOCK_TYPE_SUPPLY = 3;
|
||||
/**
|
||||
* 设备类型 - 地点
|
||||
*/
|
||||
public static final int DEVICE_PLACE = 1;
|
||||
/**
|
||||
* 设备类型 - WIFI
|
||||
*/
|
||||
public static final int DEVICE_WIFI = 2;
|
||||
/**
|
||||
* 设备类型 - 考勤机
|
||||
*/
|
||||
public static final int DEVICE_MACHINE = 3;
|
||||
/**
|
||||
* 设备类型 - 未知
|
||||
*/
|
||||
public static final int DEVICE_UNKNOWN = 99;
|
||||
/**
|
||||
* 状态 - 关闭
|
||||
*/
|
||||
public static final int NUM_CLOSE = -1;
|
||||
/**
|
||||
* 早到早走晚到晚走
|
||||
*/
|
||||
public static final int MODE_ECEG = 1;
|
||||
/**
|
||||
* 晚到早走若干分钟
|
||||
*/
|
||||
public static final int MODE_LCEG = 2;
|
||||
/**
|
||||
* 仅第一次上班、最后一次下班生效
|
||||
*/
|
||||
public static final int EFFECT_FIRST_LAST = 1;
|
||||
/**
|
||||
* 每次上班下班都生效
|
||||
*/
|
||||
public static final int EFFECT_EVERY_TIME = 2;
|
||||
/**
|
||||
* 考勤
|
||||
*/
|
||||
public static final int WORK_ATTENDANCE = 1;
|
||||
/**
|
||||
* 加班
|
||||
*/
|
||||
public static final int WORK_OVERTIME = 2;
|
||||
/**
|
||||
* 请假
|
||||
*/
|
||||
public static final int WORK_REST = 3;
|
||||
/**
|
||||
* 旷工打卡redis key
|
||||
*/
|
||||
public static final String CLOCK_REDIS_KEY = "CLOCK_ABSENCE:";
|
||||
/**
|
||||
* 是否通过_是
|
||||
*/
|
||||
public static final int PASS_TRUE = 0;
|
||||
/**
|
||||
* 是否通过_审批中
|
||||
*/
|
||||
public static final int PASS_APPROVAL = 1;
|
||||
/**
|
||||
* 是否通过_否
|
||||
*/
|
||||
public static final int PASS_FALSE = 2;
|
||||
/**
|
||||
* 租户设备关联
|
||||
*/
|
||||
public static final String DEVICE_TENANT = "DEVICE_TENANT";
|
||||
/**
|
||||
* 考勤机设备
|
||||
*/
|
||||
public static final String ATTENDANCE_DEVICE = "Attendance_Device:";
|
||||
|
||||
/**
|
||||
* 上班
|
||||
*/
|
||||
public static final Integer ON_WORK = 1;
|
||||
/**
|
||||
* 下班
|
||||
*/
|
||||
public static final Integer OFF_WORK = 2;
|
||||
/**
|
||||
* 普通打卡
|
||||
*/
|
||||
public static final int KIND_NORMAL = 1;
|
||||
/**
|
||||
* 外勤打卡
|
||||
*/
|
||||
public static final int KIND_OUTSIDE = 2;
|
||||
/**
|
||||
* 最大值
|
||||
*/
|
||||
public static final String MAX = "max";
|
||||
/**
|
||||
* 最小值
|
||||
*/
|
||||
public static final String MIN = "min";
|
||||
/**
|
||||
* ftb旷工打卡redis key
|
||||
*/
|
||||
public static final long EXPIRE_TIME_REDIS = 48 * 60 * 60;
|
||||
/**
|
||||
* ftb旷工打卡redis key
|
||||
*/
|
||||
public static final String FTB_CLOCK_KEY = "FTB_CLOCK_ABSENCE:";
|
||||
/**
|
||||
* ftb打卡提醒redis key
|
||||
*/
|
||||
public static final String FTB_REMIND_KEY = "FTB_CLOCK_REMIND:";
|
||||
|
||||
/**
|
||||
* 考勤组类型 - 我的
|
||||
*/
|
||||
public static final int GROUP_KIND_MINE = 1;
|
||||
/**
|
||||
* 考勤组类型 - 借出的
|
||||
*/
|
||||
public static final int GROUP_KIND_LEND = 2;
|
||||
/**
|
||||
* 考勤组类型 - 借入的
|
||||
*/
|
||||
public static final int GROUP_KIND_BORROW = 3;
|
||||
/**
|
||||
* 考勤组类型 - 原本的
|
||||
*/
|
||||
public static final int GROUP_KIND_OLD = 4;
|
||||
/**
|
||||
* 审批类型 - 补卡
|
||||
*/
|
||||
public static final int APPLY_REPAIR = 1;
|
||||
/**
|
||||
* 审批类型 - 出勤变更
|
||||
*/
|
||||
public static final int APPLY_CHANGE = 2;
|
||||
/**
|
||||
* 审批类型 - 外勤
|
||||
*/
|
||||
public static final int APPLY_OUTSIDE = 3;
|
||||
/**
|
||||
* 班
|
||||
*/
|
||||
public static final int RULE_TYPE_WORK = 1;
|
||||
/**
|
||||
* 休
|
||||
*/
|
||||
public static final int RULE_TYPE_REST = 2;
|
||||
/**
|
||||
* 请假
|
||||
*/
|
||||
public static final int RULE_TYPE_LEAVE = 3;
|
||||
/**
|
||||
* 规划表
|
||||
*/
|
||||
public static final int TABLE_TYPE_PLAN = 1;
|
||||
/**
|
||||
* 复查表
|
||||
*/
|
||||
public static final int TABLE_TYPE_REVIEW = 2;
|
||||
/**
|
||||
* 检查表
|
||||
*/
|
||||
public static final int TABLE_TYPE_CHECK = 3;
|
||||
/**
|
||||
* 部分
|
||||
*/
|
||||
public static final int PART = 1;
|
||||
/**
|
||||
* 总共
|
||||
*/
|
||||
public static final int TOTAL = 2;
|
||||
/**
|
||||
* 统计类型_种类
|
||||
*/
|
||||
public static final int COUNT_TYPE_KIND = 1;
|
||||
/**
|
||||
* 统计类型_检查项
|
||||
*/
|
||||
public static final int COUNT_TYPE_CHECK = 2;
|
||||
/**
|
||||
* 升序
|
||||
*/
|
||||
public static final int ASC = 1;
|
||||
/**
|
||||
* 降序
|
||||
*/
|
||||
public static final int DESC = 1;
|
||||
public static final String NAME_PLAN = "规划表";
|
||||
public static final String NAME_REVIEW = "复盘表";
|
||||
public static final String NAME_CHECK = "检查表";
|
||||
public static final String[] TITLE_STORE_TABLE = Stream.of("门店名称", "表名称", "提交人", "执行时间段", "提交时间", "提交状态").toArray(String[]::new);
|
||||
public static final String[] TITLE_STORE_CHECK_TABLE = Stream.of("门店名称", "专项", "频次", "表名称", "提交人", "执行时间段", "提交时间", "提交状态").toArray(String[]::new);
|
||||
public static final String[] TITLE_CULTURE_CLOCK_IN_TABLE = Stream.of("人员姓名", "所属组织", "最近打卡时间", "累计打卡天数", "连续打卡天数", "最长缺卡天数").toArray(String[]::new);
|
||||
/**
|
||||
* 导出表名称 - 门店执行力
|
||||
*/
|
||||
public static final String NAME_STORE_EXECUTION = "门店执行力";
|
||||
/**
|
||||
* 导出表名称 - 检查表执行力
|
||||
*/
|
||||
public static final String CHECK_STORE_EXECUTION = "检查表执行力";
|
||||
/**
|
||||
* 门店执行力导出表头
|
||||
*/
|
||||
public static final String[] TITLE_STORE_EXECUTION = Stream.of("门店名称", "已提交规划表", "应提交规划表", "规划表完成率", "已提交复盘表", "应提交复盘表", "复盘表完成率").toArray(String[]::new);
|
||||
/**
|
||||
* 门店执行力导出表头2
|
||||
*/
|
||||
public static final String[] TITLE_STORE_EXECUTION2 = Stream.of("门店名称", "应提交检查表", "未提交检查表", "已提交检查表", "待审批检查表", "检查表完成率", "审批完成率", "检查表复检率").toArray(String[]::new);
|
||||
/**
|
||||
* 检查表执行力 表头
|
||||
*/
|
||||
public static final String[] CHECK_STORE_EXECUTION2 = Stream.of("门店名称", "专项名称", "专项表名称", "完成率", "查询率", "问题率").toArray(String[]::new);
|
||||
/**
|
||||
* 检查表问题
|
||||
*/
|
||||
public static final String NAME_QUESTION_RANK = "检查表问题";
|
||||
/**
|
||||
* 问题排行导出表头
|
||||
*/
|
||||
public static final String[] TITLE_QUESTION_RANK = Stream.of("门店名称", "日期范围", "检查表名称", "检查频次", "问题分类/出现次数").toArray(String[]::new);
|
||||
/**
|
||||
* 技能点异常
|
||||
*/
|
||||
public static final String NAME_SKILL_ERROR = "技能点异常数据";
|
||||
/**
|
||||
* 技能点
|
||||
*/
|
||||
public static final String NAME_SKILL_IMPORT = "技能点导入模板";
|
||||
/**
|
||||
* 技能点导入表头
|
||||
*/
|
||||
public static final String[] TITLE_SKILL_IMPORT = Stream.of("*技能点名称", "分类", "技能点描述").toArray(String[]::new);
|
||||
/**
|
||||
* 技能点异常导出表头
|
||||
*/
|
||||
public static final String[] TITLE_SKILL_ERROR = Stream.of("*技能点名称", "分类", "技能点描述", "异常原因").toArray(String[]::new);
|
||||
/**
|
||||
* object类型 - 检查表模板
|
||||
*/
|
||||
public static final int OBJECT_TYPE_CHECK = 1;
|
||||
/**
|
||||
* 文件类型 - 附件
|
||||
*/
|
||||
public static final int FILE_TYPE_FILE = 1;
|
||||
/**
|
||||
* 文件类型 - 图片
|
||||
*/
|
||||
public static final int FILE_TYPE_PIC = 2;
|
||||
/**
|
||||
* 频率 - 每天
|
||||
*/
|
||||
public static final int FREQUENCY_EVERY_DAY = 1;
|
||||
/**
|
||||
* 频率 - 每周
|
||||
*/
|
||||
public static final int FREQUENCY_EVERY_WEEK = 2;
|
||||
/**
|
||||
* 频率 - 每月
|
||||
*/
|
||||
public static final int FREQUENCY_EVERY_MONTH = 3;
|
||||
/**
|
||||
* 0 - decimal
|
||||
*/
|
||||
public static final BigDecimal DECIMAL_ZERO = new BigDecimal("0.0000");
|
||||
/**
|
||||
* 0 - int
|
||||
*/
|
||||
public static final int ZERO = 0;
|
||||
|
||||
/**
|
||||
* 审批状态: 待审批
|
||||
*/
|
||||
public static final int STATUS_PENDING_APPROVAL = 0;
|
||||
/**
|
||||
* 审批状态: 通过
|
||||
*/
|
||||
public static final int STATUS_PASS = 1;
|
||||
/**
|
||||
* 审批状态: 未通过
|
||||
*/
|
||||
public static final int STATUS_REFUSE = 2;
|
||||
/**
|
||||
* 审批状态: 撤回
|
||||
*/
|
||||
public static final int STATUS_ROLLBACK = 3;
|
||||
/**
|
||||
* 存在
|
||||
*/
|
||||
public static final int EXIST = 0;
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
public static final int DEL = 1;
|
||||
/**
|
||||
* 消息通知缓存-外勤
|
||||
*/
|
||||
public static final String APPROVE_NOTICE_OUTSIDE = "APPROVE:OUTSIDE_";
|
||||
/**
|
||||
* 消息通知缓存-补卡
|
||||
*/
|
||||
public static final String APPROVE_NOTICE_REPAIR = "APPROVE:REPAIR_";
|
||||
/**
|
||||
* 消息通知缓存-变更
|
||||
*/
|
||||
public static final String APPROVE_NOTICE_CHANGE = "APPROVE:CHANGE_";
|
||||
public static final String FAIL_MESSAGE = "FAIL_MESSAGE:";
|
||||
/**
|
||||
* 请假
|
||||
*/
|
||||
public static final String LEAVE_STR = "请假";
|
||||
/**
|
||||
* 上班
|
||||
*/
|
||||
public static final String ON_WORK_STR = "上班";
|
||||
/**
|
||||
* 下班
|
||||
*/
|
||||
public static final String OFF_WORK_STR = "下班";
|
||||
/**
|
||||
* 请假离岗
|
||||
*/
|
||||
public static final String LEAVE_WORK_STR = "请假离岗";
|
||||
/**
|
||||
* 请假回岗
|
||||
*/
|
||||
public static final String BACK_WORK_STR = "请假回岗";
|
||||
/**
|
||||
* 考勤机打卡结果保存redis-key
|
||||
*/
|
||||
public static final String MACHINE_KEY = "MACHINE-RESULT:";
|
||||
/**
|
||||
* 猫瞳下发用户是否成功验证
|
||||
*/
|
||||
public static final String SEND_USER_CHECK = "MACHINE_SEND_CHECK:";
|
||||
|
||||
public static final List<String> IMAGE_CONTENT_TYPES = Arrays.asList(
|
||||
"image/jpeg",
|
||||
"image/jpg",
|
||||
"image/png",
|
||||
"image/webp"
|
||||
// 添加其他图片类型如果需要
|
||||
);
|
||||
public static final String EXTRA_PATH = "/jnpf-resource-1304460613/";
|
||||
public static final String URL_HEAD = "https://jnpf-resource-1304460613.cos.ap-chengdu.myqcloud.com";
|
||||
|
||||
/**
|
||||
* 适配范围[参数] - 全部
|
||||
*/
|
||||
public static final Integer SCOPE_ALL = 0;
|
||||
/**
|
||||
* 适配范围[参数] - 组织/成员
|
||||
*/
|
||||
public static final Integer SCOPE_ORG_USER = 1;
|
||||
|
||||
/**
|
||||
* 适配范围 - 全部
|
||||
*/
|
||||
public static final Integer RULE_SCOPE_ALL = 3;
|
||||
/**
|
||||
* 适配范围 - 组织
|
||||
*/
|
||||
public static final Integer RULE_SCOPE_ORG = 2;
|
||||
/**
|
||||
* 适配范围 - 个人
|
||||
*/
|
||||
public static final Integer RULE_SCOPE_USER = 1;
|
||||
|
||||
/**
|
||||
* 加班存休
|
||||
*/
|
||||
public static final Integer BALANCE_ABLE = 1;
|
||||
/**
|
||||
* 加班不存休
|
||||
*/
|
||||
public static final Integer BALANCE_ENABLE = 2;
|
||||
/**
|
||||
* 技能点导入 - 成功
|
||||
*/
|
||||
public static final String SKILL_IMPORT_SUCCESS = "skill-import-success:";
|
||||
/**
|
||||
* 技能点导入 - 失败
|
||||
*/
|
||||
public static final String SKILL_IMPORT_FAIL = "skill-import-fail:";
|
||||
/**
|
||||
* 是否需要更新_否[正常]
|
||||
*/
|
||||
public static final Integer UPDATE_FALSE = 1;
|
||||
/**
|
||||
* 是否需要更新_是[异常]
|
||||
*/
|
||||
public static final Integer UPDATE_TRUE = 2;
|
||||
/**
|
||||
* 考试类型 - 岗位
|
||||
*/
|
||||
public static final Integer EXAM_TYPE_POST = 0;
|
||||
/**
|
||||
* 考试类型 - 自定义
|
||||
*/
|
||||
public static final Integer EXAM_TYPE_CUSTOM = 1;
|
||||
/** 抽题类型 - 随机 */
|
||||
public static final int DRAW_MODE_RANDOM = 2;
|
||||
public static final int EASY = 1;
|
||||
public static final int MEDIUM = 2;
|
||||
public static final int HARD = 3;
|
||||
/** 分类 - 技能点 */
|
||||
public static final Integer LABEL_SKILL = 3;
|
||||
/** 默认ID -1 */
|
||||
public static final String DEFAULT_ID = "-1";
|
||||
public static final Integer TEN_MILLION = 10000000;
|
||||
|
||||
public static String ONLINE_MAC = "ONLINE_MACHINE:%s:";
|
||||
/**
|
||||
* 考勤机: 科密(在线状态)
|
||||
*/
|
||||
public static final String KE_MI = "KE_MI:";
|
||||
/**
|
||||
* 补卡审批类型
|
||||
*/
|
||||
public static final String REPAIR_TYPE = "6";
|
||||
// 下发考勤人员信息
|
||||
public static String MACHINE_MESSAGE = "MACHINE_MSG:%s:";
|
||||
}
|
||||
1645
jnpf-ftb/jnpf-ftb-entity/src/main/java/jnpf/util/DateDetail.java
Normal file
1645
jnpf-ftb/jnpf-ftb-entity/src/main/java/jnpf/util/DateDetail.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
package jnpf.util;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* str日期
|
||||
*
|
||||
* @author yanwenfu
|
||||
* @create 2021-06-10
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class DateStrVo {
|
||||
|
||||
/** 展示列表 */
|
||||
private List<String> showList = new ArrayList<>();
|
||||
|
||||
/** 使用列表 */
|
||||
private List<String> useList = new ArrayList<>();
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package jnpf.util;
|
||||
|
||||
/**
|
||||
* 脱敏工具类
|
||||
*
|
||||
* @author wcx
|
||||
* @date 2024/2/3
|
||||
*/
|
||||
public class DesensitizedUtils {
|
||||
/**
|
||||
* 对字符串进行脱敏操作
|
||||
*
|
||||
* @param origin 原始字符串
|
||||
* @param prefixNoMaskLen 左侧需要保留几位明文字段
|
||||
* @param suffixNoMaskLen 右侧需要保留几位明文字段
|
||||
* @param maskStr 用于遮罩的字符串, 如'*'
|
||||
* @return 脱敏后结果
|
||||
*/
|
||||
public static String desValue(String origin, int prefixNoMaskLen, int suffixNoMaskLen, String maskStr) {
|
||||
if (origin == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0, n = origin.length(); i < n; i++) {
|
||||
if (i < prefixNoMaskLen) {
|
||||
sb.append(origin.charAt(i));
|
||||
continue;
|
||||
}
|
||||
if (i > (n - suffixNoMaskLen - 1)) {
|
||||
sb.append(origin.charAt(i));
|
||||
continue;
|
||||
}
|
||||
sb.append(maskStr);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 【中文姓名】只显示最后一个汉字,其他隐藏为星号,比如:**梦
|
||||
*
|
||||
* @param fullName 姓名
|
||||
* @return 结果
|
||||
*/
|
||||
public static String chineseName(String fullName) {
|
||||
if (fullName == null) {
|
||||
return null;
|
||||
}
|
||||
return desValue(fullName, 1, 0, "*");
|
||||
}
|
||||
|
||||
/**
|
||||
* 【身份证号】显示前4位, 后2位,其他隐藏。
|
||||
*
|
||||
* @param id 身份证号码
|
||||
* @return 结果
|
||||
*/
|
||||
public static String idCardNum(String id) {
|
||||
return desValue(id, 4, 2, "*");
|
||||
}
|
||||
|
||||
/**
|
||||
* 【手机号码】前三位,后四位,其他隐藏。
|
||||
*
|
||||
* @param num 手机号码
|
||||
* @return 结果
|
||||
*/
|
||||
public static String mobilePhone(String num) {
|
||||
return desValue(num, 3, 4, "*");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package jnpf.util;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 导出工具类
|
||||
*
|
||||
* @author yanwenfu
|
||||
* @create 2023-08-10
|
||||
*/
|
||||
@Component
|
||||
public class ExportUtil {
|
||||
|
||||
public void exportTemplateExcel(Workbook workbook, HttpServletResponse response, String excelName) throws Exception {
|
||||
// 3. 导出EXCEL
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" +
|
||||
URLEncoder.encode(TemplateExcelUtils.getFileName(excelName), StandardCharsets.UTF_8));
|
||||
workbook.write(response.getOutputStream());
|
||||
}
|
||||
}
|
||||
367
jnpf-ftb/jnpf-ftb-entity/src/main/java/jnpf/util/FtbUtil.java
Normal file
367
jnpf-ftb/jnpf-ftb-entity/src/main/java/jnpf/util/FtbUtil.java
Normal file
@@ -0,0 +1,367 @@
|
||||
package jnpf.util;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.lang.Snowflake;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import jnpf.base.vo.PaginationVO;
|
||||
import jnpf.entity.attendance.AttendanceCommonSetting;
|
||||
import jnpf.enums.attendance.SettingGroup;
|
||||
import jnpf.model.common.CheckVo;
|
||||
import jnpf.permission.vo.TreeNodeVo;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 工具类
|
||||
*
|
||||
* @author yanwenfu
|
||||
* @create 2023-07-20
|
||||
*/
|
||||
public class FtbUtil {
|
||||
|
||||
public static Double getFileSize(long size) {
|
||||
|
||||
int kb = 1024;
|
||||
DecimalFormat df = new DecimalFormat("0.00");
|
||||
return Double.parseDouble(df.format(size / (float) kb));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断文件大小是否超出(true:超出)
|
||||
* @param len 文件大小
|
||||
* @param size 可使用大小
|
||||
* @param unit 单位
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean checkFileSize(double len, int size, String unit) {
|
||||
double fileSize;
|
||||
switch (unit) {
|
||||
case ConstantUtil.UNIT_B:
|
||||
fileSize = len;
|
||||
break;
|
||||
case ConstantUtil.UNIT_KB:
|
||||
fileSize = len / 1024;
|
||||
break;
|
||||
case ConstantUtil.UNIT_MB:
|
||||
fileSize = len / 1048576;
|
||||
break;
|
||||
case ConstantUtil.UNIT_G:
|
||||
fileSize = len / 1073741824;
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
return fileSize > size;
|
||||
}
|
||||
|
||||
public static PaginationVO getPagination(PageInfo<?> page) {
|
||||
|
||||
PaginationVO paginationVO = new PaginationVO();
|
||||
paginationVO.setTotal((int) page.getTotal());
|
||||
paginationVO.setPageSize((long) page.getPageSize());
|
||||
paginationVO.setCurrentPage((long) page.getPageNum());
|
||||
return paginationVO;
|
||||
}
|
||||
|
||||
public static String getId() {
|
||||
|
||||
Snowflake snowflake = IdUtil.getSnowflake(1, 1);
|
||||
return snowflake.nextIdStr();
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期差
|
||||
* @param d1 日期一
|
||||
* @param d2 日期二
|
||||
* @return int
|
||||
*/
|
||||
public static int dateDiff(Date d1, Date d2) {
|
||||
|
||||
int value = d1.compareTo(d2);
|
||||
if (value == 0) {
|
||||
return 0;
|
||||
} else if (value > 0) {
|
||||
Date temp = d1;
|
||||
d1 = d2;
|
||||
d2 = temp;
|
||||
}
|
||||
|
||||
return calculateDateDiff(d1, d2);
|
||||
}
|
||||
|
||||
|
||||
public static BigDecimal getTimeDifference(Date d1, Date d2) {
|
||||
// 计算两个时间差
|
||||
long diff = d2.getTime() - d1.getTime();
|
||||
// 计算两个时间差且结果转化为小时保留两位小数
|
||||
return new BigDecimal(diff).divide(new BigDecimal(1000 * 60 * 60), 2, BigDecimal.ROUND_HALF_UP);
|
||||
}
|
||||
/**
|
||||
* 日期差(Day2-Day1)
|
||||
* @param d1 日期一
|
||||
* @param d2 日期二
|
||||
* @return int
|
||||
*/
|
||||
public static int dateDiff2(Date d1, Date d2) {
|
||||
|
||||
return calculateDateDiff(d1, d2);
|
||||
}
|
||||
|
||||
private static int calculateDateDiff(Date d1, Date d2) {
|
||||
|
||||
Calendar cal1 = Calendar.getInstance();
|
||||
Calendar cal2 = Calendar.getInstance();
|
||||
cal1.setTime(d1);
|
||||
cal2.setTime(d2);
|
||||
|
||||
//获取日期在一年(月、星期)中的第多少天
|
||||
//第335天
|
||||
int day1 = cal1.get(Calendar.DAY_OF_YEAR);
|
||||
//第365天
|
||||
int day2 = cal2.get(Calendar.DAY_OF_YEAR);
|
||||
|
||||
//获取当前日期所在的年份
|
||||
int year1 = cal1.get(Calendar.YEAR);
|
||||
int year2 = cal2.get(Calendar.YEAR);
|
||||
|
||||
//如果两个日期的是在同一年,则只需要计算两个日期在一年的天数差;
|
||||
//不在同一年,还要加上相差年数对应的天数,闰年有366天
|
||||
if(year1 != year2) {
|
||||
// 不同年
|
||||
int timeDistance = 0 ;
|
||||
for(int i = year1 ; i < year2 ; i ++) {
|
||||
int v1 = i % 4;
|
||||
int v2 = i % 100;
|
||||
int v3 = i % 400;
|
||||
boolean flag = v1 == 0 && v2 != 0;
|
||||
if(flag || (v3 == 0)) {
|
||||
//闰年
|
||||
timeDistance += 366;
|
||||
}
|
||||
else {
|
||||
//不是闰年
|
||||
timeDistance += 365;
|
||||
}
|
||||
}
|
||||
return timeDistance + (day2 - day1);
|
||||
}
|
||||
else {
|
||||
//同年
|
||||
return day2 - day1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日期段内每一天的日期(String)
|
||||
* @param startDay 开始日期(yyyy-MM-dd)
|
||||
* @param endDay 结束日期(yyyy-MM-dd)
|
||||
* @return com.shs.cts.util.domain.vo.performance.DateStrVo
|
||||
*/
|
||||
public static DateStrVo getDateStr(String startDay, String endDay) {
|
||||
|
||||
DateStrVo dateStrVo = new DateStrVo();
|
||||
try {
|
||||
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Date start = sf.parse(startDay);
|
||||
Date end = sf.parse(endDay);
|
||||
Calendar cal1 = Calendar.getInstance();
|
||||
cal1.setTime(start);
|
||||
Calendar cal2 = Calendar.getInstance();
|
||||
cal2.setTime(end);
|
||||
while (true) {
|
||||
String format = sf.format(cal1.getTime());
|
||||
dateStrVo.getUseList().add(format);
|
||||
if (cal1.compareTo(cal2) == 0) {
|
||||
break;
|
||||
} else {
|
||||
cal1.add(Calendar.DATE, 1);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < dateStrVo.getUseList().size(); i++) {
|
||||
String str = dateStrVo.getUseList().get(i);
|
||||
String sub = str.substring(8);
|
||||
dateStrVo.getShowList().add(sub);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("getDateStr - 日期转换异常");
|
||||
}
|
||||
return dateStrVo;
|
||||
}
|
||||
|
||||
public static <T> Predicate<T> distinctByKey1(Function<? super T, ?> keyExtractor) {
|
||||
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
|
||||
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
|
||||
}
|
||||
|
||||
public static <T extends TreeNodeVo<T>> List<T> changeToTree(List<T> list) {
|
||||
|
||||
// 生成树
|
||||
List<T> tree = new ArrayList<>();
|
||||
for (T item : list) {
|
||||
if (item.getPid().equals("-1")) {
|
||||
tree.add(item);
|
||||
}
|
||||
// 寻找子节点
|
||||
for (T treeNode : list) {
|
||||
if (treeNode.getPid().equals(item.getId())) {
|
||||
item.getChildren().add(treeNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
public static <T extends TreeNodeVo<T>> List<T> changeToTreeNoParent(List<T> list, Class<T> c) {
|
||||
|
||||
CheckVo checkVo = new CheckVo(false);
|
||||
List<T> listCopy = BeanUtil.copyToList(list, c);
|
||||
for (T t : list) {
|
||||
T tt = listCopy.stream().filter(v -> v.getId().equals(t.getId())).findFirst().orElse(null);
|
||||
findParent(checkVo, listCopy, tt);
|
||||
if (checkVo.getFlag()) {
|
||||
listCopy.removeIf(v -> v.getId().equals(t.getId()));
|
||||
checkVo.setFlag(false);
|
||||
}
|
||||
}
|
||||
return listCopy;
|
||||
}
|
||||
|
||||
private static <T extends TreeNodeVo<T>> void findParent(CheckVo checkVo, List<T> list, T t) {
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
for (T value : list) {
|
||||
if (t.getPid().equals(value.getId())) {
|
||||
value.getChildren().add(t);
|
||||
checkVo.setFlag(true);
|
||||
return;
|
||||
}
|
||||
findParent(checkVo, value.getChildren(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static <T extends TreeNodeVo<T>> void nodeAddChildren(List<T> list, ConcurrentMap<String, List<T>> map) {
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
for (T value : list) {
|
||||
List<T> children = map.get(value.getId());
|
||||
if (null != children && !children.isEmpty()) {
|
||||
value.getChildren().addAll(children);
|
||||
}
|
||||
nodeAddChildren(value.getChildren(), map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 树递归排序
|
||||
* @param list 数据源
|
||||
*/
|
||||
public static <T extends TreeNodeVo<T>> void treeSort(List<T> list) {
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
list.sort(Comparator.nullsLast(Comparator.comparing(t -> t.getChildren().size(), Comparator.nullsLast(Comparable::compareTo))));
|
||||
for (T value : list) {
|
||||
treeSort(value.getChildren());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象base64编码
|
||||
* @param jsonToBean 对象
|
||||
* @return base64编码字符串
|
||||
*/
|
||||
public static String encodeJson(Object jsonToBean) {
|
||||
// return Base64.encode(new JSONObject(jsonToBean).toString(), CharsetUtil.UTF_8);
|
||||
return new JSONObject(jsonToBean).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成通用配置
|
||||
* @param group 配置组
|
||||
* @param object 实体类
|
||||
* @return java.util.List<jnpf.entity.attendance.AttendanceCommonSetting>
|
||||
*/
|
||||
public static List<AttendanceCommonSetting> generateCommonSetting(SettingGroup group, Object object, String creatorUserId) {
|
||||
|
||||
List<AttendanceCommonSetting> list = new ArrayList<>();
|
||||
try {
|
||||
for (Field field : object.getClass().getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
Object value = field.get(object);
|
||||
list.add(new AttendanceCommonSetting(
|
||||
getId(),
|
||||
group.getValue(),
|
||||
field.getName(),
|
||||
value != null ? value.toString() : null,
|
||||
creatorUserId
|
||||
));
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将配置表转换为对象
|
||||
*
|
||||
* @param list 配置表数据
|
||||
* @param clazz 目标对象的 Class
|
||||
* @return 填充了配置的对象
|
||||
*/
|
||||
public static <T> T changeListToSetting(List<AttendanceCommonSetting> list, Class<T> clazz) {
|
||||
try {
|
||||
// 创建目标对象
|
||||
T obj = clazz.getDeclaredConstructor().newInstance();
|
||||
|
||||
// 把配置表转成 Map 方便取值
|
||||
Map<String, String> map = list.stream()
|
||||
.collect(Collectors.toMap(AttendanceCommonSetting::getSettingKey, AttendanceCommonSetting::getSettingValue));
|
||||
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
String value = map.get(field.getName());
|
||||
if (value != null) {
|
||||
// 类型转换
|
||||
Object convertedValue = convertValue(value, field.getType());
|
||||
field.set(obj, convertedValue);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("配置转换失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单的类型转换(可以按需扩展)
|
||||
*/
|
||||
private static Object convertValue(String value, Class<?> targetType) {
|
||||
if (targetType == String.class) {
|
||||
return value;
|
||||
} else if (targetType == Integer.class || targetType == int.class) {
|
||||
return Integer.valueOf(value);
|
||||
} else if (targetType == Long.class || targetType == long.class) {
|
||||
return Long.valueOf(value);
|
||||
} else if (targetType == Boolean.class || targetType == boolean.class) {
|
||||
return Boolean.valueOf(value);
|
||||
} else if (targetType == Double.class || targetType == double.class) {
|
||||
return Double.valueOf(value);
|
||||
}
|
||||
// 其他类型可以根据需要扩展,比如枚举、日期等
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package jnpf.util;
|
||||
|
||||
/**
|
||||
* 人脸识别常量
|
||||
*
|
||||
* @author yanwenfu
|
||||
* @create 2025-04-08
|
||||
*/
|
||||
public class SeetaConstant {
|
||||
|
||||
// 模型文件目录
|
||||
// public static final String CSTA_PATH = "D:\\face\\models";
|
||||
public static final String CSTA_PATH = "/opt/models";
|
||||
//模型文件
|
||||
public static final String[] face_detector = {CSTA_PATH + "/face_detector.csta"};
|
||||
//年龄
|
||||
public static final String[] age_predictor = {CSTA_PATH + "/age_predictor.csta"};
|
||||
//五点
|
||||
public static final String[] face_landmarker_pts5 = {CSTA_PATH + "/face_landmarker_pts5.csta"};
|
||||
//68点
|
||||
public static final String[] face_landmarker_pts68 = {CSTA_PATH + "/face_landmarker_pts68.csta"};
|
||||
// 攻击人脸检测
|
||||
public static final String[] fas_arr = {CSTA_PATH + "/fas_first.csta", CSTA_PATH + "/fas_second.csta"};
|
||||
//眼睛状态检测的模型文件
|
||||
public static final String[] eye_state = {CSTA_PATH + "/eye_state.csta"};
|
||||
//人脸向量特征提取和对比模型
|
||||
public static final String[] face_recognizer = {CSTA_PATH + "/face_recognizer.csta"};
|
||||
//性别
|
||||
public static final String[] gender_predictor = {CSTA_PATH + "/gender_predictor.csta"};
|
||||
//姿态
|
||||
public static final String[] pose_estimation = {CSTA_PATH + "/pose_estimation.csta"};
|
||||
//清晰度
|
||||
public static final String[] quality_lbn = {CSTA_PATH + "/quality_lbn.csta"};
|
||||
//口罩模型文件
|
||||
public static final String[] mask_cstas = {CSTA_PATH + "/mask_detector.csta"};
|
||||
}
|
||||
@@ -0,0 +1,578 @@
|
||||
package jnpf.util;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
import org.apache.poi.ss.util.CellRangeAddressList;
|
||||
import org.apache.poi.xssf.usermodel.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 模板模块EXCEL工具类
|
||||
*
|
||||
* @author yanwenfu
|
||||
* @create 2019-12-29
|
||||
*/
|
||||
@Slf4j
|
||||
public class TemplateExcelUtils {
|
||||
|
||||
public static final String SUFFIX = ".xlsx";
|
||||
|
||||
/** 最大单元格宽度 */
|
||||
private static final int MAX_COL_WIDTH = 251;
|
||||
|
||||
private static final String NO_DATA = "无数据";
|
||||
public static final int MAX_ROW = 500;
|
||||
|
||||
public static String getEmptyStr(String str) {
|
||||
|
||||
return StringUtils.isEmpty(str) ? "/" : str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回完整文件名
|
||||
* @param str 文件名
|
||||
* @return java.lang.String
|
||||
*/
|
||||
public static String getFileName(String str) {
|
||||
return str + DateDetail.getDate2Str(new Date(), DateDetail.DF5) + SUFFIX;
|
||||
}
|
||||
|
||||
/**
|
||||
* 无数据的excel
|
||||
* @return org.apache.poi.ss.usermodel.Workbook
|
||||
*/
|
||||
public static Workbook createNoDateBook() {
|
||||
try {
|
||||
XSSFWorkbook workbook = new XSSFWorkbook();
|
||||
XSSFSheet sheet = workbook.createSheet();
|
||||
XSSFRow row = sheet.createRow(0);
|
||||
XSSFCell cell = row.createCell(0);
|
||||
cell.setCellValue(NO_DATA);
|
||||
return workbook;
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断文件后缀名是否与suffix一致
|
||||
* @param fileName 文件全名称
|
||||
* @param suffix 文件后缀名
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean checkFileType(String fileName, String suffix) {
|
||||
|
||||
// 文件名是否为空
|
||||
if (StringUtils.isEmpty(fileName)) {
|
||||
return false;
|
||||
}
|
||||
// 文件后缀是否为.xlsx
|
||||
return suffix.equals(fileName.substring(fileName.lastIndexOf(ConstantUtil.POINT_SYMBOL)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成工作簿
|
||||
* @param sheets 工作表集合
|
||||
* @param dealNull 是否处理dataList的null或""数据
|
||||
* @return org.apache.poi.ss.usermodel.Workbook
|
||||
*/
|
||||
public static Workbook createWorkBook(List<TemplateWorkSheet> sheets, boolean dealNull) {
|
||||
|
||||
try {
|
||||
// 创建工作簿
|
||||
XSSFWorkbook workbook = new XSSFWorkbook();
|
||||
//获取列头样式对象
|
||||
XSSFCellStyle columnTopStyle = getColumnTopStyle(workbook);
|
||||
//单元格样式对象
|
||||
XSSFCellStyle style = getStyle(workbook);
|
||||
|
||||
for (TemplateWorkSheet workSheet : sheets) {
|
||||
// 创建sheet工作表
|
||||
XSSFSheet sheet = workbook.createSheet(workSheet.getSheetName());
|
||||
sheet.setDefaultColumnWidth(17);
|
||||
|
||||
// 定义所需列数
|
||||
int column;
|
||||
if (workSheet.getTitleNum() == 1) {
|
||||
// 列名
|
||||
String[] rowNames = workSheet.getRowNames();
|
||||
column = rowNames.length;
|
||||
XSSFRow row = sheet.createRow(0);
|
||||
// 设置高度
|
||||
row.setHeight((short) (25 * 20));
|
||||
// 将列头设置到sheet的单元格中
|
||||
for (int i = 0; i < column; i++) {
|
||||
// 创建列头对应个数的单元格
|
||||
XSSFCell cell = row.createCell(i);
|
||||
// 设置列头单元格的数据类型
|
||||
cell.setCellType(CellType.STRING);
|
||||
XSSFRichTextString text = new XSSFRichTextString(rowNames[i]);
|
||||
// 设置列头单元格的值
|
||||
cell.setCellValue(text);
|
||||
// 设置列头单元格样式
|
||||
cell.setCellStyle(columnTopStyle);
|
||||
}
|
||||
} else {
|
||||
String[][] rowNames = workSheet.getComplexRowNames();
|
||||
column = rowNames[0].length;
|
||||
for (int i = 0; i < rowNames.length; i++) {
|
||||
XSSFRow row = sheet.createRow(i);
|
||||
// 设置高度
|
||||
row.setHeight((short) (25 * 20));
|
||||
for (int j = 0; j < rowNames[i].length; j++) {
|
||||
// 创建列头对应个数的单元格
|
||||
XSSFCell cell = row.createCell(j);
|
||||
// 设置列头单元格的数据类型
|
||||
cell.setCellType(CellType.STRING);
|
||||
XSSFRichTextString text = new XSSFRichTextString(rowNames[i][j]);
|
||||
// 设置列头单元格的值
|
||||
cell.setCellValue(text);
|
||||
// 设置列头单元格样式
|
||||
cell.setCellStyle(columnTopStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 将查询出的数据设置到sheet对应的单元格中
|
||||
List<Object[]> dataList = workSheet.getDataList();
|
||||
if (null == dataList || dataList.size() == 0) {
|
||||
// 合并单元格
|
||||
mergeCells(workSheet, sheet);
|
||||
if (workSheet.isHasRemark()) {
|
||||
XSSFCellStyle thisCellStyle = getColumnTopStyle(workbook);
|
||||
thisCellStyle.setAlignment(HorizontalAlignment.LEFT);
|
||||
sheet.getRow(0).getCell(0).setCellStyle(thisCellStyle);
|
||||
}
|
||||
// 生成下拉框
|
||||
Map<String[], int[]> map = workSheet.getDropDownListMap();
|
||||
generateDropDownList(sheet, map);
|
||||
continue;
|
||||
}
|
||||
// 合并单元格
|
||||
mergeCells(workSheet, sheet);
|
||||
for (int i = 0; i < dataList.size(); i++) {
|
||||
// 遍历每个对象
|
||||
Object[] obj = dataList.get(i);
|
||||
// 创建所需的行数
|
||||
XSSFRow objRow = sheet.createRow(i + workSheet.getTitleNum());
|
||||
// 设置高度
|
||||
objRow.setHeight((short) (25 * 20));
|
||||
for (int j = 0; j < obj.length; j++) {
|
||||
// 设置单元格的数据类型
|
||||
XSSFCell cell = objRow.createCell(j, CellType.STRING);
|
||||
if (null != obj[j] && (!"".equals(obj[j]))) {
|
||||
// 设置单元格的值
|
||||
cell.setCellValue(obj[j].toString());
|
||||
} else {
|
||||
if (dealNull) {
|
||||
cell.setCellValue("/");
|
||||
} else {
|
||||
cell.setCellValue("");
|
||||
}
|
||||
}
|
||||
// 设置单元格样式
|
||||
cell.setCellStyle(style);
|
||||
}
|
||||
}
|
||||
// 让列宽随着导出的列长自动适应
|
||||
for (int colNum = 0; colNum < column; colNum++) {
|
||||
int columnWidth = sheet.getColumnWidth(colNum) / 256;
|
||||
for (int rowNum = 0; rowNum < sheet.getLastRowNum(); rowNum++) {
|
||||
XSSFRow currentRow;
|
||||
// 当前行未被使用过
|
||||
if (sheet.getRow(rowNum) == null) {
|
||||
currentRow = sheet.createRow(rowNum);
|
||||
} else {
|
||||
currentRow = sheet.getRow(rowNum);
|
||||
}
|
||||
if (currentRow.getCell(colNum) != null) {
|
||||
XSSFCell currentCell = currentRow.getCell(colNum);
|
||||
if (currentCell.getCellType() == CellType.STRING) {
|
||||
int length = currentCell.getStringCellValue().getBytes().length;
|
||||
if (columnWidth < length) {
|
||||
columnWidth = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (columnWidth > MAX_COL_WIDTH) {
|
||||
columnWidth = MAX_COL_WIDTH;
|
||||
}
|
||||
sheet.setColumnWidth(colNum, (columnWidth + 4) * 256);
|
||||
}
|
||||
}
|
||||
return workbook;
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成当前下拉框的坐标
|
||||
*
|
||||
* @param x x坐标
|
||||
* @param y y坐标
|
||||
* @return int[]
|
||||
*/
|
||||
public static int[] getCoordinate(int x, int y) {
|
||||
int[] coordinate = new int[4];
|
||||
coordinate[0] = x;
|
||||
coordinate[1] = MAX_ROW;
|
||||
coordinate[2] = y;
|
||||
coordinate[3] = y;
|
||||
return coordinate;
|
||||
}
|
||||
|
||||
public static void generateDropDownList(XSSFSheet sheet, Map<String[], int[]> map) {
|
||||
|
||||
if (null != map && map.size() > 0) {
|
||||
for (String[] key : map.keySet()) {
|
||||
if (key.length > 0) {
|
||||
int[] coordinate = map.get(key);
|
||||
DataValidationHelper dvHelper = sheet.getDataValidationHelper();
|
||||
XSSFDataValidationConstraint dvConstraint = (XSSFDataValidationConstraint) dvHelper
|
||||
.createExplicitListConstraint(key);
|
||||
// 第一个参数是开始行,第二个是结束行,第三个是开始列,第四个是结束列
|
||||
CellRangeAddressList addressList = new CellRangeAddressList(coordinate[0], coordinate[1], coordinate[2], coordinate[3]);
|
||||
DataValidation validation = dvHelper.createValidation(dvConstraint, addressList);
|
||||
// 设置只能选下拉菜单里的值不能随便输入
|
||||
validation.setSuppressDropDownArrow(true);
|
||||
validation.setShowErrorBox(true);
|
||||
sheet.addValidationData(validation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并单元格
|
||||
* @param workSheet 工作表对象
|
||||
* @param sheet 工作表
|
||||
*/
|
||||
private static void mergeCells(TemplateWorkSheet workSheet, XSSFSheet sheet) {
|
||||
if (workSheet.getTitleNum() > 1) {
|
||||
List<int[]> mergeCells = workSheet.getMergeCells();
|
||||
for (int[] mergeCell : mergeCells) {
|
||||
CellRangeAddress region = new CellRangeAddress(mergeCell[0], mergeCell[1], mergeCell[2], mergeCell[3]);
|
||||
sheet.addMergedRegion(region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置列头样式
|
||||
* @param workbook 工作簿
|
||||
* @return org.apache.poi.xssf.usermodel.XSSFCellStyle
|
||||
*/
|
||||
private static XSSFCellStyle getColumnTopStyle(XSSFWorkbook workbook) {
|
||||
|
||||
// 设置字体
|
||||
XSSFFont font = workbook.createFont();
|
||||
// 设置字体大小
|
||||
font.setFontHeightInPoints((short) 11);
|
||||
// 字体加粗
|
||||
font.setBold(true);
|
||||
// 设置字体名字
|
||||
font.setFontName("宋体");
|
||||
// 设置样式;
|
||||
XSSFCellStyle style = workbook.createCellStyle();
|
||||
setCellStyle(font, style);
|
||||
|
||||
// 设置单元格背景颜色
|
||||
style.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());
|
||||
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
private static XSSFCellStyle setCellStyle(XSSFFont font, XSSFCellStyle style) {
|
||||
// 设置底边框;
|
||||
style.setBorderBottom(BorderStyle.THIN);
|
||||
// 设置底边框颜色;
|
||||
style.setBottomBorderColor(new XSSFColor(Color.black));
|
||||
// 设置左边框;
|
||||
style.setBorderLeft(BorderStyle.THIN);
|
||||
// 设置左边框颜色;
|
||||
style.setLeftBorderColor(new XSSFColor(Color.black));
|
||||
// 设置右边框;
|
||||
style.setBorderRight(BorderStyle.THIN);
|
||||
// 设置右边框颜色;
|
||||
style.setRightBorderColor(new XSSFColor(Color.black));
|
||||
// 设置顶边框;
|
||||
style.setBorderTop(BorderStyle.THIN);
|
||||
// 设置顶边框颜色;
|
||||
style.setTopBorderColor(new XSSFColor(Color.black));
|
||||
// 在样式用应用设置的字体;
|
||||
style.setFont(font);
|
||||
// 设置自动换行;
|
||||
style.setWrapText(false);
|
||||
// 设置水平对齐的样式为居中对齐;
|
||||
style.setAlignment(HorizontalAlignment.CENTER);
|
||||
// 设置垂直对齐的样式为居中对齐;
|
||||
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列数据信息单元格样式
|
||||
* @param workbook 工作簿
|
||||
* @return org.apache.poi.xssf.usermodel.XSSFCellStyle
|
||||
*/
|
||||
public static XSSFCellStyle getStyle(XSSFWorkbook workbook) {
|
||||
// 设置字体
|
||||
XSSFFont font = workbook.createFont();
|
||||
font.setFontName("宋体");
|
||||
//设置样式;
|
||||
XSSFCellStyle style = workbook.createCellStyle();
|
||||
setCellStyle(font, style);
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回交集的值
|
||||
* @param chooseList 多选val
|
||||
* @param value excel val
|
||||
* @return java.lang.String
|
||||
*/
|
||||
private static String checkMultiSelectValue(String[] chooseList, String value) {
|
||||
String str = "";
|
||||
if (chooseList.length < 1 || StringUtils.isEmpty(value)) {
|
||||
return str;
|
||||
}
|
||||
String replace = value.replace(",", ",");
|
||||
String[] split = replace.split(",");
|
||||
str = getMixed(chooseList, split);
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 求交集
|
||||
* @param chooseList 多选val
|
||||
* @param split excel val
|
||||
* @return java.lang.String
|
||||
*/
|
||||
private static String getMixed(String[] chooseList, String[] split) {
|
||||
List<String> list = new ArrayList<>();
|
||||
String[] longArray = chooseList.length > split.length ? chooseList : split;
|
||||
String[] shortArray = chooseList.length > split.length ? split : chooseList;
|
||||
Set<String> set = new HashSet<>(Arrays.asList(longArray));
|
||||
for (String str : shortArray) {
|
||||
if (set.contains(str)) {
|
||||
list.add(str);
|
||||
}
|
||||
}
|
||||
if (list.size() == 0) {
|
||||
return "";
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (i == list.size() - 1) {
|
||||
sb.append(list.get(i));
|
||||
} else {
|
||||
sb.append(list.get(i)).append(",");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析工作表
|
||||
* @param sheet 工作表
|
||||
* @return java.util.List<java.lang.String[]>
|
||||
*/
|
||||
public static List<String[]> getDataFromSheet(XSSFSheet sheet) {
|
||||
List<String[]> list = new ArrayList<>();
|
||||
int maxCellNum = sheet.getRow(0).getLastCellNum(); // 获取第一行的最大列数
|
||||
|
||||
for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) {
|
||||
XSSFRow row = sheet.getRow(i);
|
||||
if (row == null || row.getPhysicalNumberOfCells() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] cells = new String[maxCellNum]; // 使用最大列数初始化数组
|
||||
boolean hasValidData = false; // 标记是否有有效数据
|
||||
for (int j = 0; j < maxCellNum; j++) { // 从0开始遍历到最大列数
|
||||
XSSFCell cell = row.getCell(j, XSSFRow.MissingCellPolicy.RETURN_BLANK_AS_NULL); // 使用RETURN_BLANK_AS_NULL策略获取单元格
|
||||
if (cell == null) {
|
||||
cells[j] = "";
|
||||
} else {
|
||||
cells[j] = getCellValue(cell);
|
||||
if (!cells[j].isEmpty()) {
|
||||
hasValidData = true; // 如果单元格不为空,标记有有效数据
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasValidData) { // 只有当行中有有效数据时才添加到列表中
|
||||
list.add(cells);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析工作表[包含空行]
|
||||
* @param sheet 工作表
|
||||
* @return java.util.List<java.lang.String[]>
|
||||
*/
|
||||
public static List<String[]> getDataFromSheetWithNullRow(XSSFSheet sheet) {
|
||||
|
||||
List<String[]> list = new ArrayList<>();
|
||||
int maxCellNum = sheet.getRow(0).getLastCellNum(); // 获取第一行的最大列数
|
||||
|
||||
for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) {
|
||||
XSSFRow row = sheet.getRow(i);
|
||||
String[] cells = new String[maxCellNum]; // 使用最大列数初始化数组
|
||||
if (row == null || row.getPhysicalNumberOfCells() == 0) {
|
||||
Arrays.fill(cells, "");
|
||||
list.add(cells);
|
||||
} else {
|
||||
boolean hasValidData = false; // 标记是否有有效数据
|
||||
for (int j = 0; j < maxCellNum; j++) { // 从0开始遍历到最大列数
|
||||
XSSFCell cell = row.getCell(j, XSSFRow.MissingCellPolicy.RETURN_BLANK_AS_NULL); // 使用RETURN_BLANK_AS_NULL策略获取单元格
|
||||
if (cell == null) {
|
||||
cells[j] = "";
|
||||
} else {
|
||||
cells[j] = getCellValue(cell);
|
||||
if (!cells[j].isEmpty()) {
|
||||
hasValidData = true; // 如果单元格不为空,标记有有效数据
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasValidData) { // 只有当行中有有效数据时才添加到列表中
|
||||
list.add(cells);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析工作表
|
||||
* @param sheet 工作表
|
||||
* @return java.util.List<java.lang.String[]>
|
||||
*/
|
||||
public static List<String[]> getDataFromSheetByRow(XSSFSheet sheet) {
|
||||
List<String[]> list = new ArrayList<>();
|
||||
int len = new String[sheet.getRow(0).getLastCellNum()].length;
|
||||
for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i ++) {
|
||||
XSSFRow row = sheet.getRow(i);
|
||||
if (row == null || row.getPhysicalNumberOfCells() == 0) {
|
||||
continue;
|
||||
}
|
||||
int currentLen = sheet.getRow(i).getLastCellNum();
|
||||
len = currentLen >= len ? currentLen : len;
|
||||
String[] cells = new String[len];
|
||||
for (int j = row.getFirstCellNum(); j < len; j++) {
|
||||
XSSFCell cell = row.getCell(j);
|
||||
if (null == cell) {
|
||||
cells[j] = "";
|
||||
} else {
|
||||
cells[j] = getCellValue(cell);
|
||||
}
|
||||
}
|
||||
list.add(cells);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单元格的值
|
||||
* @param cell 单元格
|
||||
* @return java.lang.String
|
||||
*/
|
||||
private static String getCellValue(XSSFCell cell) {
|
||||
String str;
|
||||
CellType cellType = cell.getCellTypeEnum();
|
||||
if (cellType.equals(CellType.NUMERIC)) {
|
||||
if (DateUtil.isCellDateFormatted(cell)) {
|
||||
str = DateDetail.getDate2Str(cell.getDateCellValue(), DateDetail.DF4);
|
||||
} else {
|
||||
NumberFormat nf = NumberFormat.getInstance();
|
||||
str = nf.format(cell.getNumericCellValue());
|
||||
if (str.contains(",")) {
|
||||
str = str.replace(",", "");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
str = cell.getStringCellValue();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析出下拉框的值
|
||||
* @param val 组件属性
|
||||
* @return java.lang.String[]
|
||||
*/
|
||||
public static String[] getDropDownList(String val) {
|
||||
List<String> list = new ArrayList<>();
|
||||
if (StringUtils.isNotEmpty(val)) {
|
||||
try {
|
||||
if (StringUtils.isNotEmpty(val)) {
|
||||
Map jsonMap = JSONObject.parseObject(val, Map.class);
|
||||
if (null != jsonMap) {
|
||||
JSONArray jsonArray = (JSONArray) jsonMap.get("options");
|
||||
if (null != jsonArray) {
|
||||
for (Object obj : jsonArray) {
|
||||
Map m = (Map) obj;
|
||||
Object o = m.get("selectName");
|
||||
String value = null == o ? "" : o.toString();
|
||||
if (StringUtils.isNotEmpty(value)) {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.info(e.getMessage());
|
||||
list = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
String[] values = new String[list.size()];
|
||||
list.toArray(values);
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证值是否在数组中
|
||||
* @param values 数组
|
||||
* @param value 值
|
||||
* @return java.lang.String
|
||||
*/
|
||||
private static String checkValue(String[] values, String value) {
|
||||
String str = "";
|
||||
if (values.length < 1) {
|
||||
return str;
|
||||
}
|
||||
for (String val : values) {
|
||||
if (val.equals(value)) {
|
||||
str = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判读是否为空
|
||||
* @param obj 对象
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean isEmpty(Object obj) {
|
||||
return null == obj || "".equals(obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package jnpf.util;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 模板工作表
|
||||
*
|
||||
* @author yanwenfu
|
||||
* @create 2019-12-29
|
||||
*/
|
||||
@Component
|
||||
@Scope("prototype")
|
||||
@Getter
|
||||
@Setter
|
||||
public class TemplateWorkSheet {
|
||||
|
||||
/** 工作表名称 */
|
||||
private String sheetName;
|
||||
/** 列名 */
|
||||
private String[] rowNames;
|
||||
/** 数据 */
|
||||
private List<Object[]> dataList;
|
||||
/** 复杂列 */
|
||||
private String[][] complexRowNames;
|
||||
/** 需要合并的单元格 */
|
||||
private List<int[]> mergeCells;
|
||||
/** 表头行数 */
|
||||
private int titleNum;
|
||||
/** 下拉列表及坐标 */
|
||||
private Map<String[], int[]> dropDownListMap;
|
||||
/** 有无备注 */
|
||||
private boolean hasRemark = false;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package jnpf.util.excelv2.adapter;
|
||||
|
||||
/**
|
||||
* 动态数据转换适配器
|
||||
* <p>
|
||||
* 例如: ["1"=>"张三","2"=>"王五"]
|
||||
*
|
||||
* @author
|
||||
*/
|
||||
public interface SelfDataConversionAdapter {
|
||||
|
||||
|
||||
/**
|
||||
* 数据转换 [value 转 key]
|
||||
* 例如:[把 张三 换成 1]
|
||||
*
|
||||
* @param value 数据
|
||||
* @param args 参数
|
||||
* @return
|
||||
*/
|
||||
Object valueToKeyConvert(Object value, String[] args);
|
||||
|
||||
/**
|
||||
* 数据转换 [key 转 value]
|
||||
* 例如:[把 1 换成 张三]
|
||||
*
|
||||
* @param key 数据
|
||||
* @param args 参数
|
||||
* @return
|
||||
*/
|
||||
Object keyToValueConvert(Object key, String[] args);
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
package jnpf.util.excelv2.annotation;
|
||||
|
||||
import jnpf.util.excelv2.adapter.SelfDataConversionAdapter;
|
||||
import org.apache.poi.common.usermodel.HyperlinkType;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 自定义导出 Excel 数据注解
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Excel {
|
||||
/**
|
||||
* 默认列高
|
||||
*/
|
||||
double DEFAULT_HEIGHT = 14.0;
|
||||
|
||||
/**
|
||||
* 默认列宽
|
||||
*/
|
||||
double DEFAULT_WIDTH = 16.0;
|
||||
|
||||
/**
|
||||
* 默认排序权重(值越小越靠前)
|
||||
*/
|
||||
int DEFAULT_SORT = Integer.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* 默认 BigDecimal 精度(-1 表示不启用)
|
||||
*/
|
||||
int DEFAULT_SCALE = -1;
|
||||
|
||||
/**
|
||||
* 默认 BigDecimal 舍入模式(对应 BigDecimal.ROUND_HALF_EVEN)
|
||||
*/
|
||||
int DEFAULT_ROUNDING_MODE = BigDecimal.ROUND_HALF_EVEN;
|
||||
|
||||
/**
|
||||
* 导出时在excel中排序
|
||||
*/
|
||||
int sort() default DEFAULT_SORT;
|
||||
|
||||
/**
|
||||
* 导出到 Excel 中的名字
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* 日期格式,如:yyyy-MM-dd
|
||||
*/
|
||||
String dateFormat() default "";
|
||||
|
||||
/**
|
||||
* 读取固定内容转表达式 (如: 0=男,1=女,2=未知)
|
||||
*/
|
||||
String fixDataConverter() default "";
|
||||
|
||||
/**
|
||||
* 自定义数据处理器(必须是 SelfDataConversionAdapter 的子类)
|
||||
*/
|
||||
Class<? extends SelfDataConversionAdapter> selfDataConversionAdapter() default SelfDataConversionAdapter.class;
|
||||
|
||||
/**
|
||||
* 是否开启导出数据转换
|
||||
*/
|
||||
boolean isOpenExportDataConverter() default false;
|
||||
|
||||
/**
|
||||
* 是否开启导入数据转换
|
||||
*/
|
||||
boolean isOpenImportDataConverter() default true;
|
||||
|
||||
/**
|
||||
* 分隔符,读取字符串组内容
|
||||
*/
|
||||
String separator() default ",";
|
||||
|
||||
/**
|
||||
* BigDecimal 精度,-1 表示不启用格式化
|
||||
*/
|
||||
int scale() default DEFAULT_SCALE;
|
||||
|
||||
/**
|
||||
* BigDecimal 舍入规则(使用 BigDecimal 的舍入模式常量,如:BigDecimal.ROUND_HALF_UP)
|
||||
*/
|
||||
int roundingMode() default DEFAULT_ROUNDING_MODE;
|
||||
|
||||
/**
|
||||
* 导出时在 excel 中每个列的高度(单位:磅)
|
||||
*/
|
||||
double height() default DEFAULT_HEIGHT;
|
||||
|
||||
/**
|
||||
* 导出时在 excel 中每个列的宽度(单位:字符)
|
||||
*/
|
||||
double width() default DEFAULT_WIDTH;
|
||||
|
||||
/**
|
||||
* 文字后缀,如:90 变成 90%
|
||||
*/
|
||||
String suffix() default "";
|
||||
|
||||
/**
|
||||
* 当值为空时的默认值
|
||||
*/
|
||||
String defaultValue() default "";
|
||||
|
||||
/**
|
||||
* 单元格提示信息
|
||||
*/
|
||||
String prompt() default "";
|
||||
|
||||
/**
|
||||
* 是否允许单元格内容换行
|
||||
*/
|
||||
boolean wrapText() default false;
|
||||
|
||||
/**
|
||||
* 下拉选择选项(静态定义)
|
||||
*/
|
||||
String[] combo() default {};
|
||||
|
||||
/**
|
||||
* 下拉选择选项(从 Bean 方法动态获取,格式:className.getMethodName())
|
||||
*/
|
||||
String comboFromBeanMethod() default "";
|
||||
|
||||
/**
|
||||
* 是否需要纵向合并单元格(用于处理 list 集合)
|
||||
*/
|
||||
boolean needMerge() default false;
|
||||
|
||||
/**
|
||||
* 是否导出数据(false 时仅导出标题,内容需手工填写)
|
||||
*/
|
||||
boolean isExport() default true;
|
||||
|
||||
/**
|
||||
* 关联属性名(支持多级获取,以小数点隔开)
|
||||
*/
|
||||
String targetAttr() default "";
|
||||
|
||||
/**
|
||||
* 是否自动统计(在最后追加一行汇总数据)
|
||||
*/
|
||||
boolean isStatistics() default false;
|
||||
|
||||
/**
|
||||
* 单元格数据类型
|
||||
*/
|
||||
ColumnType cellType() default ColumnType.STRING;
|
||||
|
||||
// ========== 样式配置开始 ==========
|
||||
|
||||
/**
|
||||
* 列头背景颜色
|
||||
*/
|
||||
IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT;
|
||||
|
||||
/**
|
||||
* 列头字体颜色
|
||||
*/
|
||||
IndexedColors headerColor() default IndexedColors.WHITE;
|
||||
|
||||
/**
|
||||
* 单元格背景颜色
|
||||
*/
|
||||
IndexedColors backgroundColor() default IndexedColors.WHITE;
|
||||
|
||||
/**
|
||||
* 单元格字体颜色
|
||||
*/
|
||||
IndexedColors color() default IndexedColors.BLACK;
|
||||
|
||||
/**
|
||||
* 字段对齐方式
|
||||
*/
|
||||
HorizontalAlignment align() default HorizontalAlignment.CENTER;
|
||||
|
||||
// ========== 样式配置结束 ==========
|
||||
|
||||
|
||||
/**
|
||||
* 自定义数据处理器参数
|
||||
*/
|
||||
String[] args() default {};
|
||||
|
||||
// ========== 超链接配置开始 ==========
|
||||
|
||||
/**
|
||||
* 超链接类型(仅对 LINK 类型有效)
|
||||
*/
|
||||
HyperlinkType hyperlinkType() default HyperlinkType.URL;
|
||||
|
||||
/**
|
||||
* 超链接显示文本(仅对 LINK 类型有效)
|
||||
* 如果不设置,则默认显示链接地址本身
|
||||
*/
|
||||
String linkText() default "";
|
||||
|
||||
/**
|
||||
* 超链接显示文本的字段名(仅对 LINK 类型有效 且 字段在本类中存在)
|
||||
* 优先级高于 linkText,即如果同时设置了 linkTextField 和 linkText,将使用 linkTextField
|
||||
*/
|
||||
String linkTextField() default "";
|
||||
|
||||
// ========== 超链接配置结束 ==========
|
||||
|
||||
|
||||
/**
|
||||
* 字段用途(控制导入导出行为)
|
||||
*/
|
||||
Type type() default Type.ALL;
|
||||
|
||||
/**
|
||||
* 字段用途枚举
|
||||
*/
|
||||
enum Type {
|
||||
/**
|
||||
* 既导入又导出
|
||||
*/
|
||||
ALL(0),
|
||||
/**
|
||||
* 仅导出
|
||||
*/
|
||||
EXPORT(1),
|
||||
/**
|
||||
* 仅导入
|
||||
*/
|
||||
IMPORT(2);
|
||||
|
||||
private final int value;
|
||||
|
||||
Type(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单元格数据类型枚举
|
||||
*/
|
||||
enum ColumnType {
|
||||
/**
|
||||
* 数字类型
|
||||
*/
|
||||
NUMERIC(0),
|
||||
/**
|
||||
* 字符串类型
|
||||
*/
|
||||
STRING(1),
|
||||
/**
|
||||
* 图片类型
|
||||
*/
|
||||
IMAGE(2),
|
||||
/**
|
||||
* 文本类型(长文本)
|
||||
*/
|
||||
TEXT(3),
|
||||
/**
|
||||
* 超链接类型
|
||||
*/
|
||||
LINK(4),
|
||||
/**
|
||||
* 列表超链接类型
|
||||
*/
|
||||
LINK_LIST(5);
|
||||
|
||||
private final int value;
|
||||
|
||||
ColumnType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package jnpf.util.excelv2.annotation;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Excels {
|
||||
Excel[] value();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package jnpf.util.excelv2.domain;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class ExcelBeanMethodParseVo {
|
||||
/**
|
||||
* bean名称
|
||||
*/
|
||||
private String serviceName;
|
||||
/**
|
||||
* 方法名称
|
||||
*/
|
||||
private String methodName;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private String[] parameters;
|
||||
|
||||
public ExcelBeanMethodParseVo(String serviceName, String methodName, String[] parameters) {
|
||||
this.serviceName = serviceName;
|
||||
this.methodName = methodName;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,851 @@
|
||||
package jnpf.util.excelv2.util;
|
||||
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Set;
|
||||
|
||||
public class CellValueConvert {
|
||||
/**
|
||||
* 转换为字符串<br>
|
||||
* 如果给定的值为null,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static String toStr(Object value, String defaultValue) {
|
||||
if (null == value) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof String) {
|
||||
return (String) value;
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为字符串<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static String toStr(Object value) {
|
||||
return toStr(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为字符<br>
|
||||
* 如果给定的值为null,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Character toChar(Object value, Character defaultValue) {
|
||||
if (null == value) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Character) {
|
||||
return (Character) value;
|
||||
}
|
||||
|
||||
final String valueStr = toStr(value, null);
|
||||
return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为字符<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Character toChar(Object value) {
|
||||
return toChar(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为byte<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Byte toByte(Object value, Byte defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Byte) {
|
||||
return (Byte) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).byteValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Byte.parseByte(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为byte<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Byte toByte(Object value) {
|
||||
return toByte(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Short<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Short toShort(Object value, Short defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Short) {
|
||||
return (Short) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).shortValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Short.parseShort(valueStr.trim());
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Short<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Short toShort(Object value) {
|
||||
return toShort(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Number<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Number toNumber(Object value, Number defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return (Number) value;
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return NumberFormat.getInstance().parse(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Number<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Number toNumber(Object value) {
|
||||
return toNumber(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为int<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Integer toInt(Object value, Integer defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
return (Integer) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(valueStr.trim());
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为int<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Integer toInt(Object value) {
|
||||
return toInt(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Integer数组<br>
|
||||
*
|
||||
* @param str 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Integer[] toIntArray(String str) {
|
||||
return toIntArray(",", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Long数组<br>
|
||||
*
|
||||
* @param str 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Long[] toLongArray(String str) {
|
||||
return toLongArray(",", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Integer数组<br>
|
||||
*
|
||||
* @param split 分隔符
|
||||
* @param str 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Integer[] toIntArray(String split, String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return new Integer[]{};
|
||||
}
|
||||
String[] arr = str.split(split);
|
||||
final Integer[] ints = new Integer[arr.length];
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
final Integer v = toInt(arr[i], 0);
|
||||
ints[i] = v;
|
||||
}
|
||||
return ints;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Long数组<br>
|
||||
*
|
||||
* @param split 分隔符
|
||||
* @param str 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Long[] toLongArray(String split, String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return new Long[]{};
|
||||
}
|
||||
String[] arr = str.split(split);
|
||||
final Long[] longs = new Long[arr.length];
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
final Long v = toLong(arr[i], null);
|
||||
longs[i] = v;
|
||||
}
|
||||
return longs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为String数组<br>
|
||||
*
|
||||
* @param str 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static String[] toStrArray(String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return new String[]{};
|
||||
}
|
||||
return toStrArray(",", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为String数组<br>
|
||||
*
|
||||
* @param split 分隔符
|
||||
* @param str 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static String[] toStrArray(String split, String str) {
|
||||
return str.split(split);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为long<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Long toLong(Object value, Long defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
// 支持科学计数法
|
||||
return new BigDecimal(valueStr.trim()).longValue();
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为long<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Long toLong(Object value) {
|
||||
return toLong(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为double<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Double toDouble(Object value, Double defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Double) {
|
||||
return (Double) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
// 支持科学计数法
|
||||
return new BigDecimal(valueStr.trim()).doubleValue();
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为double<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Double toDouble(Object value) {
|
||||
return toDouble(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Float<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Float toFloat(Object value, Float defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Float) {
|
||||
return (Float) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).floatValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Float.parseFloat(valueStr.trim());
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Float<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Float toFloat(Object value) {
|
||||
return toFloat(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为boolean<br>
|
||||
* String支持的值为:true、false、yes、ok、no、1、0、是、否, 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Boolean toBool(Object value, Boolean defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value;
|
||||
}
|
||||
String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
valueStr = valueStr.trim().toLowerCase();
|
||||
switch (valueStr) {
|
||||
case "true":
|
||||
case "yes":
|
||||
case "ok":
|
||||
case "1":
|
||||
case "是":
|
||||
return true;
|
||||
case "false":
|
||||
case "no":
|
||||
case "0":
|
||||
case "否":
|
||||
return false;
|
||||
default:
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为boolean<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static Boolean toBool(Object value) {
|
||||
return toBool(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Enum对象<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
*
|
||||
* @param clazz Enum的Class
|
||||
* @param value 值
|
||||
* @param defaultValue 默认值
|
||||
* @return Enum
|
||||
*/
|
||||
public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value, E defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (clazz.isAssignableFrom(value.getClass())) {
|
||||
@SuppressWarnings("unchecked")
|
||||
E myE = (E) value;
|
||||
return myE;
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Enum.valueOf(clazz, valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Enum对象<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
*
|
||||
* @param clazz Enum的Class
|
||||
* @param value 值
|
||||
* @return Enum
|
||||
*/
|
||||
public static <E extends Enum<E>> E toEnum(Class<E> clazz, Object value) {
|
||||
return toEnum(clazz, value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigInteger<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static BigInteger toBigInteger(Object value, BigInteger defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof BigInteger) {
|
||||
return (BigInteger) value;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return BigInteger.valueOf((Long) value);
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return new BigInteger(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigInteger<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static BigInteger toBigInteger(Object value) {
|
||||
return toBigInteger(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigDecimal<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
* @return 结果
|
||||
*/
|
||||
public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof BigDecimal) {
|
||||
return (BigDecimal) value;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return new BigDecimal((Long) value);
|
||||
}
|
||||
if (value instanceof Double) {
|
||||
return BigDecimal.valueOf((Double) value);
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
return new BigDecimal((Integer) value);
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigDecimal<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @return 结果
|
||||
*/
|
||||
public static BigDecimal toBigDecimal(Object value) {
|
||||
return toBigDecimal(value, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 将对象转为字符串<br>
|
||||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param charsetName 字符集
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str(Object obj, String charsetName) {
|
||||
return str(obj, Charset.forName(charsetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转为字符串<br>
|
||||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param charset 字符集
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str(Object obj, Charset charset) {
|
||||
if (null == obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (obj instanceof String) {
|
||||
return (String) obj;
|
||||
} else if (obj instanceof byte[] || obj instanceof Byte[]) {
|
||||
if (obj instanceof byte[]) {
|
||||
return str((byte[]) obj, charset);
|
||||
} else {
|
||||
Byte[] bytes = (Byte[]) obj;
|
||||
int length = bytes.length;
|
||||
byte[] dest = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
dest[i] = bytes[i];
|
||||
}
|
||||
return str(dest, charset);
|
||||
}
|
||||
} else if (obj instanceof ByteBuffer) {
|
||||
return str((ByteBuffer) obj, charset);
|
||||
}
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将byte数组转为字符串
|
||||
*
|
||||
* @param bytes byte数组
|
||||
* @param charset 字符集
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str(byte[] bytes, String charset) {
|
||||
return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码字节码
|
||||
*
|
||||
* @param data 字符串
|
||||
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
|
||||
* @return 解码后的字符串
|
||||
*/
|
||||
public static String str(byte[] data, Charset charset) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null == charset) {
|
||||
return new String(data);
|
||||
}
|
||||
return new String(data, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将编码的byteBuffer数据转换为字符串
|
||||
*
|
||||
* @param data 数据
|
||||
* @param charset 字符集,如果为空使用当前系统字符集
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str(ByteBuffer data, String charset) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return str(data, Charset.forName(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将编码的byteBuffer数据转换为字符串
|
||||
*
|
||||
* @param data 数据
|
||||
* @param charset 字符集,如果为空使用当前系统字符集
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str(ByteBuffer data, Charset charset) {
|
||||
if (null == charset) {
|
||||
charset = Charset.defaultCharset();
|
||||
}
|
||||
return charset.decode(data).toString();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- 全角半角转换
|
||||
|
||||
/**
|
||||
* 半角转全角
|
||||
*
|
||||
* @param input String.
|
||||
* @return 全角字符串.
|
||||
*/
|
||||
public static String toSBC(String input) {
|
||||
return toSBC(input, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 半角转全角
|
||||
*
|
||||
* @param input String
|
||||
* @param notConvertSet 不替换的字符集合
|
||||
* @return 全角字符串.
|
||||
*/
|
||||
public static String toSBC(String input, Set<Character> notConvertSet) {
|
||||
char[] c = input.toCharArray();
|
||||
for (int i = 0; i < c.length; i++) {
|
||||
if (null != notConvertSet && notConvertSet.contains(c[i])) {
|
||||
// 跳过不替换的字符
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c[i] == ' ') {
|
||||
c[i] = '\u3000';
|
||||
} else if (c[i] < '\177') {
|
||||
c[i] = (char) (c[i] + 65248);
|
||||
|
||||
}
|
||||
}
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全角转半角
|
||||
*
|
||||
* @param input String.
|
||||
* @return 半角字符串
|
||||
*/
|
||||
public static String toDBC(String input) {
|
||||
return toDBC(input, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换全角为半角
|
||||
*
|
||||
* @param text 文本
|
||||
* @param notConvertSet 不替换的字符集合
|
||||
* @return 替换后的字符
|
||||
*/
|
||||
public static String toDBC(String text, Set<Character> notConvertSet) {
|
||||
char[] c = text.toCharArray();
|
||||
for (int i = 0; i < c.length; i++) {
|
||||
if (null != notConvertSet && notConvertSet.contains(c[i])) {
|
||||
// 跳过不替换的字符
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c[i] == '\u3000') {
|
||||
c[i] = ' ';
|
||||
} else if (c[i] > '\uFF00' && c[i] < '\uFF5F') {
|
||||
c[i] = (char) (c[i] - 65248);
|
||||
}
|
||||
}
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字金额大写转换 先写个完整的然后将如零拾替换成零
|
||||
*
|
||||
* @param n 数字
|
||||
* @return 中文大写数字
|
||||
*/
|
||||
public static String digitUppercase(double n) {
|
||||
String[] fraction = {"角", "分"};
|
||||
String[] digit = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
|
||||
String[][] unit = {{"元", "万", "亿"}, {"", "拾", "佰", "仟"}};
|
||||
|
||||
String head = n < 0 ? "负" : "";
|
||||
n = Math.abs(n);
|
||||
|
||||
String s = "";
|
||||
for (int i = 0; i < fraction.length; i++) {
|
||||
// 优化double计算精度丢失问题
|
||||
BigDecimal nNum = new BigDecimal(n);
|
||||
BigDecimal decimal = new BigDecimal(10);
|
||||
BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN);
|
||||
double d = scale.doubleValue();
|
||||
s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
|
||||
}
|
||||
if (s.length() < 1) {
|
||||
s = "整";
|
||||
}
|
||||
int integerPart = (int) Math.floor(n);
|
||||
|
||||
for (int i = 0; i < unit[0].length && integerPart > 0; i++) {
|
||||
String p = "";
|
||||
for (int j = 0; j < unit[1].length && n > 0; j++) {
|
||||
p = digit[integerPart % 10] + unit[1][j] + p;
|
||||
integerPart = integerPart / 10;
|
||||
}
|
||||
s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
|
||||
}
|
||||
return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package jnpf.util.excelv2.util;
|
||||
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.*;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
public class ExcelDateUtils extends DateUtils {
|
||||
public static String YYYY = "yyyy";
|
||||
public static String YYYY_MM_DD = "yyyy-MM-dd";
|
||||
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
private static final String[] parsePatterns = {
|
||||
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
|
||||
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
|
||||
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
|
||||
|
||||
/**
|
||||
* 获取当前Date型日期
|
||||
*
|
||||
* @return Date() 当前日期
|
||||
*/
|
||||
public static Date getNowDate() {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日期, 默认格式为yyyy-MM-dd
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static String getDate() {
|
||||
return dateTimeNow(YYYY_MM_DD);
|
||||
}
|
||||
|
||||
public static String getTime() {
|
||||
return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
|
||||
}
|
||||
|
||||
public static String dateTimeNow(final String format) {
|
||||
return parseDateToStr(format, new Date());
|
||||
}
|
||||
|
||||
public static String dateTime(final Date date) {
|
||||
return parseDateToStr(YYYY_MM_DD, date);
|
||||
}
|
||||
|
||||
public static String parseDateToStr(final String format, final Date date) {
|
||||
return new SimpleDateFormat(format).format(date);
|
||||
}
|
||||
|
||||
public static Date dateTime(final String format, final String ts) {
|
||||
try {
|
||||
return new SimpleDateFormat(format).parse(ts);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如20180808
|
||||
*/
|
||||
public static String dateTime() {
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyyMMdd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期型字符串转化为日期 格式
|
||||
*/
|
||||
public static Date parseDate(Object str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return parseDate(str.toString(), parsePatterns);
|
||||
} catch (ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算时间差
|
||||
*
|
||||
* @param endDate 最后时间
|
||||
* @param startTime 开始时间
|
||||
* @return 时间差(天/小时/分钟)
|
||||
*/
|
||||
public static String timeDistance(Date endDate, Date startTime) {
|
||||
long nd = 1000 * 24 * 60 * 60;
|
||||
long nh = 1000 * 60 * 60;
|
||||
long nm = 1000 * 60;
|
||||
// long ns = 1000;
|
||||
// 获得两个时间的毫秒时间差异
|
||||
long diff = endDate.getTime() - startTime.getTime();
|
||||
// 计算差多少天
|
||||
long day = diff / nd;
|
||||
// 计算差多少小时
|
||||
long hour = diff % nd / nh;
|
||||
// 计算差多少分钟
|
||||
long min = diff % nd % nh / nm;
|
||||
// 计算差多少秒//输出结果
|
||||
// long sec = diff % nd % nh % nm / ns;
|
||||
return day + "天" + hour + "小时" + min + "分钟";
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加 LocalDateTime ==> Date
|
||||
*/
|
||||
public static Date toDate(LocalDateTime temporalAccessor) {
|
||||
ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
|
||||
return Date.from(zdt.toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加 LocalDate ==> Date
|
||||
*/
|
||||
public static Date toDate(LocalDate temporalAccessor) {
|
||||
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
|
||||
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
|
||||
return Date.from(zdt.toInstant());
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,64 @@
|
||||
package jnpf.util.excelv2.util;
|
||||
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
public class ImageUtils {
|
||||
private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
|
||||
|
||||
public static byte[] getImage(String imagePath) {
|
||||
InputStream is = getFile(imagePath);
|
||||
try {
|
||||
return IOUtils.toByteArray(is);
|
||||
} catch (Exception e) {
|
||||
log.error("图片加载异常 {}", e);
|
||||
return null;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
public static InputStream getFile(String imagePath) {
|
||||
try {
|
||||
byte[] result = readFile(imagePath);
|
||||
result = Arrays.copyOf(result, result.length);
|
||||
return new ByteArrayInputStream(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取图片异常 {}", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件为字节数据
|
||||
*
|
||||
* @param url 地址
|
||||
* @return 字节数据
|
||||
*/
|
||||
public static byte[] readFile(String url) {
|
||||
InputStream in = null;
|
||||
try {
|
||||
// 网络地址
|
||||
URL urlObj = new URL(url);
|
||||
URLConnection urlConnection = urlObj.openConnection();
|
||||
urlConnection.setConnectTimeout(30 * 1000);
|
||||
urlConnection.setReadTimeout(60 * 1000);
|
||||
urlConnection.setDoInput(true);
|
||||
in = urlConnection.getInputStream();
|
||||
return IOUtils.toByteArray(in);
|
||||
} catch (Exception e) {
|
||||
log.error("访问文件异常 {}", e);
|
||||
return null;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package jnpf.util.excelv2.util;
|
||||
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Date;
|
||||
|
||||
public class ReflectUtils {
|
||||
private static final String SETTER_PREFIX = "set";
|
||||
|
||||
private static final String GETTER_PREFIX = "get";
|
||||
|
||||
private static final String CGLIB_CLASS_SEPARATOR = "$$";
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
|
||||
|
||||
/**
|
||||
* 调用Setter方法, 仅匹配方法名。
|
||||
* 支持多级,如:对象名.对象名.方法
|
||||
*/
|
||||
public static <E> void invokeSetter(Object obj, String propertyName, E value) {
|
||||
Object object = obj;
|
||||
String[] names = StringUtils.split(propertyName, ".");
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
if (i < names.length - 1) {
|
||||
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||
object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{});
|
||||
} else {
|
||||
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||
invokeMethodByName(object, setterMethodName, new Object[]{value});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E getFieldValue(final Object obj, final String fieldName) {
|
||||
Field field = getAccessibleField(obj, fieldName);
|
||||
if (field == null) {
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
return null;
|
||||
}
|
||||
E result = null;
|
||||
try {
|
||||
result = (E) field.get(obj);
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.error("不可能抛出的异常{}", e.getMessage());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
|
||||
*/
|
||||
public static <E> void setFieldValue(final Object obj, final String fieldName, final E value) {
|
||||
Field field = getAccessibleField(obj, fieldName);
|
||||
if (field == null) {
|
||||
// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
field.set(obj, value);
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.error("不可能抛出的异常: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接调用对象方法, 无视private/protected修饰符.
|
||||
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
|
||||
* 同时匹配方法名+参数类型,
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
|
||||
final Object[] args) {
|
||||
if (obj == null || methodName == null) {
|
||||
return null;
|
||||
}
|
||||
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
|
||||
if (method == null) {
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (E) method.invoke(obj, args);
|
||||
} catch (Exception e) {
|
||||
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
|
||||
throw convertReflectionExceptionToUnchecked(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接调用对象方法, 无视private/protected修饰符,
|
||||
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
|
||||
* 只匹配函数名,如果有多个同名函数调用第一个。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
|
||||
Method method = getAccessibleMethodByName(obj, methodName, args.length);
|
||||
if (method == null) {
|
||||
// 如果为空不报错,直接返回空。
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// 类型转换(将参数数据类型转换为目标方法参数类型)
|
||||
Class<?>[] cs = method.getParameterTypes();
|
||||
for (int i = 0; i < cs.length; i++) {
|
||||
if (args[i] != null && !args[i].getClass().equals(cs[i])) {
|
||||
if (cs[i] == String.class) {
|
||||
args[i] = CellValueConvert.toStr(args[i]);
|
||||
if (StringUtils.endsWith((String) args[i], ".0")) {
|
||||
args[i] = StringUtils.substringBefore((String) args[i], ".0");
|
||||
}
|
||||
} else if (cs[i] == Integer.class) {
|
||||
args[i] = CellValueConvert.toInt(args[i]);
|
||||
} else if (cs[i] == Long.class) {
|
||||
args[i] = CellValueConvert.toLong(args[i]);
|
||||
} else if (cs[i] == Double.class) {
|
||||
args[i] = CellValueConvert.toDouble(args[i]);
|
||||
} else if (cs[i] == Float.class) {
|
||||
args[i] = CellValueConvert.toFloat(args[i]);
|
||||
} else if (cs[i] == Date.class) {
|
||||
if (args[i] instanceof String) {
|
||||
args[i] = ExcelDateUtils.parseDate(args[i]);
|
||||
} else {
|
||||
args[i] = DateUtil.getJavaDate((Double) args[i]);
|
||||
}
|
||||
} else if (cs[i] == boolean.class || cs[i] == Boolean.class) {
|
||||
args[i] = CellValueConvert.toBool(args[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (E) method.invoke(obj, args);
|
||||
} catch (Exception e) {
|
||||
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
|
||||
throw convertReflectionExceptionToUnchecked(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
*/
|
||||
public static Field getAccessibleField(final Object obj, final String fieldName) {
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(fieldName, "fieldName can't be blank");
|
||||
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
|
||||
try {
|
||||
Field field = superClass.getDeclaredField(fieldName);
|
||||
makeAccessible(field);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
* 匹配函数名+参数类型。
|
||||
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
|
||||
*/
|
||||
public static Method getAccessibleMethod(final Object obj, final String methodName,
|
||||
final Class<?>... parameterTypes) {
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(methodName, "methodName can't be blank");
|
||||
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
|
||||
try {
|
||||
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
|
||||
makeAccessible(method);
|
||||
return method;
|
||||
} catch (NoSuchMethodException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
* 只匹配函数名。
|
||||
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
|
||||
*/
|
||||
public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) {
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(methodName, "methodName can't be blank");
|
||||
for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {
|
||||
Method[] methods = searchType.getDeclaredMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) {
|
||||
makeAccessible(method);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||
*/
|
||||
public static void makeAccessible(Method method) {
|
||||
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
|
||||
&& !method.isAccessible()) {
|
||||
method.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||
*/
|
||||
public static void makeAccessible(Field field) {
|
||||
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
|
||||
|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将反射时的checked exception转换为unchecked exception.
|
||||
*/
|
||||
public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) {
|
||||
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|
||||
|| e instanceof NoSuchMethodException) {
|
||||
return new IllegalArgumentException(msg, e);
|
||||
} else if (e instanceof InvocationTargetException) {
|
||||
return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
|
||||
}
|
||||
return new RuntimeException(msg, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package jnpf.util.excelv2.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public final class SpringBeanUtils implements BeanFactoryPostProcessor {
|
||||
/**
|
||||
* Spring 应用上下文环境
|
||||
*/
|
||||
private static volatile ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
SpringBeanUtils.beanFactory = beanFactory;
|
||||
log.info("SpringBeanUtils 初始化完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 BeanFactory 是否已初始化
|
||||
* @throws IllegalStateException 如果未初始化则抛出异常
|
||||
*/
|
||||
private static void checkInitialized() {
|
||||
if (beanFactory == null) {
|
||||
throw new IllegalStateException("SpringBeanUtils 尚未初始化,请确保在 Spring 容器启动后再使用");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getBean(String name) throws BeansException {
|
||||
checkInitialized();
|
||||
try {
|
||||
return (T) beanFactory.getBean(name);
|
||||
} catch (BeansException e) {
|
||||
log.error("获取 Bean 失败,name: {}", name, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static <T> T getBean(Class<T> clz) throws BeansException {
|
||||
checkInitialized();
|
||||
try {
|
||||
T result = beanFactory.getBean(clz);
|
||||
log.debug("成功获取 Bean: {}", clz.getName());
|
||||
return result;
|
||||
} catch (BeansException e) {
|
||||
log.error("获取 Bean 失败,class: {}", clz.getName(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果 BeanFactory 包含一个与所给名称匹配的 bean 定义,则返回 true
|
||||
*
|
||||
* @param name bean 名称
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean containsBean(String name) {
|
||||
checkInitialized();
|
||||
return beanFactory.containsBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断以给定名字注册的 bean 定义是一个 singleton 还是一个 prototype。如果与给定名字相应的 bean 定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
|
||||
*
|
||||
* @param name bean 名称
|
||||
* @return boolean true 如果是 singleton,false 如果是 prototype
|
||||
* @throws NoSuchBeanDefinitionException 如果找不到对应的 bean 定义
|
||||
*/
|
||||
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
|
||||
checkInitialized();
|
||||
return beanFactory.isSingleton(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注册对象的类型
|
||||
*
|
||||
* @param name bean 名称
|
||||
* @return Class 注册对象的类型
|
||||
* @throws NoSuchBeanDefinitionException 如果找不到对应的 bean 定义
|
||||
*/
|
||||
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
|
||||
checkInitialized();
|
||||
return beanFactory.getType(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定的 bean 名字在 bean 定义中有别名,则返回这些别名
|
||||
*
|
||||
* @param name bean 名称
|
||||
* @return 别名数组
|
||||
* @throws NoSuchBeanDefinitionException 如果找不到对应的 bean 定义
|
||||
*/
|
||||
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
|
||||
checkInitialized();
|
||||
return beanFactory.getAliases(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 aop 代理对象
|
||||
* <p>注意:需要在配置类上添加@EnableAspectJAutoProxy(exposeProxy=true)才能正常使用</p>
|
||||
*
|
||||
* @param invoker 调用对象
|
||||
* @return AOP 代理对象
|
||||
* @throws IllegalStateException 如果未暴露代理对象
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getAopProxy(T invoker) {
|
||||
checkInitialized();
|
||||
Object proxy = AopContext.currentProxy();
|
||||
if (proxy == null) {
|
||||
throw new IllegalStateException("无法获取 AOP 代理,请确保已配置 exposeProxy=true,例如:@EnableAspectJAutoProxy(exposeProxy=true)");
|
||||
}
|
||||
log.debug("成功获取 AOP 代理对象:{}", invoker.getClass().getName());
|
||||
return (T) proxy;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user