commit
Some checks failed
API接口参数变更检测 / api-param-check (push) Has been cancelled

This commit is contained in:
2026-06-05 16:18:40 +08:00
parent 1ca34c6bb2
commit 3cba3bb74e
4393 changed files with 450030 additions and 103 deletions

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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("^整$", "零元整");
}
}

View File

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

View File

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

View File

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

View File

@@ -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 如果是 singletonfalse 如果是 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;
}
}