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,71 @@
package jnpf.util;
import cn.xuyanwu.spring.file.storage.FileInfo;
import cn.xuyanwu.spring.file.storage.MockMultipartFile;
import jnpf.constant.FileTypeConstant;
import jnpf.file.FileApi;
import jnpf.file.FileUploadApi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.Base64;
import java.util.UUID;
/**
* base64工具
*
* @author yanwenfu
* @create 2025-09-08
*/
@Component
@Slf4j
public class Base64Util {
@Autowired
private FileUploadApi fileUploadApi;
@Autowired
private FileApi fileApi;
/**
* 将 Base64 转换为图片文件并上传
* @param base64Str Base64 字符串
* @return 上传后的 URL
*/
public String convertAndUpload(String base64Str) {
try {
// 1. 获取系统临时目录
String tempDir = System.getProperty("java.io.tmpdir");
// 文件名用时间戳,避免重复
String suffix = ".jpg";
String fileName = "temp_" + System.currentTimeMillis() + suffix;
File file = new File(tempDir, fileName);
// 2. Base64 解码并写入文件
byte[] imageBytes = Base64.getDecoder().decode(base64Str);
try (OutputStream out = new FileOutputStream(file)) {
out.write(imageBytes);
}
// 3. 上传
String url = uploadFile(file, suffix);
// 4. 删除临时文件(可选,看你是否需要保留)
file.delete();
return url;
} catch (Exception e) {
log.error("转换并上传base64图片失败, {}", e.getMessage());
return null;
}
}
private String uploadFile(File file, String suffix) throws IOException {
FileInputStream input = new FileInputStream(file);
String uuid = UUID.randomUUID().toString();
MultipartFile multiFile = new MockMultipartFile(uuid + TemplateExcelUtils.SUFFIX, file.getName(), MediaType.MULTIPART_FORM_DATA_VALUE, input);
input.close();
FileInfo fileInfo = fileUploadApi.uploadFileCustomName(multiFile, fileApi.getPath(FileTypeConstant.TEMPORARY), uuid + suffix);
return fileInfo.getUrl().replace(ConstantUtil.EXTRA_PATH, "");
}
}

View File

@@ -0,0 +1,104 @@
package jnpf.util;
import jnpf.base.UserInfo;
import jnpf.config.ConfigValueUtil;
import jnpf.database.model.TenantVO;
import jnpf.database.util.TenantDataSourceUtil;
import jnpf.exception.LoginException;
import jnpf.util.data.DataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import java.util.Objects;
/**
* 租户工具类
*
* @author yanwenfu
* @create 2023-11-08
*/
@Component("ftbCustomTenantUtil")
@Slf4j
public class CustomTenantUtil {
@Autowired
private ConfigValueUtil configValueUtil;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private AuthUtil authUtil;
private static final String SCHEDULETASK_TOKEN = "scheduletask::fill::token::%s::%s";
public void checkOutTenant(String tenantId) {
if (configValueUtil.isMultiTenancy()) {
// 判断是不是从外面直接请求
if (StringUtil.isNotEmpty(tenantId)) {
//切换成租户库
try {
TenantDataSourceUtil.switchTenant(tenantId);
} catch (LoginException e) {
// throw new RuntimeException("切换租户失败");
log.error("切换租户失败, 租户id: {}", tenantId);
}
} else {
UserInfo userInfo = UserProvider.getUser();
Assert.notNull(userInfo.getUserId(), "缺少租户信息");
DataSourceContextHolder.setDatasource(userInfo.getTenantId(), userInfo.getTenantDbConnectionString(), userInfo.isAssignDataSource());
}
}
}
public void checkOutTenant(String tenantId, String userId) {
if (configValueUtil.isMultiTenancy()) {
// 判断是不是从外面直接请求
if (StringUtil.isNotEmpty(tenantId)) {
TenantVO tenantVO;
try {
tenantVO = TenantDataSourceUtil.getRemoteTenantInfo(tenantId);
TenantDataSourceUtil.switchTenant(tenantId, tenantVO);
} catch (LoginException e1) {
throw new RuntimeException("切换租户失败");
}
if (StringUtil.isEmpty(userId)) {
log.error("当前切库用户为空");
return;
}
String tokenKey = String.format(SCHEDULETASK_TOKEN, tenantId, userId);
Object tokenObj = redisTemplate.opsForValue().get(tokenKey);
String token = Objects.isNull(tokenObj) ? null : (String) tokenObj;
UserInfo userInfo = new UserInfo();
userInfo.setUserId(userId);
userInfo.setToken(token);
userInfo.setTenantDbConnectionString(tenantVO.getDbName());
userInfo.setAssignDataSource(DataSourceContextHolder.isAssignDataSource());
userInfo.setTenantId(tenantId);
if (StringUtil.isNotBlank(token) && UserProvider.isValidToken(token)) {
UserProvider.setLocalLoginUser(userInfo);
return;
}
try {
token = authUtil.loginTempUser(userId, tenantId);
} catch (Exception e) {
throw new IllegalStateException("生成token失败", e);
}
// 检查 token 是否为空
if (token == null || token.isEmpty()) {
log.error("当前用户为[{}],租户为[{}]", userId, tenantId);
throw new IllegalStateException("Token is empty or null");
}
redisTemplate.opsForValue().set(tokenKey, token);
// 设置请求头中的 Token
userInfo.setToken(token);
UserProvider.setLocalLoginUser(userInfo);
} else {
UserInfo userInfo = UserProvider.getUser();
Assert.notNull(userInfo.getUserId(), "缺少租户信息");
DataSourceContextHolder.setDatasource(userInfo.getTenantId(), userInfo.getTenantDbConnectionString(), userInfo.isAssignDataSource());
}
}
}
}

View File

@@ -0,0 +1,149 @@
package jnpf.util;
import cn.hutool.core.date.DateTime;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.Objects;
@Slf4j
public class DateConvertUtil {
/**
* 计算时间差
*
* @param startTime 开始时间
* @param endTime 结束时间
* @param returnType 返回值类型1-分钟 2- 小时 3-天)
* @param scale 保留小数位
* @param attendanceRatio 换算比(小时折算天)
* @return
*/
public static BigDecimal dateConvert(Date startTime, Date endTime, Integer returnType, Integer scale, BigDecimal attendanceRatio) {
long diffMillis = endTime.getTime() - startTime.getTime();
BigDecimal returnData = BigDecimal.ZERO;
switch (returnType) {
case 1: {
returnData = new BigDecimal(diffMillis).divide(new BigDecimal(60 * 1000), scale, RoundingMode.HALF_UP);
break;
}
case 2: {
returnData = new BigDecimal(diffMillis).divide(new BigDecimal(60 * 60 * 1000), scale, RoundingMode.HALF_UP);
break;
}
case 3: {
returnData = new BigDecimal(diffMillis).divide(attendanceRatio.multiply(new BigDecimal(60 * 60 * 1000)), scale, RoundingMode.HALF_UP);
break;
}
}
return returnData;
}
/**
* 分钟换位
*
* @param number 分钟数
* @param returnType 返回值类型1- 小时 2-天)
* @param scale 保留小数位
* @param attendanceRatio 换算比(小时折算天)
* @return
*/
public static BigDecimal minuteConvert(BigDecimal number, Integer returnType, Integer scale, BigDecimal attendanceRatio) {
BigDecimal returnData = BigDecimal.ZERO;
switch (returnType) {
case 1: {
returnData = number.divide(new BigDecimal(60), scale, RoundingMode.HALF_UP);
break;
}
case 2: {
returnData = number.divide(new BigDecimal(60).multiply(attendanceRatio), scale, RoundingMode.HALF_UP);
break;
}
}
return returnData;
}
/**
* 秒换位
*
* @param number 秒数
* @param returnType 返回值类型1- 分钟 2-小时 3-天)
* @param scale 保留小数位
* @param attendanceRatio 换算比(小时折算天)
* @return
*/
public static BigDecimal secondConvert(Integer number, Integer returnType, Integer scale, BigDecimal attendanceRatio) {
BigDecimal returnData = BigDecimal.ZERO;
switch (returnType) {
case 1: {
returnData = new BigDecimal(number).divide(new BigDecimal(60), scale, RoundingMode.HALF_UP);
break;
}
case 2: {
returnData = new BigDecimal(number).divide(new BigDecimal(60 * 60), scale, RoundingMode.HALF_UP);
break;
}
case 3: {
returnData = new BigDecimal(number).divide(new BigDecimal(60 * 60).multiply(attendanceRatio), scale, RoundingMode.HALF_UP);
break;
}
}
return returnData;
}
public static HashMap<String, DateTime> setOverlap(DateTime startDate1,
DateTime endDate1, DateTime startDate2, DateTime endDate2, boolean isStrict) throws Exception {
HashMap<String, DateTime> intersection = new HashMap<>();
if (endDate1.compareTo(startDate1) < 0) {
return null;
}
if (endDate2.compareTo(startDate2) < 0) {
return null;
}
if (isStrict) {
if (!(endDate1.getTime() <= startDate2.getTime() || startDate1.getTime() >= endDate2.getTime())) {
//存在交集 取 两个时间中最大的开始时间和最小的结束时间
intersection.put("startDate", startDate1.getTime() < startDate2.getTime() ? startDate2 : startDate1);
intersection.put("endDate", endDate1.getTime() < endDate2.getTime() ? endDate1 : endDate2);
}
} else {
if (!(endDate1.getTime() < startDate2.getTime() || startDate1.getTime() > endDate2.getTime())) {
//存在交集 取 两个时间中最大的开始时间和最小的结束时间
intersection.put("startDate", startDate1.getTime() < startDate2.getTime() ? startDate2 : startDate1);
intersection.put("endDate", endDate1.getTime() < endDate2.getTime() ? endDate1 : endDate2);
}
}
return intersection;
}
/**
* 判断2个时间段是否有重叠交集
*
* @param startDate1 时间段1开始时间
* @param endDate1 时间段1结束时间
* @param startDate2 时间段2开始时间
* @param endDate2 时间段2结束时间
* @param isStrict 是否严格重叠true 严格没有任何相交或相等false 不严格,可以首尾相等
* @return ashMap<String, DateTime> key startDate endDate
*/
public static HashMap<String, DateTime> getOverlap(DateTime startDate1,DateTime endDate1, DateTime startDate2, DateTime endDate2, boolean isStrict) throws Exception {
Objects.requireNonNull(startDate1, "startDate1");
Objects.requireNonNull(endDate1, "endDate1");
Objects.requireNonNull(startDate2, "startDate2");
Objects.requireNonNull(endDate2, "endDate2");
return setOverlap(startDate1, endDate1, startDate2, endDate2, isStrict);
}
public static void main(String[] args) {
DateTime startDate1 = new DateTime(DateUtil.stringToDates("2024-01-28"));
DateTime endDate1 = new DateTime(DateUtil.stringToDates("2024-01-31"));
DateTime startDate2 = new DateTime(DateUtil.stringToDates("2024-01-29"));
DateTime endDate2 = new DateTime(DateUtil.stringToDates("2024-02-28"));
try {
HashMap<String, DateTime> hashMap = getOverlap(startDate1, endDate1, startDate2, endDate2, false);
log.error("交叉数据:{}", JSON.toJSON(hashMap));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,86 @@
package jnpf.util;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.time.DateUtils;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
/**
* 时间范围操作类
* @author Lengyunxiong
* @date 2023/3/09 16:31
*/
@Setter
@Getter
@Data
public class DateRange {
private Date start;
private Date end;
public DateRange() {
super();
}
public DateRange(Date start, Date end) {
super();
this.start = start;
this.end = end;
}
/**
* 判断时间范围是否不为空,即开始时间不在结束时间之后
*/
public boolean isNotEmptyDateRange() {
return start.before(end) || start.equals(end);
}
public List<DateRange> splitByMonth() {
List<DateRange> result = new ArrayList<>();
if(!this.isNotEmptyDateRange()) {
return result;
}
Calendar sc = Calendar.getInstance();
sc.setTime(start);
Calendar ec = Calendar.getInstance();
ec.setTime(end);
if (this.isSameMonth(sc, ec)) {
result.add(this);
return result;
}
while(sc.before(ec)) {
DateRange d = new DateRange();
d.setStart(sc.getTime());
//向时间点之后取整,这里以月取整,就是下月初第一天零点
//还有一个方法是truncate是向之前的时间取整和celling正好相反
//为什么celling有向后取整的意思因为吊灯天花板之类的英文就是celling
sc = DateUtils.ceiling(sc, Calendar.MONDAY);
//这里主要考虑最后一个月的情况这个if其实可以提到while外面性能会更好一点
//我懒得提了
if(sc.before(ec)) {
d.setEnd(sc.getTime());
} else {
d.setEnd(ec.getTime());
}
result.add(d);
}
return result;
}
private boolean isSameMonth(final Calendar cal1, final Calendar cal2) {
return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)
&& cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
}
}

View File

@@ -0,0 +1,196 @@
package jnpf.util;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import jnpf.base.ActionResult;
import jnpf.base.DataInterFaceApi;
import jnpf.base.DictionaryDataApi;
import jnpf.base.entity.DictionaryDataEntity;
import jnpf.base.model.datainterface.DataInterfaceActionVo;
import jnpf.util.context.SpringContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author JNPF开发平台组
* @version V3.1.0
* @copyright 引迈信息技术有限公司https://www.jnpfsoft.com
* @date 2021/3/16
*/
@Component
public class DynDicUtil {
@Autowired
private RedisUtil redisUtil;
@Autowired
private CacheKeyUtil cacheKeyUtil;
@Autowired
private DictionaryDataApi dictionaryDataApi;
@Autowired
private DataInterFaceApi dataInterfaceApi;
public final String regEx = "[\\[\\]\"]";
/**
* 获取数据字典数据
*
* @param feild
* @return
*/
public String getDicName(String feild,String parentId) {
if (redisUtil.exists(cacheKeyUtil.getDictionary() + feild)) {
return redisUtil.getString(cacheKeyUtil.getDictionary() + feild).toString();
}
if (StringUtil.isNotEmpty(feild)) {
//去除中括号以及双引号
feild = feild.replaceAll(regEx, "");
//判断多选框
String[] feilds = feild.split(",");
if (feilds.length > 1) {
StringBuilder feildsValue = new StringBuilder();
DictionaryDataEntity dictionaryDataEntity;
for (String feil : feilds) {
dictionaryDataEntity = dictionaryDataApi.getSwapInfo(feil,parentId);
if (dictionaryDataEntity != null) {
feildsValue.append(dictionaryDataEntity.getFullName() + ",");
}
}
String finalValue;
if (StringUtil.isEmpty(feildsValue) || feildsValue.equals("")) {
finalValue = feildsValue.toString();
} else {
finalValue = feildsValue.substring(0, feildsValue.length() - 1);
}
redisUtil = SpringContext.getBean(RedisUtil.class);
redisUtil.insert(cacheKeyUtil.getDictionary() + feild, finalValue, 20);
return finalValue;
}
DictionaryDataEntity dictionaryDataentity = dictionaryDataApi.getSwapInfo(feild,parentId);
if (dictionaryDataentity != null) {
redisUtil = SpringContext.getBean(RedisUtil.class);
redisUtil.insert(cacheKeyUtil.getDictionary() + feild, dictionaryDataentity.getFullName(), 20);
return dictionaryDataentity.getFullName();
}
return feild;
}
return feild;
}
/**
* 获取远端数据
*
* @param urlId
* @param label
* @param value
* @param feildValue
* @return
* @throws IOException
*/
public String getDynName(String urlId, String label, String value, String feildValue) {
String rediskey = cacheKeyUtil.getDynamic() + "_" + urlId + "_" + feildValue;
if (redisUtil.exists(rediskey)) {
return redisUtil.getString(rediskey).toString();
}
if (StringUtil.isNotEmpty(feildValue)) {
//去除中括号以及双引号
feildValue = feildValue.replaceAll(regEx, "");
//获取远端数据
Map<String , String> a = new HashMap<>();
ActionResult object = dataInterfaceApi.getDataInterfaceInfo(urlId);
if (object.getData() != null && object.getData() instanceof DataInterfaceActionVo) {
DataInterfaceActionVo vo= (DataInterfaceActionVo) object.getData();
List<Map<String, Object>> dataList = (List<Map<String, Object>>) vo.getData();
//判断是否多选
String[] feildValues = feildValue.split(",");
if (feildValues.length > 0) {
//转换的真实值
StringBuilder feildVa = new StringBuilder();
for (String feild : feildValues) {
for (Map<String, Object> data : dataList) {
if (String.valueOf(data.get(value)).equals(feild)) {
feildVa.append(data.get(label) + ",");
}
}
}
String finalValue;
if (StringUtil.isEmpty(feildVa) || feildVa.equals("")) {
finalValue = feildVa.toString();
} else {
finalValue = feildVa.substring(0, feildVa.length() - 1);
}
redisUtil = SpringContext.getBean(RedisUtil.class);
redisUtil.insert(rediskey, finalValue, 20);
return finalValue;
}
for (Map<String, Object> data : dataList) {
if (feildValue.equals(String.valueOf(data.get(value)))) {
redisUtil = SpringContext.getBean(RedisUtil.class);
redisUtil.insert(rediskey, data.get(label).toString(), 20);
return data.get(label).toString();
}
return feildValue;
}
}
return feildValue;
}
return feildValue;
}
/**
* 获取远端数据
*
* @param urlId
* @param name
* @param id
* @param children
* @param feildValue
* @return
*/
public String getDynName(String urlId, String name, String id, String children, String feildValue) {
String rediskey = cacheKeyUtil.getDynamic() + "_" + urlId + "_" + feildValue;
if (redisUtil.exists(rediskey)) {
return redisUtil.getString(rediskey).toString();
}
List<String> result = new ArrayList<>();
if (StringUtil.isNotEmpty(feildValue)) {
Map<String , String> a = new HashMap<>();
ActionResult object = dataInterfaceApi.getDataInterfaceInfo(urlId);
if (object.getData() != null && object.getData() instanceof DataInterfaceActionVo) {
DataInterfaceActionVo vo= (DataInterfaceActionVo) object.getData();
List<Map<String, Object>> dataList = (List<Map<String, Object>>) vo.getData();
JSONArray dataAll = JsonUtil.getListToJsonArray(dataList);
List<Map<String, String>> list = new ArrayList<>();
treeToList(id, name, children, dataAll, list);
String value = feildValue.replaceAll("\\[", "").replaceAll("\\]", "");
result = list.stream().filter(t -> value.contains(String.valueOf(t.get(id)))).map(t -> String.valueOf(t.get(name))).collect(Collectors.toList());
}
}
return String.join(",", result);
}
/**
* 树转成list
**/
private void treeToList(String id, String fullName, String children, JSONArray data, List<Map<String, String>> result) {
for (int i = 0; i < data.size(); i++) {
JSONObject ob = data.getJSONObject(i);
Map<String, String> tree = new HashMap<>(16);
tree.put(id, String.valueOf(ob.get(id)));
tree.put(fullName, String.valueOf(ob.get(fullName)));
result.add(tree);
if (ob.get(children) != null) {
JSONArray childArray = ob.getJSONArray(children);
treeToList(id, fullName, children, childArray, result);
}
}
}
}

View File

@@ -0,0 +1,180 @@
package jnpf.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.http.Header;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import jnpf.handler.CustomCellStyleHandler;
import jnpf.handler.CustomCellWriteHandler;
import jnpf.handler.CustomRowHeightHandler;
import jnpf.model.cultivate.vo.teaching.SkillCountVo;
import jnpf.model.cultivate.vo.teaching.SummaryPageListVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
/**
* easyExcel导入导出工具类
* @author yier
*/
public class EasyExcelUtil {
private static final Logger logger = LoggerFactory.getLogger(EasyExcelUtil.class);
public static <T> void simpleWrite(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) throws IOException {
response.setContentType("application/octet-stream");
response.setCharacterEncoding("utf-8");
OutputStream outputStream = response.getOutputStream();
try (outputStream) {
String fileName = URLEncoder.encode(sheetName, StandardCharsets.UTF_8);
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(outputStream, clazz).sheet(sheetName).doWrite(list);
}
}
/**
* Excel导出功能
*
* @param response 相应数据
* @param fileName 文件名
* @param sheetName sheet名
* @param cls 对象属性名的值
*/
public static <T> void export(HttpServletResponse response, String fileName, String sheetName, Class<T> cls, List<T> list) throws Exception {
ServletOutputStream outputStream = response.getOutputStream();
response.setCharacterEncoding("utf-8");
response.setHeader(Header.CONTENT_DISPOSITION.toString(), "attachment; filename=".concat(URLEncoder.encode(fileName, StandardCharsets.UTF_8)));
response.setContentType("application/octet-stream");
EasyExcel.write(outputStream, cls).sheet(sheetName).doWrite(list);
}
/**
* 动态表头导出
* @param headList 表头
* @param dataList 导出数据
* @param filename 文件名
* @param sheetName sheet名称
*/
public static void dynamicTableExport(List<List<String>> headList, List<List<String>> dataList, String filename, String sheetName) {
ExcelWriter excelWriter = null;
HttpServletResponse response = ServletUtil.getResponse();
try {
response.setCharacterEncoding("utf-8");
response.setContentType("application/octet-stream");
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8));
excelWriter = EasyExcel.write()
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.file(response.getOutputStream())
.inMemory(true).build();
WriteSheet writeSheet = EasyExcel.writerSheet(0, sheetName)
.head(headList).build();
excelWriter.write(dataList, writeSheet);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (excelWriter != null) {
excelWriter.finish();
}
}
}
/**
* 获取动态表头
* @param dataList 数据集合
* @param list 已存在的动态表头
* @return 返回动态表头
*/
public static List<List<String>> getHeaders(List<SummaryPageListVo> dataList, List<List<String>> list) {
//获取最长的技能点集合
List<SkillCountVo> skillList = dataList.stream().map(SummaryPageListVo::getSkillList).max(Comparator.comparingInt(List::size)).orElse(new LinkedList<>());
if (CollUtil.isNotEmpty(dataList) && CollUtil.isNotEmpty(skillList)) {
for (SkillCountVo skillCountVo : skillList) {
List<String> head = new ArrayList<>();
head.add(skillCountVo.getName());
list.add(head);
}
}
return list;
}
/**
* 获取表内容数据
* @param dataList 数据集合
* @return 表内容数据
*/
public static List<List<Object>> getDataList(List<SummaryPageListVo> dataList) {
List<List<Object>> list = ListUtils.newArrayList();
if (CollUtil.isNotEmpty(dataList)) {
for (SummaryPageListVo rankingDTO : dataList) {
List<Object> data = ListUtils.newArrayList();
data.add(rankingDTO.getStoreName());
data.add(rankingDTO.getUserName());
data.add(rankingDTO.getPostName());
data.add(rankingDTO.getTotalCount());
if (CollUtil.isNotEmpty(rankingDTO.getSkillList())) {
for (SkillCountVo skillCountVo : rankingDTO.getSkillList()) {
data.add(skillCountVo.getCount());
}
}
list.add(data);
}
}
return list;
}
/**
* 导出排行榜数据
*
* @param list 导出数据
* @param headers 表头
* @param fileName 导出文件名称
* @param sheetName sheet名称
* @param response 响应对象
* @throws Exception 抛出异常
*/
public static void rankingExportData(List<List<Object>> list, List<List<String>> headers, String fileName,
String sheetName, HttpServletResponse response) throws Exception {
EasyExcelUtil.export(response, fileName + ".xlsx", sheetName + "排行", list, headers);
}
/**
* Excel导出功能(根据自定义表头)
*
* @param response 响应数据
* @param fileName 文件名
* @param sheetName sheet名
* @param list 数据列表
* @param headers 表头集合
*/
public static void export(HttpServletResponse response, String fileName, String sheetName,
List<List<Object>> list, List<List<String>> headers) throws Exception {
ServletOutputStream outputStream = response.getOutputStream();
response.setCharacterEncoding("utf-8");
response.setHeader(Header.CONTENT_DISPOSITION.toString(), "attachment; filename=".concat(URLEncoder.encode(fileName, StandardCharsets.UTF_8)));
response.setContentType("application/octet-stream");
EasyExcel.write(outputStream)
.head(headers)
.sheet(sheetName)
.registerWriteHandler(new CustomRowHeightHandler())
.registerWriteHandler(new CustomCellStyleHandler())
.registerWriteHandler(new CustomCellWriteHandler())
.doWrite(list);
}
}

View File

@@ -0,0 +1,473 @@
package jnpf.util;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import jnpf.onlinedev.util.onlineDevUtil.OnlineDatabaseUtils;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@Data
public class GenUtil {
/**
* 字段说明
*/
private String fieldName;
/**
* 运算符
*/
private String operator;
/**
* 逻辑拼接符号
*/
private String logic;
/**
* 组件标识
*/
private String jnpfKey;
/**
* 字段key
*/
private String field;
/**
* 自定义的值
*/
private String fieldValue;
/**
* 自定义的值2
*/
private String fieldValue2;
private List<String> selectIgnore;
/**
* 数据库类型
*/
private String dbType;
/**
* 日期格式
*/
private String format;
/**
* 数字精度
*/
private String precision;
/**
* @param wrapper wrapper对象
* @param fieldDb 数据库字段名实际包括前缀
* @return
*/
public QueryWrapper<?> solveValue(QueryWrapper<?> wrapper, String fieldDb) {
MyType myType = myControl(jnpfKey);
if ("||".equals(logic)) {
wrapper.or();
}
if (fieldValue == null) {
fieldValue = "";
}
try {
ArrayList splitKey = new ArrayList<String>() {{
add("date");
add("time");
add("numInput");
}};
if (splitKey.contains(jnpfKey) && "between".equals(operator)) {
List<String> data = JsonUtil.getJsonToList(fieldValue, String.class);
fieldValue = data.get(0);
fieldValue2 = data.get(1);
}
// 显示组织还是部门,全部拿最后一级
if (jnpfKey.equals("currOrganize") && StringUtils.isNoneBlank(fieldValue)) {
List<String> data = JsonUtil.getJsonToList(fieldValue, String.class);
fieldValue = data.get(data.size() - 1);
}
selectIgnore = new ArrayList<String>() {{
add("comSelect");
add("address");
add("cascader");
add("checkbox");
add("depSelect");
}};
myType.judge(wrapper, fieldDb);
return wrapper;
} catch (Exception e) {
return wrapper;
}
}
/**
* 判断控件的所属类型
*
* @param jnpfKey 控件标识
* @return 控件类型
*/
public MyType myControl(String jnpfKey) {
MyType myType = null;
switch (jnpfKey) {
/** 基础 */
case "comInput":
case "textarea":
case "billRule":
case "popupTableSelect":
case "relationForm":
case "relationFormAttr":
case "popupSelect":
case "popupAttr":
myType = new BasicControl();
break;
// 数字类型
case "numInput":
case "calculate":
myType = new NumControl();
break;
// 日期类型
case "date":
case "createTime":
case "modifyTime":
myType = new DateControl();
break;
// 时间类型
case "time":
myType = new TimeControl();
break;
// 下拉类型
default:
myType = new SelectControl();
}
return myType;
}
public void getNullWrapper(QueryWrapper<?> wrapper, String fieldDb) {
if ("||".equals(logic)) {
wrapper.or(t -> {
t.isNull(fieldDb);
t.or().eq(fieldDb, "");
t.or().eq(fieldDb, "[]");
});
} else {
wrapper.and(t -> {
t.isNull(fieldDb);
t.or().eq(fieldDb, "");
t.or().eq(fieldDb, "[]");
});
}
}
private void getNotNullWrapper(QueryWrapper<?> wrapper, String fieldDb) {
if ("||".equals(logic)) {
wrapper.or(t -> {
t.isNotNull(fieldDb);
t.ne(fieldDb, "");
t.ne(fieldDb, "[]");
});
} else {
wrapper.and(t -> {
t.isNotNull(fieldDb);
t.ne(fieldDb, "");
t.ne(fieldDb, "[]");
});
}
}
/**
* 基础类型
*/
class BasicControl extends MyType {
@Override
void judge(QueryWrapper<?> wrapper, String fieldDb) {
switch (operator) {
case "null":
getNullWrapper(wrapper, fieldDb);
break;
case "notNull":
getNotNullWrapper(wrapper, fieldDb);
break;
case "==":
wrapper.eq(fieldDb, fieldValue);
break;
case "<>":
wrapper.ne(fieldDb, fieldValue);
break;
case "like":
wrapper.like(fieldDb, fieldValue);
break;
case "notLike":
wrapper.notLike(fieldDb, fieldValue);
break;
}
}
}
class NumControl extends MyType {
@Override
void judge(QueryWrapper<?> wrapper, String fieldDb) {
BigDecimal num1 = new BigDecimal(fieldValue);
BigDecimal num2 = null;
if(fieldValue2!=null){
num2 = new BigDecimal(fieldValue2);
}
// 精度处理
String fieldPrecisionValue;
String fieldPrecisionValue2;
if(StringUtils.isNotBlank(precision)){
String zeroNum = "0."+ StringUtils.repeat("0", Integer.parseInt(precision));
DecimalFormat numFormat = new DecimalFormat(zeroNum);
fieldPrecisionValue = numFormat.format(new BigDecimal(fieldValue));
num1 = new BigDecimal(fieldPrecisionValue);
if(fieldValue2 != null ){
fieldPrecisionValue2 = numFormat.format(new BigDecimal(fieldValue2));
num2 = new BigDecimal(fieldPrecisionValue2);
}
}
switch (operator) {
case "null":
getNullWrapper(wrapper, fieldDb);
break;
case "notNull":
getNotNullWrapper(wrapper, fieldDb);
break;
case "==":
wrapper.eq(fieldDb, num1);
break;
case "<>":
wrapper.ne(fieldDb, num1);
break;
case ">":
wrapper.gt(fieldDb, num1);
break;
case "<":
wrapper.lt(fieldDb, num1);
break;
case ">=":
wrapper.ge(fieldDb, num1);
break;
case "<=":
wrapper.le(fieldDb, num1);
break;
case "between":
wrapper.between(fieldDb, num1, num2);
break;
}
}
}
class DateControl extends MyType {
@Override
void judge(QueryWrapper<?> wrapper, String fieldDb) {
long time = 0;
Date date = new Date();
if (StringUtils.isNoneBlank(fieldValue)) {
time = Long.parseLong(fieldValue);
date.setTime(time);
fieldValue = DateUtil.daFormat(date);
}
Date fieldValueDate = DateUtil.stringToDates(fieldValue);
switch (operator) {
case "null":
getNullWrapper(wrapper, fieldDb);
break;
case "notNull":
getNotNullWrapper(wrapper, fieldDb);
break;
case "==":
wrapper.between(fieldDb, fieldValueDate, new Date(time + 60 * 60 * 24 * 1000));
break;
case "<>":
wrapper.ne(fieldDb, fieldValueDate);
break;
case ">":
wrapper.gt(fieldDb, fieldValueDate);
break;
case "<":
wrapper.lt(fieldDb, fieldValueDate);
break;
case ">=":
wrapper.ge(fieldDb, fieldValueDate);
break;
case "<=":
wrapper.le(fieldDb, fieldValueDate);
break;
case "between":
long time2 = Long.parseLong(fieldValue2);
wrapper.between(fieldDb, fieldValueDate, new Date(time2 + 60 * 60 * 24 * 1000));
break;
}
}
}
class TimeControl extends MyType {
@Override
void judge(QueryWrapper<?> wrapper, String fieldDb) {
switch (operator) {
case "null":
getNullWrapper(wrapper, fieldDb);
break;
case "notNull":
getNotNullWrapper(wrapper, fieldDb);
break;
case "==":
wrapper.eq(fieldDb, fieldValue);
break;
case "<>":
wrapper.ne(fieldDb, fieldValue);
break;
case ">":
wrapper.gt(fieldDb, fieldValue);
break;
case "<":
wrapper.lt(fieldDb, fieldValue);
break;
case ">=":
wrapper.ge(fieldDb, fieldValue);
break;
case "<=":
wrapper.le(fieldDb, fieldValue);
break;
case "between":
wrapper.between(fieldDb, fieldValue, fieldValue2);
break;
}
}
}
/**
* 下拉控件类型
*/
class SelectControl extends MyType {
@Override
void judge(QueryWrapper<?> wrapper, String fieldDb) {
List<String> list = new ArrayList<>();
if (StringUtils.isNoneBlank(fieldValue) && fieldValue.charAt(0) == '[' && !selectIgnore.contains(jnpfKey)) {
list = JSONUtil.toList(fieldValue, String.class);
if (!Objects.equals(operator, "in") && !Objects.equals(operator, "notIn")) {
fieldValue = String.join(",", list);
}
}
if (selectIgnore.contains(jnpfKey)) {
if (StringUtils.isBlank(fieldValue)) {
fieldValue = "[]";
}
list = JsonUtil.getJsonToList(fieldValue, String.class);
}
switch (operator) {
case "null":
getNullWrapper(wrapper, fieldDb);
break;
case "notNull":
getNotNullWrapper(wrapper, fieldDb);
break;
case "==":
wrapper.eq(fieldDb, fieldValue);
break;
case "<>":
wrapper.ne(fieldDb, fieldValue);
break;
case "like":
wrapper.like(fieldDb, fieldValue);
break;
case "notLike":
wrapper.notLike(fieldDb, fieldValue);
break;
case "in":
if (list.size() > 0) {
List<String> finalList = list;
if ("||".equals(logic)) {
wrapper.or(t -> {
if (finalList.size() > 0) {
for (int i = 0; i < finalList.size(); i++) {
String value = finalList.get(i);
if (i == 0) {
t.like(fieldDb, value);
} else {
t.or().like(fieldDb, value);
}
}
}
});
} else {
wrapper.and(t -> {
if (finalList.size() > 0) {
for (int i = 0; i < finalList.size(); i++) {
String value = finalList.get(i);
if (i == 0) {
t.like(fieldDb, value);
} else {
t.or().like(fieldDb, value);
}
}
}
});
}
}
break;
case "notIn":
if (list.size() > 0) {
List<String> finalList1 = list;
if ("||".equals(logic)) {
wrapper.or(t -> {
if (finalList1.size() > 0) {
for (int i = 0; i < finalList1.size(); i++) {
String value = finalList1.get(i);
if (i == 0) {
t.notLike(fieldDb, value);
} else {
t.notLike(fieldDb, value);
}
}
}
});
} else {
wrapper.and(t -> {
if (finalList1.size() > 0) {
for (int i = 0; i < finalList1.size(); i++) {
String value = finalList1.get(i);
if (i == 0) {
t.notLike(fieldDb, value);
} else {
t.notLike(fieldDb, value);
}
}
}
});
}
}
break;
}
}
}
abstract class MyType {
abstract void judge(QueryWrapper<?> wrapper, String fieldDb);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,93 @@
package jnpf.util;
/**
* 返回状态码
*
* @author admin
*/
public class HttpStatus {
/**
* 操作成功
*/
public static final int SUCCESS = 200;
/**
* 对象创建成功
*/
public static final int CREATED = 201;
/**
* 请求已经被接受
*/
public static final int ACCEPTED = 202;
/**
* 操作已经执行成功,但是没有返回数据
*/
public static final int NO_CONTENT = 204;
/**
* 资源已被移除
*/
public static final int MOVED_PERM = 301;
/**
* 未登录
*/
public static final int NO_LOGIN = 302;
/**
* 重定向
*/
public static final int SEE_OTHER = 303;
/**
* 资源没有被修改
*/
public static final int NOT_MODIFIED = 304;
/**
* 参数列表错误(缺少,格式不匹配)
*/
public static final int BAD_REQUEST = 400;
/**
* 未授权
*/
public static final int UNAUTHORIZED = 401;
/**
* 访问受限,授权过期
*/
public static final int FORBIDDEN = 403;
/**
* 资源,服务未找到
*/
public static final int NOT_FOUND = 404;
/**
* 不允许的http方法
*/
public static final int BAD_METHOD = 405;
/**
* 资源冲突,或者资源被锁
*/
public static final int CONFLICT = 409;
/**
* 不支持的数据,媒体类型
*/
public static final int UNSUPPORTED_TYPE = 415;
/**
* 系统内部错误
*/
public static final int ERROR = 500;
/**
* 接口未实现
*/
public static final int NOT_IMPLEMENTED = 501;
}

View File

@@ -0,0 +1,173 @@
package jnpf.util;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HttpUtil {
private static final CloseableHttpClient httpclient = HttpClients.createDefault();
/**
* 发送HttpGet请求
*
* @param url
* @return
*/
public static String sendGet(String url) {
HttpGet httpget = new HttpGet(url);
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httpget);
} catch (IOException e1) {
e1.printStackTrace();
}
String result = null;
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
result = EntityUtils.toString(entity);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 发送HttpPost请求参数为map
*
* @param url
* @param map
* @return
*/
public static String sendPost(String url, Map<String, String> map) {
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
for (Map.Entry<String, String> entry : map.entrySet()) {
formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
HttpPost httppost = new HttpPost(url);
httppost.setEntity(entity);
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httppost);
} catch (IOException e) {
e.printStackTrace();
}
HttpEntity entity1 = response.getEntity();
String result = null;
try {
result = EntityUtils.toString(entity1);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 发送不带参数的HttpPost请求
*
* @param url
* @return
*/
public static String sendPost(String url) {
HttpPost httppost = new HttpPost(url);
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httppost);
} catch (IOException e) {
e.printStackTrace();
}
HttpEntity entity = response.getEntity();
String result = null;
try {
result = EntityUtils.toString(entity);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static String doPost2(String url, JSONObject param) {
HttpPost httpPost = null;
String result = null;
try {
HttpClient client = new DefaultHttpClient();
httpPost = new HttpPost(url);
if (param != null) {
StringEntity se = new StringEntity(param.toString(), "utf-8");
httpPost.setEntity(se); // post方法中加入json数据
httpPost.setHeader("Content-Type", "application/json");
httpPost.setHeader("Authorization", param.getString("authorization"));
}
HttpResponse response = client.execute(httpPost);
if (response != null) {
HttpEntity resEntity = response.getEntity();
if (resEntity != null) {
result = EntityUtils.toString(resEntity, "utf-8");
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return result;
}
// 将 JSON 字符串的字段首字母大写
public static String convertFirstLetterToUpperCase(String jsonString) {
StringBuilder result = new StringBuilder();
int index = 0;
while (index < jsonString.length()) {
char currentChar = jsonString.charAt(index);
if (currentChar == '"' || currentChar == '{' || currentChar == ',') {
result.append(currentChar);
index++;
} else {
int fieldStartIndex = index;
while (index < jsonString.length() && jsonString.charAt(index) != ':') {
index++;
}
int fieldEndIndex = index;
String fieldName = jsonString.substring(fieldStartIndex, fieldEndIndex).trim();
String upperCaseFieldName = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
result.append(upperCaseFieldName);
while (index < jsonString.length() && jsonString.charAt(index) != ',' && jsonString.charAt(index) != '}') {
result.append(jsonString.charAt(index));
index++;
}
}
}
return result.toString();
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2022-2024 Ponfee (http://www.ponfee.cn/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jnpf.util;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Multi Thread executor
*
* @author Ponfee
*/
public class MultithreadExecutors {
public static <T> void run(Collection<T> coll, Consumer<T> action, Executor executor) {
run(coll, action, executor, 2);
}
/**
* Run async, action the T collection
*
* @param coll the collection
* @param action the action
* @param executor the executor
* @param dataSizeThreshold the dataSizeThreshold
* @param <T> the collection element type
*/
public static <T> void run(Collection<T> coll, Consumer<T> action, Executor executor, int dataSizeThreshold) {
if (coll == null || coll.isEmpty()) {
return;
}
if (dataSizeThreshold <= 0 || coll.size() < dataSizeThreshold) {
coll.forEach(action);
return;
}
coll.stream()
.map(e -> CompletableFuture.runAsync(() -> action.accept(e), executor))
.collect(Collectors.toList())
.forEach(CompletableFuture::join);
}
public static <T, U> List<U> call(Collection<T> coll, Function<T, U> mapper, Executor executor) {
return call(coll, mapper, executor, 2);
}
/**
* Convert collection element data
*
* @param coll the collection
* @param mapper the mapper
* @param executor the executor
* @param dataSizeThreshold the executor
* @param <T> the source collection element type
* @param <U> the target collection element type
* @return target collection
*/
public static <T, U> List<U> call(Collection<T> coll, Function<T, U> mapper, Executor executor, int dataSizeThreshold) {
if (coll == null) {
return null;
}
if (coll.isEmpty()) {
return Collections.emptyList();
}
if (dataSizeThreshold <= 0 || coll.size() < dataSizeThreshold) {
return coll.stream().map(mapper).collect(Collectors.toList());
}
return coll.stream()
.map(e -> CompletableFuture.supplyAsync(() -> mapper.apply(e), executor))
.collect(Collectors.toList())
.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,24 @@
package jnpf.util;
import jnpf.exception.HandleException;
/**
* 数字转换类
*
* @author yanwenfu
* @create 2025-04-17
*/
public class NumberUtils {
public static float safeToFloat(Object obj) throws HandleException {
if (obj instanceof Number) {
Number num = (Number) obj;
return num.floatValue();
}
try {
return Float.parseFloat(String.valueOf(obj));
} catch (NumberFormatException e) {
throw new HandleException("转换失败");
}
}
}

View File

@@ -0,0 +1,280 @@
package jnpf.util;
import java.util.*;
/**
* 空判断工具
*
* @author tq
*/
public class OptionalUtils {
/**
* 或 处理
* 所有if里只要有一个满足条件最后才会执行done方法
* 每个if满足条件则会执行then方法
* <pre>
* <code>
* if(){
* then();
* } if(){
* then();
* } if(){
* then();
* }
* ...
* if(条件N){
* then();
* }
* if(任一满足){
* done();
* }
* 例子:
*
* OptionalUtils.or()
* .ifPresent(req.getProvinceCode()).then(() -> update.setProvinceCode(req.getProvinceCode()))
* .ifPresent(req.getCityCode()).then(() -> update.setCityCode(req.getCityCode()))
* .ifPresent(req.getDistrictCode()).then(() -> update.setDistrictCode(req.getDistrictCode()))
* .ifPresent(req.getCategoryId()).then(() -> update.setCategoryId(req.getCategoryId()))
* .ifPresent(req.getAddress()).then(() -> update.setAddress(req.getAddress()))
* .done(() -> {
* this.updateById(update);
* });
* </code>
* </pre>
*
* @param <T>
* @return
*/
public static <T> Condition<T> or() {
return new OrCondition<>();
}
/**
* 并且 处理
* 所有if里必须所有满足条件最后才会执行done方法
* 每个if满足条件则会执行then方法
* <pre>
* <code>
* if(条件1){
* then();
* }
* if(条件2){
* then();
* }
* if(条件3){
* then();
* }
* ...
* if(条件N){
* then();
* }
* if(所有满足){
* done();
* }
*
* 例子:
* OptionalUtils.and()
* .ifPresent(req.getProvinceCode()).then(() -> update.setProvinceCode(req.getProvinceCode()))
* .ifPresent(req.getCityCode()).then(() -> update.setCityCode(req.getCityCode()))
* .ifPresent(req.getDistrictCode()).then(() -> update.setDistrictCode(req.getDistrictCode()))
* .ifPresent(req.getCategoryId()).then(() -> update.setCategoryId(req.getCategoryId()))
* .ifPresent(req.getAddress()).then(() -> update.setAddress(req.getAddress()))
* .done(() -> {
* this.updateById(update);
* });
* </code>
* </pre>
*
* @param <T>
* @return
*/
public static <T> Condition<T> and() {
return new AndCondition<>();
}
public static <T> void ifPresent(T value, RunBlockNoReturnFunction<T> runBlock) {
if (!isEmpty(value)) {
runBlock.run(value);
}
}
public static <T, R> R ifPresent(T value, RunBlockReturnFunction<T, R> runBlock) {
if (!isEmpty(value)) {
return runBlock.run(value);
}
return null;
}
public static <T, R> R ifPresent(T value, R defaultValue, RunBlockReturnFunction<T, R> runBlock) {
if (!isEmpty(value)) {
return runBlock.run(value);
}
return defaultValue;
}
public static <T> void ifNotPresent(T value, String exceptionMsg) {
if (isEmpty(value)) {
throw new ServiceException(exceptionMsg);
}
}
public static <T> void ifNotPresent(T value, RunBlockNoParameterNoReturnFunction runBlock) {
if (isEmpty(value)) {
runBlock.run();
}
}
public static <T, R> R ifNotPresent(T value, RunBlockNoParameterReturnFunction<R> runBlock, R defaultValue) {
if (isEmpty(value)) {
return runBlock.run();
}
return defaultValue;
}
@FunctionalInterface
public interface RunBlockNoParameterReturnFunction<R> {
R run();
}
@FunctionalInterface
public interface RunBlockReturnFunction<T, R> {
R run(T t);
}
@FunctionalInterface
public interface RunBlockNoReturnFunction<T> {
void run(T t);
}
@FunctionalInterface
public interface RunBlockNoParameterNoReturnFunction {
void run();
}
public static class Execute<T> {
private final T value;
private final Condition<T> condition;
public Execute(T value, Condition<T> condition) {
this.value = value;
this.condition = condition;
}
public Condition<T> then(RunBlockNoParameterNoReturnFunction runBlock) {
runBlock.run();
return condition;
}
}
public static class NullExecute<T> extends Execute<T> {
private final Condition<T> condition;
public NullExecute(Condition<T> condition) {
super(null, condition);
this.condition = condition;
}
@Override
public Condition<T> then(RunBlockNoParameterNoReturnFunction runBlock) {
return condition;
}
}
public abstract static class Condition<T> {
protected final List<Boolean> stepNullOptional = new ArrayList<>();
public Condition() {
}
public Execute<T> ifPresent(T value) {
if (!isEmpty(value)) {
stepNullOptional.add(true);
return new Execute<>(value, this);
} else {
stepNullOptional.add(false);
return new NullExecute<>(this);
}
}
public Execute<T> ifPresent(T value, RunBlockReturnFunction<T, Boolean> runBlock) {
if (!runBlock.run(value)) {
stepNullOptional.add(true);
return new Execute<>(value, this);
} else {
stepNullOptional.add(false);
return new NullExecute<>(this);
}
}
public Execute<T> ifNotPresent(T value) {
return new NullExecute<>(this);
}
public Execute<T> ifNotPresent(T value, RunBlockNoParameterNoReturnFunction runBlock) {
runBlock.run();
return new NullExecute<>(this);
}
public abstract <E> E done(RunBlockNoParameterReturnFunction<E> runBlock);
public abstract void done(RunBlockNoParameterNoReturnFunction runBlock);
}
public static class AndCondition<T> extends Condition<T> {
@Override
public <E> E done(RunBlockNoParameterReturnFunction<E> runBlock) {
if (!stepNullOptional.contains(false)) {
return runBlock.run();
}
return null;
}
@Override
public void done(RunBlockNoParameterNoReturnFunction runBlock) {
if (!stepNullOptional.contains(false)) {
runBlock.run();
}
}
}
public static class OrCondition<T> extends Condition<T> {
@Override
public <E> E done(RunBlockNoParameterReturnFunction<E> runBlock) {
if (stepNullOptional.contains(true)) {
return runBlock.run();
}
return null;
}
@Override
public void done(RunBlockNoParameterNoReturnFunction runBlock) {
if (stepNullOptional.contains(true)) {
runBlock.run();
}
}
}
private static <T> boolean isEmpty(T value) {
if (Objects.isNull(value)) {
return true;
}
if (value instanceof String) {
if ("".equals(value) || "".equals(((String) value).trim())) {
return true;
}
} else if (Collection.class.isAssignableFrom(value.getClass())) {
return ((Collection<?>) (value)).isEmpty();
} else if (Map.class.isAssignableFrom(value.getClass())) {
return ((Map<?, ?>) value).isEmpty();
}
return false;
}
}

View File

@@ -0,0 +1,34 @@
package jnpf.util;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import jnpf.base.vo.PaginationVO;
import java.util.List;
public class PageUtil {
public static PaginationVO page(PageDTO page){
PaginationVO paginationVO = new PaginationVO();
paginationVO.setCurrentPage(page.getCurrent());
paginationVO.setPageSize(page.getSize());
paginationVO.setTotal((int)page.getTotal());
return paginationVO;
}
public static List getListPage(int page, int pageSize, List list) {
if (list == null || list.size() == 0) {
return list;
}
int totalCount = list.size();
page = page - 1;
int fromIndex = page * pageSize;
if (fromIndex >= totalCount) {
return list;
}
int toIndex = ((page + 1) * pageSize);
if (toIndex > totalCount) {
toIndex = totalCount;
}
return list.subList(fromIndex, toIndex);
}
}

View File

@@ -0,0 +1,125 @@
package jnpf.util;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import jnpf.annotation.check.CheckLength;
import jnpf.annotation.check.CheckListSize;
import jnpf.annotation.check.CheckNull;
import jnpf.exception.HandleException;
import java.lang.reflect.Field;
import java.util.List;
/**
* 参数校验
*/
public class ParamUtil {
/**
* 参数校验
* @param t
*/
public static void checkParam(Object t) throws Exception{
// Class<T> aClass = ClassUtil.getClass(t);
Class<?> aClass = t.getClass();
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
/** 校验字段是否是空*/
CheckNull annotation = field.getAnnotation(CheckNull.class);
if (annotation == null) {
continue;
}
field.setAccessible(true);
Class<?> type = field.getType();
if (type == String.class) {
/** 字符串类型*/
try {
Object o = field.get(t);
String s = ObjectUtil.isNull(o) ? null : String.valueOf(o);
if (StrUtil.isBlank(s)) {
throw new HandleException(annotation.message());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}else {
/** 普通对象类型*/
try {
if (field.get(t) == null) {
throw new HandleException(annotation.message());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (HandleException e) {
throw new RuntimeException(e);
}
}
}
for (Field field : fields) {
field.setAccessible(true);
/** 校验字段长度*/
CheckLength checkLength = field.getAnnotation(CheckLength.class);
if (checkLength == null) {
continue;
}
int max = checkLength.max();
int min = checkLength.min();
try {
Object o = field.get(t);
String str = String.valueOf(o);
if (StrUtil.length(str) > max && max > 0) {
throw new HandleException(checkLength.message());
}
if (StrUtil.length(str) < min && min > 0) {
throw new HandleException(checkLength.message());
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/** 检查数组长度*/
for (Field field : fields) {
check(field, t);
// field.setAccessible(true);
// CheckListSize listSize = field.getAnnotation(CheckListSize.class);
// if (listSize == null) {
// continue;
// }
// try {
// Object o = field.get(t);
// if (o instanceof List) {
// List list = (List) o;
// if (CollectionUtil.isEmpty(list)) {
// throw new ApiException(listSize.message());
// }
// }
// } catch (IllegalAccessException e) {
// e.printStackTrace();
// }
}
}
private static <T, R> void check(Field field, T t) throws Exception{
field.setAccessible(true);
CheckListSize listSize = field.getAnnotation(CheckListSize.class);
if (listSize != null) {
try {
Object o = field.get(t);
if (ObjectUtil.isNull(o)) {
throw new HandleException(listSize.message());
}
if (o instanceof List) {
List list = (List) o;
if (CollectionUtil.isEmpty(list)) {
throw new HandleException(listSize.message());
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,63 @@
package jnpf.util;
import net.sourceforge.pinyin4j.PinyinHelper;
public class PingYinUtil {
/**
* 提取每个汉字的首字母(大写)
*
* @param str
* @return
*/
public static String getPinYinHeadChar(String str) {
if (isNull(str)) {
return "";
}
String convert = "";
for (int j = 0; j < str.length(); j++) {
char word = str.charAt(j);
// 提取汉字的首字母
String[] pinyinArray = PinyinHelper.toHanyuPinyinStringArray(word);
if (pinyinArray != null) {
convert += pinyinArray[0].charAt(0);
}
else {
convert += word;
}
}
convert = string2AllTrim(convert);
return convert.toLowerCase();
}
/*
* 判断字符串是否为空
*/
public static boolean isNull(Object strData) {
if (strData == null || String.valueOf(strData).trim().equals("")) {
return true;
}
return false;
}
/**
* 去掉字符串包含的所有空格
*
* @param value
* @return
*/
public static String string2AllTrim(String value) {
if (isNull(value)) {
return "";
}
return value.trim().replace(" ", "");
}
public static void main(String[] args) {
String s = PingYinUtil.getPinYinHeadChar("");
System.out.println(s);
}
}

View File

@@ -0,0 +1,736 @@
package jnpf.util;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import jnpf.model.cultivate.po.exam.FtbCultivateExam;
import jnpf.model.cultivate.po.exam.FtbCultivateExamUser;
import jnpf.model.cultivate.po.exam.FtbCultivateExamUserDetail;
import jnpf.model.cultivate.po.paper.FtbCultivateTestPaper;
import jnpf.model.cultivate.po.question.FtbCultivateQuestion;
import jnpf.model.cultivate.req.paper.PaperConfigReq;
import jnpf.model.cultivate.resp.*;
import jnpf.model.cultivate.v2.exam.po.CultivateExam;
import jnpf.model.cultivate.v2.exam.vo.V2ExamStatisticsForPersonExcelVo;
import jnpf.model.cultivate.v2.exam.vo.V2ExamStatisticsForPersonVo;
import jnpf.model.enums.CourseEnums;
import jnpf.model.personnels.dto.staff.roster.WorkerGroupDataDto;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 题目分析工具类
*/
public class QuestionAnalysisUtil {
/**
* 初始化返回值
*
* @return
*/
public static Map<String, PaperConfigReq.QuestionNum> initAnalysQuestionCount() {
HashMap<String, PaperConfigReq.QuestionNum> map = new HashMap<>();
map.put(String.valueOf(CourseEnums.QuestionType.SINGLE.getCode()), new PaperConfigReq.QuestionNum(0, 0, 0));
map.put(String.valueOf(CourseEnums.QuestionType.MULTI.getCode()), new PaperConfigReq.QuestionNum(0, 0, 0));
map.put(String.valueOf(CourseEnums.QuestionType.JUDGE.getCode()), new PaperConfigReq.QuestionNum(0, 0, 0));
map.put(String.valueOf(CourseEnums.QuestionType.FILL.getCode()), new PaperConfigReq.QuestionNum(0, 0, 0));
map.put(String.valueOf(CourseEnums.QuestionType.INPUT.getCode()), new PaperConfigReq.QuestionNum(0, 0, 0));
map.put(String.valueOf(CourseEnums.QuestionType.ONE_OR_MULTI.getCode()), new PaperConfigReq.QuestionNum(0, 0, 0));
return map;
}
/**
* 分析题目数量
*
* @param map
* @param questionList
* @return
*/
public static void analysQuestionCount(Map<String, PaperConfigReq.QuestionNum> map,
List<FtbCultivateQuestion> questionList) {
if (CollectionUtil.isNotEmpty(questionList)) {
for (FtbCultivateQuestion question : questionList) {
String type = String.valueOf(question.getType());
PaperConfigReq.QuestionNum questionNum = map.get(type);
if (question.getDifficulty().equals(CourseEnums.QuestionDifficulty.EASY.getCode())) {
questionNum.setSimpleNum(questionNum.getSimpleNum() + 1);
} else if (question.getDifficulty().equals(CourseEnums.QuestionDifficulty.MIDDLE.getCode())) {
questionNum.setGeneralNum(questionNum.getGeneralNum() + 1);
} else if (question.getDifficulty().equals(CourseEnums.QuestionDifficulty.MAX.getCode())) {
questionNum.setHardNum(questionNum.getHardNum() + 1);
}
}
}
}
/**
* 计算百分比
*
* @param score 用户考试分数
* @param total 试卷总分数
* @return
*/
public static Integer calculatePercentage(int score, int total) {
if (total == 0) {
return 0; // 避免除以零的错误
}
return (int) Math.round((score / (float) total) * 100);
}
/**
* 计算用户考试的状态
*
* @param exam 考试信息
* @param paper 试卷信息
* @param score 用户考试的总分数
* @return
*/
public static Integer calculateUserExamStatus(FtbCultivateExam exam, FtbCultivateTestPaper paper, Integer score) {
int examTotleScore = paper.getTotalScore();//试卷总分数
//合格
Integer passType = exam.getPassType();//合格分数类型1固定分2百分比
Integer passMark = exam.getPassMark();//合格分数
if (CourseEnums.ExamScoreCheckType.FIXED.getCode().equals(passType)) {
if (score >= passMark) {
//已经合格,检测是否优秀
if (checkIsVeryPass(examTotleScore, score, exam.getExcellentType(), exam.getExcellentMark())) {
return CourseEnums.ExamStatus.VERY_PASS.getCode();
}
return CourseEnums.ExamStatus.PASS.getCode();
} else {
///不合格
return CourseEnums.ExamStatus.NO_PASS.getCode();
}
} else {
Integer calculateScore = QuestionAnalysisUtil.calculateScore(passMark, examTotleScore);
if (score >= calculateScore) {
//已经合格 判断是否优秀
if (checkIsVeryPass(examTotleScore, score, exam.getExcellentType(), exam.getExcellentMark())) {
return CourseEnums.ExamStatus.VERY_PASS.getCode();
}
return CourseEnums.ExamStatus.PASS.getCode();
} else {
//不合格
return CourseEnums.ExamStatus.NO_PASS.getCode();
}
}
}
public static Integer calculateUserExamStatus(FtbCultivateExam exam, Integer examTotleScore, Integer score) {
//合格
Integer passType = exam.getPassType();//合格分数类型1固定分2百分比
Integer passMark = exam.getPassMark();//合格分数
if (CourseEnums.ExamScoreCheckType.FIXED.getCode().equals(passType)) {
if (score >= passMark) {
//已经合格,检测是否优秀
if (checkIsVeryPass(examTotleScore, score, exam.getExcellentType(), exam.getExcellentMark())) {
return CourseEnums.ExamStatus.VERY_PASS.getCode();
}
return CourseEnums.ExamStatus.PASS.getCode();
} else {
///不合格
return CourseEnums.ExamStatus.NO_PASS.getCode();
}
} else {
Integer calculateScore = QuestionAnalysisUtil.calculateScore(passMark, examTotleScore);
if (score >= calculateScore) {
//已经合格 判断是否优秀
if (checkIsVeryPass(examTotleScore, score, exam.getExcellentType(), exam.getExcellentMark())) {
return CourseEnums.ExamStatus.VERY_PASS.getCode();
}
return CourseEnums.ExamStatus.PASS.getCode();
} else {
//不合格
return CourseEnums.ExamStatus.NO_PASS.getCode();
}
}
}
/**
* 判断是否优秀
*
* @param totleScore 考试总分数
* @param score 用户考试分数
* @param excellentType 优秀分数类型1固定分2百分比)
* @param excellentMark 优秀分数
* @return false 不优秀 true 优秀
*/
public static boolean checkIsVeryPass(Integer totleScore, Integer score, Integer excellentType, Integer excellentMark) {
if (CourseEnums.ExamScoreCheckType.FIXED.getCode().equals(excellentType)) {
//固定分
if (score >= excellentMark) {
return true;
}
return false;
}
Integer calculateScore = QuestionAnalysisUtil.calculateScore(excellentMark, totleScore);
if (score >= calculateScore) {
return true;
}
return false;
}
/**
* 转换试卷题目
*
* @param questionList
* @param examUserDetailList
* @return
*/
public static PaperQuestionVo convertPaperQuestionVo(List<UserQuestionVo> questionList, List<FtbCultivateExamUserDetail> examUserDetailList) {
PaperQuestionVo vo = new PaperQuestionVo(new HashMap<>());
if (CollectionUtil.isEmpty(questionList)) {
return vo;
}
List<AppQuestionVo> appQuestionVoList = BeanUtil.copyToList(questionList, AppQuestionVo.class);
if (CollectionUtil.isNotEmpty(examUserDetailList)) {
//examUserDetailList 转换成 题目id 的map
Map<String, FtbCultivateExamUserDetail> examUserDetailMap = examUserDetailList.stream().collect(Collectors.toMap(FtbCultivateExamUserDetail::getQuestionId, Function.identity()));
//填充用户答案
for (AppQuestionVo appQuestionVo : appQuestionVoList) {
FtbCultivateExamUserDetail detail = examUserDetailMap.get(appQuestionVo.getQuestionId());
if (null != detail) {
appQuestionVo.setUserAnswer(examUserDetailMap.get(appQuestionVo.getId()).getUserAnswer());
appQuestionVo.setIsComplete(true);
}
}
}
Map<String, List<AppQuestionVo>> questionOptionMap = new HashMap<>();
//填充题目选项
for (AppQuestionVo appQuestionVo : appQuestionVoList) {
String type = String.valueOf(appQuestionVo.getType());
List<AppQuestionVo> questionOptionVoList = questionOptionMap.get(type);
if (CollectionUtil.isEmpty(questionOptionVoList)) {
questionOptionVoList = new ArrayList<>();
}
questionOptionVoList.add(appQuestionVo);
questionOptionMap.put(type, questionOptionVoList);
}
vo.setQuestionMap(questionOptionMap);
return vo;
}
/**
* 多选题判断是否正确
*
* @param answer 标准答案
* @param userAnswer 用户答案
* @return true 正确 false 错误
*/
public static boolean checkMultiRight(String answer, String userAnswer) {
if (StringUtils.isEmpty(answer) || StringUtils.isEmpty(userAnswer)) {
return false;
}
if (answer.equals(userAnswer)) {
return true;
}
List<String> answerList = Arrays.asList(answer.split(","));
List<String> userAnswerList = Arrays.asList(userAnswer.split(","));
if (answerList.size() != userAnswerList.size()) {
return false;
}
Collections.sort(answerList);
Collections.sort(userAnswerList);
String answerStr = String.join(",", answerList);
String userAnswerStr = String.join(",", userAnswerList);
if (answerStr.equals(userAnswerStr)) {
return true;
}
return false;
}
/**
* 计算两个日期之间的秒数
*
* @param start
* @param end
* @return
*/
public static Long differenceSecond(Date start, Date end) {
//计算两个日期之间的秒数
Calendar calendar1 = Calendar.getInstance();
Calendar calendar2 = Calendar.getInstance();
// 设置Calendar对象的时间为date1和date2
calendar1.setTime(start);
calendar2.setTime(end);
// 计算两个日期之间的秒数差
return (calendar2.getTimeInMillis() - calendar1.getTimeInMillis()) / 1000;
}
/**
* 统计用户考试总数和已完成数
*
* @param examUserList
* @return
*/
public static UserExamCount countCompleteAndTotleExamNum(List<FtbCultivateExamUser> examUserList) {
Set<String> totle = new HashSet<>();
Set<String> complete = new HashSet<>();
for (FtbCultivateExamUser examUser : examUserList) {
StringBuilder sbTotle = new StringBuilder()
.append(examUser.getExamId())
.append(examUser.getExamSource())
.append(examUser.getRelationRankId())
.append(examUser.getRelationCourseExamId())
.append(examUser.getRelationPositionExamId());
totle.add(sbTotle.toString());
if (!CourseEnums.ExamStatus.WAIT.getCode().equals(examUser.getStatus()) &&
!CourseEnums.ExamStatus.OVERDUE.getCode().equals(examUser.getStatus())) {
StringBuilder completeTotle = new StringBuilder()
.append(examUser.getExamId())
.append(examUser.getExamSource())
.append(examUser.getRelationRankId())
.append(examUser.getRelationCourseExamId())
.append(examUser.getRelationPositionExamId());
complete.add(completeTotle.toString());
}
}
UserExamCount userExamCount = new UserExamCount();
userExamCount.setTotleNum(totle.size());
userExamCount.setCompleteNum(complete.size());
return userExamCount;
}
/**
* 获取统计开始日期
*
* @param date 目标日期
* @param type 统计类型 1月2季度3年
* @return
*/
public static Date getStatisticsStartDate(Date date, Integer type) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
if (type == 1) {
calendar.add(Calendar.MONTH, -1);
} else if (type == 2) {
calendar.add(Calendar.MONTH, -3);
} else {
calendar.add(Calendar.YEAR, -1);
}
return calendar.getTime();
}
/**
* 统计用户考试合格率
*
* @param examUserList
*/
public static StatisticsResultDto statisticeLv(List<FtbCultivateExamUser> examUserList) {
StatisticsResultDto dto = new StatisticsResultDto();
if (CollectionUtil.isEmpty(examUserList)) {
return dto;
}
int totle = examUserList.size();
int pass = 0;
int noPass = 0;
int excellent = 0;
for (FtbCultivateExamUser examUser : examUserList) {
if (CourseEnums.ExamStatus.PASS.getCode().equals(examUser.getStatus())) {
pass++;
} else if (CourseEnums.ExamStatus.VERY_PASS.getCode().equals(examUser.getStatus())) {
pass++;
excellent++;
} else if (CourseEnums.ExamStatus.NO_PASS.getCode().equals(examUser.getStatus())) {
noPass++;
}
}
dto.setPass(pass);
dto.setNoPass(noPass);
dto.setTotle(totle);
dto.setExcellent(excellent);
dto.setPassLv(Double.valueOf(Math.round((pass / (float) totle) * 100)));
dto.setExcellentLv(Double.valueOf(Math.round((excellent / (float) totle) * 100)));
dto.setNoPassLv(Double.valueOf(Math.round((noPass / (float) totle) * 100)));
return dto;
}
public static StatisticsResultDto statisticeLvForAppExam(List<AppExamListVo> examUserList) {
StatisticsResultDto dto = new StatisticsResultDto();
if (CollectionUtil.isEmpty(examUserList)) {
return dto;
}
int totle = examUserList.size();
int pass = 0;
int noPass = 0;
int excellent = 0;
for (AppExamListVo examUser : examUserList) {
if (CourseEnums.ExamStatus.PASS.getCode().equals(examUser.getStatus())) {
pass++;
} else if (CourseEnums.ExamStatus.VERY_PASS.getCode().equals(examUser.getStatus())) {
excellent++;
} else if (CourseEnums.ExamStatus.NO_PASS.getCode().equals(examUser.getStatus())) {
noPass++;
}
}
dto.setPass(pass);
dto.setNoPass(noPass);
dto.setTotle(totle);
dto.setExcellent(excellent);
dto.setPassLv(Double.valueOf(Math.round(((pass + excellent) / (float) totle) * 100)));
dto.setExcellentLv(Double.valueOf(Math.round((excellent / (float) totle) * 100)));
dto.setNoPassLv(Double.valueOf(Math.round((noPass / (float) totle) * 100)));
return dto;
}
/**
* 统计合格数量 和 总数量
*
* @param examUserList
* @return
*/
public static UserExamCount countPassAndTotleExamNum(List<FtbCultivateExamUser> examUserList) {
Set<String> totle = new HashSet<>();
Set<String> pass = new HashSet<>();
Set<String> noPassNum = new HashSet<>();
Set<String> waitNumSet = new HashSet<>();
for (FtbCultivateExamUser examUser : examUserList) {
StringBuilder sbTotle = new StringBuilder()
.append(examUser.getExamId())
.append(examUser.getUserId())
.append(examUser.getExamSource())
.append(examUser.getRelationRankId())
.append(examUser.getRelationCourseExamId())
.append(examUser.getRelationPositionExamId());
totle.add(sbTotle.toString());
if (CourseEnums.ExamStatus.PASS.getCode().equals(examUser.getStatus()) ||
CourseEnums.ExamStatus.VERY_PASS.getCode().equals(examUser.getStatus())) {
StringBuilder passTotle = new StringBuilder()
.append(examUser.getExamId())
.append(examUser.getUserId())
.append(examUser.getExamSource())
.append(examUser.getRelationRankId())
.append(examUser.getRelationCourseExamId())
.append(examUser.getRelationPositionExamId());
pass.add(passTotle.toString());
}
if (CourseEnums.ExamStatus.NO_PASS.getCode().equals(examUser.getStatus())) {
StringBuilder noPassSb = new StringBuilder()
.append(examUser.getExamId())
.append(examUser.getUserId())
.append(examUser.getExamSource())
.append(examUser.getRelationRankId())
.append(examUser.getRelationCourseExamId())
.append(examUser.getRelationPositionExamId());
noPassNum.add(noPassSb.toString());
}
if (CourseEnums.ExamStatus.WAIT_CHECK.getCode().equals(examUser.getStatus())) {
StringBuilder waitSb = new StringBuilder()
.append(examUser.getExamId())
.append(examUser.getUserId())
.append(examUser.getExamSource())
.append(examUser.getRelationRankId())
.append(examUser.getRelationCourseExamId())
.append(examUser.getRelationPositionExamId());
waitNumSet.add(waitSb.toString());
}
}
UserExamCount userExamCount = new UserExamCount();
userExamCount.setTotleNum(totle.size());
userExamCount.setPassTotleNum(pass.size());
userExamCount.setNoPassNum(noPassNum.size());
userExamCount.setWaitNum(waitNumSet.size());
return userExamCount;
}
/**
* 统计初试复试合格率
*
* @param examUserList
* @return
*/
public static StatisticsResultFirstAndRepeatDto statisticeFirstAndRepeatLv(List<FtbCultivateExamUser> examUserList) {
Set<String> totle = new HashSet<>();
Map<String, Integer> pass = new HashMap<>();
for (FtbCultivateExamUser examUser : examUserList) {
StringBuilder sbTotleKey = new StringBuilder()
.append(examUser.getExamId())
.append(examUser.getExamSource())
.append(examUser.getRelationRankId())
.append(examUser.getRelationCourseExamId())
.append(examUser.getRelationPositionExamId());
totle.add(sbTotleKey.toString());
if (CourseEnums.ExamStatus.PASS.getCode().equals(examUser.getStatus()) ||
CourseEnums.ExamStatus.VERY_PASS.getCode().equals(examUser.getStatus())) {
StringBuilder sbPassKey = new StringBuilder()
.append(examUser.getExamId())
.append(examUser.getExamSource())
.append(examUser.getRelationRankId())
.append(examUser.getRelationCourseExamId())
.append(examUser.getRelationPositionExamId());
String key = sbPassKey.toString();
Integer num = pass.get(key);
if (null == num) {
pass.put(key, 1);
} else {
pass.put(key, num + 1);
}
}
}
StatisticsResultFirstAndRepeatDto dto = new StatisticsResultFirstAndRepeatDto();
dto.setTotle(totle.size());
//遍历 pass
int firstPass = 0;
int repeatPass = 0;
for (Map.Entry<String, Integer> entry : pass.entrySet()) {
String key = entry.getKey();
Integer num = entry.getValue();
if (num == 1) {
firstPass++;
} else {
repeatPass++;
}
}
dto.setFirstPass(firstPass);
dto.setRepeatPass(repeatPass);
dto.setFirstPassLv(Double.valueOf(Math.round((firstPass / (float) dto.getTotle()) * 100)));
dto.setRepeatPassLv(Double.valueOf(Math.round((repeatPass / (float) dto.getTotle()) * 100)));
return dto;
}
/**
* 计算合格分数
*
* @param num1
* @param num2
* @return
*/
public static Integer calculateScore(Integer num1, Integer num2) {
if (num1 == null || num1 == 0) {
return 0;
}
if (num2 == null || num2 == 0) {
return 0;
}
double lv = (double) num1 / 100 * num2;
DecimalFormat df = new DecimalFormat("#");
return Integer.valueOf(df.format(lv));
}
public static BigDecimal calculateScoreV2(Integer num1, Integer num2) {
if (num1 == null || num1 == 0) {
return new BigDecimal(0);
}
if (num2 == null || num2 == 0) {
return new BigDecimal(0);
}
return new BigDecimal(num1).multiply(new BigDecimal(num2)).divide(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP);
}
public static void main(String[] args) throws InterruptedException {
Date date = new Date();
Thread.sleep(1000);
Date now = new Date();
if (now.after(date)) {
System.out.println("在之后");
} else {
System.out.println("不在之后");
}
}
/**
* 获取最高分
*
* @param list
* @return
*/
public static UserRankingVo getMaxScore(List<UserRankingVo> list) {
UserRankingVo vo = list.get(0);
for (UserRankingVo userRankingVo : list) {
int score = Integer.parseInt(userRankingVo.getScore());
int maxScore = Integer.parseInt(vo.getScore());
if (score > maxScore) {
vo = userRankingVo;
}
}
return vo;
}
public static ExamStatisticsForPersonExcelVo convertToExcelPersonvo(ExamStatisticsForPersonVo vo) {
ExamStatisticsForPersonExcelVo excel = new ExamStatisticsForPersonExcelVo();
excel.setUserName(vo.getUserName());
excel.setSystemWokerId(vo.getSystemWokerId());
List<WorkerGroupDataDto> userOrgList = vo.getUserOrgList();
if (CollectionUtil.isNotEmpty(userOrgList)) {
List<String> names = userOrgList.stream()
.map(WorkerGroupDataDto::getAffiliatedOrgName)
.collect(Collectors.toList());
excel.setOrgName(String.join(",", names));
List<String> orgIds = userOrgList.stream()
.map(WorkerGroupDataDto::getOrgEncode)
.collect(Collectors.toList());
excel.setOrgId(String.join(",", orgIds));
List<String> positionAndRanks = new ArrayList<>();
for (WorkerGroupDataDto workerGroupDataDto : userOrgList) {
positionAndRanks.add(workerGroupDataDto.getAffiliatedPositionName() + "_" + workerGroupDataDto.getAffiliatedRankName());
}
excel.setPositionAndRank(String.join(",", positionAndRanks));
List<String> positionIds = userOrgList.stream()
.map(WorkerGroupDataDto::getPositionEncode)
.collect(Collectors.toList());
excel.setPositonId(String.join(",", positionIds));
}
if (null != vo.getStudyPostionName()) {
excel.setStudyPositionAndRank(vo.getStudyPostionName());
}
//试卷类型,1岗位学习试卷,2常规试卷
if (vo.getExamType() == 0) {
excel.setExamType("岗位学习考试");
} else if (vo.getExamType() == 1) {
excel.setExamType("自定义考试");
}
if (null != vo.getFinishtime()) {
excel.setFinishtime(DateUtil.format(vo.getFinishtime(), DatePattern.NORM_DATETIME_PATTERN));
}
if (null != vo.getDuration()) {
excel.setDuration(vo.getDuration() / 60 + "分钟");
}
if (null != vo.getExamTime()) {
excel.setExamTime(vo.getExamTime() + "分钟");
}
if (null != vo.getScore()) {
excel.setScore(vo.getScore() + "");
}
//0待考试1待批阅2已逾期3合格4不合格5优秀
if (vo.getStatus() == 0 || vo.getStatus() == 2) {
excel.setExamStatus("待考");
} else {
excel.setExamStatus("已考");
}
if (vo.getStatus() == 1) {
excel.setExamResult("待批阅");
} else if (vo.getStatus() == 3) {
excel.setExamResult("合格");
} else if (vo.getStatus() == 4) {
excel.setExamResult("不合格");
} else if (vo.getStatus() == 5) {
excel.setExamResult("优秀");
}
return excel;
}
public static V2ExamStatisticsForPersonExcelVo convertToExcelPersonvoV2(V2ExamStatisticsForPersonVo vo) {
V2ExamStatisticsForPersonExcelVo excel = new V2ExamStatisticsForPersonExcelVo();
excel.setUserName(vo.getUserName());
excel.setSystemWorkerId(vo.getSystemWorkerId());
excel.setOrgName(vo.getOrganizeName());
excel.setOrgId(vo.getOrganizeId());
if (StringUtils.isEmpty(vo.getGradeName())) {
excel.setPositionAndRank(vo.getPositionName());
} else {
excel.setPositionAndRank(vo.getPositionName() + "_" + vo.getGradeName());
}
excel.setPositionId(vo.getPositionEnCode());
if (null != vo.getStudyPositionName()) {
excel.setStudyPositionAndRank(vo.getStudyPositionName());
}
excel.setExamName(vo.getExamName());
//试卷类型,1岗位学习试卷,2常规试卷
if (vo.getExamType() == 0) {
excel.setExamType("岗位学习考试");
} else if (vo.getExamType() == 1) {
excel.setExamType("自定义考试");
}
if (null != vo.getFinishtime()) {
excel.setFinishtime(DateUtil.format(vo.getFinishtime(), DatePattern.NORM_DATETIME_PATTERN));
}
if (null != vo.getDuration()) {
excel.setDuration(vo.getDuration() / 60 + "分钟");
}
if (null != vo.getExamTime()) {
excel.setExamTime(vo.getExamTime() + "分钟");
}
if (null != vo.getScore()) {
excel.setScore(vo.getScore() + "");
}
//0待考试1待批阅2已逾期3合格4不合格5优秀
if (vo.getStatus() == 0 || vo.getStatus() == 2) {
excel.setExamStatus("待考");
} else {
excel.setExamStatus("已考");
}
if (vo.getStatus() == 1) {
excel.setExamResult("待批阅");
} else if (vo.getStatus() == 3) {
excel.setExamResult("合格");
} else if (vo.getStatus() == 4) {
excel.setExamResult("不合格");
} else if (vo.getStatus() == 5) {
excel.setExamResult("优秀");
}
return excel;
}
/**
* 对应字符串list去重复
*
* @param list
* @return
*/
public static List<String> uniqueStringList(List<String> list) {
if (CollectionUtil.isEmpty(list)) {
return new ArrayList<>();
}
List<String> filteredList = list.stream()
.filter(str -> !str.isEmpty())
.collect(Collectors.toList());
Set<String> uniqueSet = new HashSet<>(filteredList);
return new ArrayList<>(uniqueSet);
}
public static BigDecimal calPassScore(Integer totalScore, CultivateExam exam) {
//合格
Integer type = exam.getPassType();//合格分数类型1固定分2百分比
Integer mark = exam.getPassMark();//合格分数
if (CourseEnums.ExamScoreCheckType.FIXED.getCode().equals(type)) {
return new BigDecimal(mark);
} else {
return calculateScoreV2(mark, totalScore);
}
}
public static BigDecimal calExcellentScore(Integer totalScore, CultivateExam exam) {
Integer type = exam.getExcellentType();//优秀分数类型1固定分2百分比
Integer mark = exam.getExcellentMark();//优秀分数
if (CourseEnums.ExamScoreCheckType.FIXED.getCode().equals(type)) {
return new BigDecimal(mark);
} else {
return calculateScoreV2(mark, totalScore);
}
}
}

View File

@@ -0,0 +1,29 @@
package jnpf.util;
import org.springframework.stereotype.Component;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 题库ID生成规则
*/
@Component
public class QuestionBankIDGenerator {
private static AtomicInteger counter = new AtomicInteger(0);
public String generateID() {
int currentCounter = counter.incrementAndGet();
int letter = currentCounter / 1000;
char firstChar = (char) (letter + 'A');
int remainingCounter = currentCounter % 1000;
String threeDigits = String.format("%03d", remainingCounter);
return "TK" + firstChar + threeDigits;
}
// public static void main(String[] args) {
// for (int i = 0; i < 10000; i++) {
// System.out.println(generateID());
// }
// }
}

View File

@@ -0,0 +1,194 @@
package jnpf.util;
import jnpf.model.personnels.dto.range.FtbRangeConfigDIYDTO;
import jnpf.model.personnels.dto.range.RangeDTO;
import jnpf.model.personnels.vo.range.FtbRangeConfigDIYVO;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 范围计算
* @Author:peng.hao
*/
public class RangeConfigUtil {
/**
* 平均计算
*
* @param initialValue 起始值
* @param increment 步长值
* @param numberOfIntervals 档位数
* @param previewValue
*/
public static List<RangeDTO> generateRanges(int initialValue, int increment, int numberOfIntervals, String previewValue) {
List<RangeDTO> RangeDTOList = Collections.synchronizedList(new ArrayList<>());
int start = initialValue - increment;
int end = initialValue;
String[] split = previewValue.split("");
RangeDTO priceBandStartDTO = new RangeDTO(0, end, "0_" + end, split[0]);
RangeDTOList.add(priceBandStartDTO);
for (int i = 1; i < numberOfIntervals - 1; i++) {
start += increment;
end = start + increment;
RangeDTO rangeDTO = new RangeDTO(start, end, start + "_" + end, split[i]);
RangeDTOList.add(rangeDTO);
}
RangeDTO rangeDTO = new RangeDTO(end, null, end + "", split[split.length - 1]);
RangeDTOList.add(rangeDTO);
return RangeDTOList;
}
/**
* 计算区间
*
* @param priceBandVOListConfig 自定义区间
*/
public static List<RangeDTO> generateTakeOutRangesDto(List<FtbRangeConfigDIYDTO> priceBandVOListConfig) {
List<RangeDTO> RangeDTOList = new ArrayList<>();
List<FtbRangeConfigDIYDTO> collected = priceBandVOListConfig.stream().sorted(Comparator.comparing(FtbRangeConfigDIYDTO::getIntervalCount)).collect(Collectors.toList());
Map<Integer, FtbRangeConfigDIYDTO> bandVOMap = collected.stream().collect(Collectors.toMap(FtbRangeConfigDIYDTO::getIntervalCount, Function.identity()));
Set<Integer> keySet = bandVOMap.keySet();
for (Integer integer : keySet) {
FtbRangeConfigDIYDTO priceBandVO = bandVOMap.get(integer);
Integer initialValue = priceBandVO.getInitialValue();
// 10以下
if (priceBandVO.getIntervalCount() == 1) {
RangeDTO RangeDTO = new RangeDTO(0, initialValue, "0_" + initialValue, initialValue + "以下");
RangeDTOList.add(RangeDTO);
}
// 结束值
Integer endInterval = priceBandVO.getEndInterval();
// 间隔值
Integer intervalValue = priceBandVO.getIntervalValue();
if (priceBandVO.getIsAbove() == 1) {
initialValue = priceBandVO.getStartInterval();
for (int i = initialValue; i < endInterval; i += intervalValue) {
if (i < initialValue) continue;
RangeDTO RangeDTO = new RangeDTO(i,
i + intervalValue,
i + "_" + (i + intervalValue),
i + "-" + (i + intervalValue));
RangeDTOList.add(RangeDTO);
// 最后一个
if (integer == keySet.size()) {
RangeDTO newRangeDTO = new RangeDTO((i + intervalValue),
(i + intervalValue + intervalValue),
(i + intervalValue) + "_" + (i + intervalValue + intervalValue),
(i + intervalValue) + "-" + (i + intervalValue + intervalValue));
RangeDTOList.add(newRangeDTO);
}
}
} else {
for (int i = initialValue; i < endInterval; i += intervalValue) {
if (i < initialValue) continue;
RangeDTO RangeDTO;
if (endInterval < i + intervalValue) {
RangeDTO = new RangeDTO(i, endInterval, i + "_" + endInterval, i + "-" + endInterval);
} else {
RangeDTO = new RangeDTO(i,
i + intervalValue,
i + "_" + (i + intervalValue),
i + "-" + (i + intervalValue));
}
RangeDTOList.add(RangeDTO);
// 最后一个
if (integer == keySet.size() ) {
RangeDTO newRangeDTO = new RangeDTO((i + intervalValue),
(i + intervalValue + intervalValue),
(i + intervalValue) + "_" + (i + intervalValue + intervalValue),
(i + intervalValue) + "-" + (i + intervalValue + intervalValue));
RangeDTOList.add(newRangeDTO);
}
}
// 只有一个
if ( integer == 1) {
return RangeDTOList;
}
}
}
return RangeDTOList;
}
/**
* 计算区间
*
* @param priceBandVOListConfig 自定义区间
*/
public static List<RangeDTO> generateTakeOutRangesVo(List<FtbRangeConfigDIYVO> priceBandVOListConfig) {
List<RangeDTO> RangeDTOList = new ArrayList<>();
List<FtbRangeConfigDIYVO> collected = priceBandVOListConfig.stream().sorted(Comparator.comparing(FtbRangeConfigDIYVO::getIntervalCount)).collect(Collectors.toList());
Map<Integer, FtbRangeConfigDIYVO> bandVOMap = collected.stream().collect(Collectors.toMap(FtbRangeConfigDIYVO::getIntervalCount, Function.identity()));
Set<Integer> keySet = bandVOMap.keySet();
for (Integer integer : keySet) {
FtbRangeConfigDIYVO priceBandVO = bandVOMap.get(integer);
Integer initialValue = priceBandVO.getInitialValue();
// 10以下
if (priceBandVO.getIntervalCount() == 1) {
RangeDTO RangeDTO = new RangeDTO(0, initialValue, "0_" + initialValue, initialValue + "以下");
RangeDTOList.add(RangeDTO);
}
// 结束值
Integer endInterval = priceBandVO.getEndInterval();
// 间隔值
Integer intervalValue = priceBandVO.getIntervalValue();
if (priceBandVO.getIsAbove() == 1) {
initialValue = priceBandVO.getStartInterval();
for (int i = initialValue; i < endInterval; i += intervalValue) {
if (i < initialValue) continue;
RangeDTO RangeDTO = new RangeDTO(i,
i + intervalValue,
i + "_" + (i + intervalValue),
i + "-" + (i + intervalValue));
RangeDTOList.add(RangeDTO);
// 最后一个
if (integer == keySet.size()) {
RangeDTO newRangeDTO = new RangeDTO((i + intervalValue),
(i + intervalValue + intervalValue),
(i + intervalValue) + "_" + (i + intervalValue + intervalValue),
(i + intervalValue) + "-" + (i + intervalValue + intervalValue));
RangeDTOList.add(newRangeDTO);
}
}
} else {
for (int i = initialValue; i < endInterval; i += intervalValue) {
if (i < initialValue) continue;
RangeDTO RangeDTO;
if (endInterval < i + intervalValue) {
RangeDTO = new RangeDTO(i, endInterval, i + "_" + endInterval, i + "-" + endInterval);
} else {
RangeDTO = new RangeDTO(i,
i + intervalValue,
i + "_" + (i + intervalValue),
i + "-" + (i + intervalValue));
}
RangeDTOList.add(RangeDTO);
// 最后一个
if (integer == keySet.size() ) {
RangeDTO newRangeDTO = new RangeDTO((i + intervalValue),
(i + intervalValue + intervalValue),
(i + intervalValue) + "_" + (i + intervalValue + intervalValue),
(i + intervalValue) + "-" + (i + intervalValue + intervalValue));
RangeDTOList.add(newRangeDTO);
}
}
// 只有一个
if ( integer == 1) {
return RangeDTOList;
}
}
}
return RangeDTOList;
}
}

View File

@@ -0,0 +1,77 @@
package jnpf.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Component
public class RedisDistributedLock {
private final RedisTemplate<String, String> redisTemplate;
// Lua脚本用于原子性解锁
private static final String UNLOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
@Autowired
public RedisDistributedLock(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 尝试获取分布式锁
* @param lockKey 锁的key
* @param requestId 请求标识建议使用UUID
* @param expireTime 锁的过期时间(毫秒)
* @return 是否获取成功
*/
public boolean tryLock(String lockKey, String requestId, long expireTime) {
return Boolean.TRUE.equals(
redisTemplate.opsForValue().setIfAbsent(
lockKey,
requestId,
expireTime,
TimeUnit.MILLISECONDS
)
);
}
/**
* 释放分布式锁
* @param lockKey 锁的key
* @param requestId 请求标识
* @return 是否释放成功
*/
public boolean releaseLock(String lockKey, String requestId) {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);
Long result = redisTemplate.execute(
redisScript,
Collections.singletonList(lockKey),
requestId
);
return result != null && result == 1;
}
/**
* 获取锁的简单方法自动生成requestId
* @param lockKey 锁的key
* @param expireTime 锁的过期时间(毫秒)
* @return requestId 如果获取成功否则null
*/
public String lock(String lockKey, long expireTime) {
String requestId = UUID.randomUUID().toString();
if (tryLock(lockKey, requestId, expireTime)) {
return requestId;
}
return null;
}
}

View File

@@ -0,0 +1,137 @@
//package jnpf.util;
//
//import com.seeta.sdk.*;
//import com.seeta.sdk.util.SeetafaceUtil;
//import jnpf.exception.HandleException;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.web.multipart.MultipartFile;
//
//import javax.imageio.ImageIO;
//import java.awt.image.BufferedImage;
//import java.io.InputStream;
//import java.net.HttpURLConnection;
//import java.net.URL;
//
///**
// * 人脸识别工具类
// *
// * @author yanwenfu
// * @create 2025-04-08
// */
//@Slf4j
//public class SeetaFaceUtil {
//
// //人脸检测器
// private static final FaceDetector detector;
// //关键点定位器 5点
// private static final FaceLandmarker faceLandmarker;
// //人脸向量特征提取和对比器
// private static final FaceRecognizer faceRecognizer;
// static {
// try {
// detector = new FaceDetector(new SeetaModelSetting(SeetaConstant.face_detector, SeetaDevice.SEETA_DEVICE_AUTO));
// faceLandmarker = new FaceLandmarker(new SeetaModelSetting(SeetaConstant.face_landmarker_pts5, SeetaDevice.SEETA_DEVICE_AUTO));
// faceRecognizer = new FaceRecognizer(new SeetaModelSetting(SeetaConstant.face_recognizer, SeetaDevice.SEETA_DEVICE_AUTO));
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// }
//
// // 资源释放方法
// public static synchronized void releaseModels() {
// try {
// if (detector != null) {
// detector.dispose(); // 实际方法名以SDK为准
// }
// if (faceLandmarker != null) {
// faceLandmarker.dispose();
// }
// if (faceRecognizer != null) {
// faceRecognizer.dispose();
// }
// } catch (Exception e) {
// log.error("模型资源释放异常", e);
// }
// }
//
// /**
// * 获取照片特征(参数必须传递一个)
// * @param file 文件
// * @param url 远程路径
// * @return float[]
// */
// public static float[] analyzeEcognizer(MultipartFile file, String url) throws HandleException {
// float[] features1 = null;
// try {
// //第1张照片
// SeetaImageData image1;
// if (null != file) {
// image1 = SeetafaceUtil.toSeetaImageData(imageAsBufferedImage(file));
// } else {
// image1 = SeetafaceUtil.toSeetaImageData(downloadImageAsBufferedImage(url));
// }
// //第一张照片人脸识别
// SeetaRect[] detects1 = detector.Detect(image1);
// if (null == detects1 || detects1.length == 0) {
// throw new HandleException("未识别到人脸,请重试");
// }
// SeetaPointF[] pointFS1 = new SeetaPointF[faceRecognizer.GetExtractFeatureSize()];
// int[] masks1 = new int[faceRecognizer.GetExtractFeatureSize()];
// //第一张图片,第一个人脸关键点定位,有多个人脸的情况下,只取第一个人脸(这是测试,先这样写)
// faceLandmarker.mark(image1, detects1[0], pointFS1, masks1);
// //第一张图片第一个人脸向量特征提取features1
// features1 = new float[faceRecognizer.GetExtractFeatureSize()];
// faceRecognizer.Extract(image1, pointFS1, features1);
// } catch (Exception e) {
// throw new HandleException(e.getMessage());
// }
// return features1;
// }
//
// public static Boolean compareEigenvalue(float[] features1, float[] features2) {
// try {
// //人脸向量特征提取和对比器
// // FaceRecognizer faceRecognizer = new FaceRecognizer(new SeetaModelSetting(SeetaConstant.face_recognizer, SeetaDevice.SEETA_DEVICE_AUTO));
// if (features1 != null && features2 != null ) {
// float calculateSimilarity = faceRecognizer.CalculateSimilarity(features1, features2);
//
// if (calculateSimilarity > 0.75) {
// return true;
// }
// }
// } catch (Exception e) {
// log.error(e.getMessage());
// return false;
// }
// return false;
// }
//
// public static BufferedImage imageAsBufferedImage(MultipartFile file) throws Exception {
// // 获取输入流
// InputStream inputStream = file.getInputStream();
// // 使用ImageIO读取InputStream到BufferedImage
// BufferedImage bufferedImage = ImageIO.read(inputStream);
// // 关闭输入流
// inputStream.close();
// return bufferedImage;
// }
//
// public static BufferedImage downloadImageAsBufferedImage(String imageUrl) throws Exception {
// // 创建URL对象
// URL url = new URL(imageUrl);
// // 打开连接
// HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// connection.setRequestMethod("GET");
// // 连接到资源
// connection.connect();
// // 获取输入流
// InputStream inputStream = connection.getInputStream();
// // 使用ImageIO读取InputStream到BufferedImage
// BufferedImage bufferedImage = ImageIO.read(inputStream);
// // 关闭输入流
// inputStream.close();
// // 断开连接
// connection.disconnect();
// return bufferedImage;
// }
//}

View File

@@ -0,0 +1,108 @@
package jnpf.util;
import cn.hutool.core.collection.CollUtil;
import jnpf.model.enums.SelfrowingEnum;
import jnpf.util.context.SpringContext;
import org.springframework.data.redis.core.RedisTemplate;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.Arrays;
import java.util.Date;
/**
* @Title: SelfGrowthUtil 自增长工具类
* @Author: peng.hao
* @create: 2023/12/1818:07
*/
public class SelfGrowthUtil {
/**
* 提供基于模块自定义 ID
* 提供基于模块自定义 ID
*
* @param selfrowingEnum SelfRowing 枚举
* @return {@link String}
*/
public static String provideACustomIDBasedOnTheModule(SelfrowingEnum selfrowingEnum,Integer... args){
RedisTemplate redisTemplate = SpringContext.getBean("redisTemplate");
String key = UserProvider.getUser().getTenantId() + ":" + selfrowingEnum.getRedisKey();
boolean temp = false;
Long result;
synchronized (SelfGrowthUtil.class){
//只会为key设置一次过期时间
if (!redisTemplate.hasKey(key)) {
temp = true;
}
result = redisTemplate.opsForValue().increment(key);
}
// 设置键的过期时间为当天23:59:59
Date expirationDate = Date.from(LocalDate.now().atTime(23, 59, 59).atZone(ZoneOffset.of("+8")).toInstant());
if (temp) {
redisTemplate.expireAt(key, expirationDate);
}
//向前补齐默认为3位,可以动态设置补位
String perfix = "%03d";
if (CollUtil.isNotEmpty(Arrays.asList(args))) perfix = "%0" + args[0] + "d";
String format= String.format(perfix, result);
SimpleDateFormat spd =new SimpleDateFormat("yyyyMMdd");
//当前年月日
String nowDate = spd.format(expirationDate);
return selfrowingEnum.getReturnsThePrefix() + nowDate + format;
}
public static String providePersonnelsCustomIDBasedOnTheModule(SelfrowingEnum selfrowingEnum){
RedisTemplate redisTemplate = SpringContext.getBean("redisTemplate");
String key = UserProvider.getUser().getTenantId() + ":" + selfrowingEnum.getRedisKey();
boolean temp = false;
Long result;
synchronized (SelfGrowthUtil.class){
//只会为key设置一次过期时间
if (!redisTemplate.hasKey(key)) {
temp = true;
}
result = redisTemplate.opsForValue().increment(key);
}
// 设置键的过期时间为当天23:59:59
Date expirationDate = Date.from(LocalDate.now().atTime(23, 59, 59).atZone(ZoneOffset.of("+8")).toInstant());
if (temp) {
redisTemplate.expireAt(key, expirationDate);
}
//向前补齐0
String format= String.format("%03d", result);
SimpleDateFormat spd =new SimpleDateFormat("yyMMdd");
//当前年月日
String nowDate = spd.format(expirationDate);
return selfrowingEnum.getReturnsThePrefix() + nowDate + format;
}
public static String provideACustomIDBasedOnTheModule(SelfrowingEnum selfrowingEnum, String tenantId) {
RedisTemplate redisTemplate = SpringContext.getBean("redisTemplate");
String key = tenantId + ":" + selfrowingEnum.getRedisKey();
boolean temp = false;
Long result;
synchronized (SelfGrowthUtil.class) {
//只会为key设置一次过期时间
if (!redisTemplate.hasKey(key)) {
temp = true;
}
result = redisTemplate.opsForValue().increment(key);
}
// 设置键的过期时间为当天23:59:59
Date expirationDate = Date.from(LocalDate.now().atTime(23, 59, 59).atZone(ZoneOffset.of("+8")).toInstant());
if (temp) {
redisTemplate.expireAt(key, expirationDate);
}
//向前补齐0
String format = String.format("%03d", result);
SimpleDateFormat spd = new SimpleDateFormat("yyyyMMdd");
//当前年月日
String nowDate = spd.format(expirationDate);
return selfrowingEnum.getReturnsThePrefix() + nowDate + format;
}
}

View File

@@ -0,0 +1,94 @@
package jnpf.util;
import java.util.Objects;
import java.util.function.Supplier;
/**
* 业务异常
*
* @author admin
*/
public final class ServiceException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
private Integer code;
/**
* 错误提示
*/
private String message;
/**
* 错误明细,内部调试错误
* <p>
* 和 {@link CommonResult#getDetailMessage()} 一致的设计
*/
private String detailMessage;
/**
* 空构造方法,避免反序列化问题
*/
public ServiceException() {
}
public ServiceException(String message) {
this.message = message;
}
public ServiceException(String message, Integer code) {
this.message = message;
this.code = code;
}
public String getDetailMessage() {
return detailMessage;
}
@Override
public String getMessage() {
return message;
}
public Integer getCode() {
return code;
}
public ServiceException setMessage(String message) {
this.message = message;
return this;
}
public ServiceException setDetailMessage(String detailMessage) {
this.detailMessage = detailMessage;
return this;
}
public static void isTrue(boolean expression, String message) throws RuntimeException {
if (!expression) {
throw new ServiceException(message);
}
}
public static void isTrue(Supplier<Boolean> expression, String message) throws RuntimeException {
if (!expression.get()) {
throw new ServiceException(message);
}
}
public static void notNull(Object obj, String message) throws RuntimeException {
if (Objects.isNull(obj)) {
throw new ServiceException(message);
}
}
public static void isNull(Object obj, String message) {
if (Objects.nonNull(obj)) {
throw new ServiceException(message);
}
}
}

View File

@@ -0,0 +1,38 @@
package jnpf.util;
import jnpf.base.UserInfo;
import jnpf.config.ConfigValueUtil;
import jnpf.database.util.TenantDataSourceUtil;
import jnpf.exception.LoginException;
import jnpf.util.data.DataSourceContextHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
@Component
public class TenantUtil {
@Autowired
private ConfigValueUtil configValueUtil;
public void switchTenant(){
switchTenant("yawen");
}
public void switchTenant(String tenantId){
// 判断是否为多租户
if (configValueUtil.isMultiTenancy()) {
// 判断是不是从外面直接请求
if (StringUtil.isNotEmpty(tenantId)) {
//切换成租户库
try {
TenantDataSourceUtil.switchTenant(tenantId);
} catch (LoginException e) {
throw new RuntimeException("切换租户失败");
}
} else {
UserInfo userInfo = UserProvider.getUser();
Assert.notNull(userInfo.getUserId(), "缺少租户信息");
DataSourceContextHolder.setDatasource(userInfo.getTenantId(), userInfo.getTenantDbConnectionString(), userInfo.isAssignDataSource());
}
}
}
}

View File

@@ -0,0 +1,101 @@
package jnpf.util;
import cn.hutool.core.collection.CollectionUtil;
import jnpf.authority.FtbAuthorityApi;
import jnpf.base.ActionResult;
import jnpf.permission.V2UserApi;
import jnpf.permission.eum.v2.UserWorkStatusEnums;
import jnpf.permission.model.user.PartUserInfoVo;
import jnpf.permission.vo.v2.user.UserBoundVO;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* V2版本用户查询工具类提供多种方式获取用户信息的方法。
*/
@Component
public class V2UserQueryUtil {
@Resource
private V2UserApi v2UserApi;
@Resource
private FtbAuthorityApi authorityApi;
/**
* 根据用户ID获取单个用户信息
* @param id 用户唯一标识符
* @return PartUserInfoVo 用户部分信息对象
*/
public PartUserInfoVo getUserById(String id) {
return getUserListToPartUserInfoVo(List.of(id)).get(0);
}
/**
* 批量根据用户ID列表获取用户信息列表
* @param userIds 用户ID集合
* @return List<PartUserInfoVo> 用户信息列表
*/
public List<PartUserInfoVo> getUserListToPartUserInfoVo(List<String> userIds) {
ActionResult<List<UserBoundVO>> allUserInfoBatch = v2UserApi.getAllUserInfoBatch(userIds, UserProvider.getUser().getTenantId());
List<UserBoundVO> userList = allUserInfoBatch.getData();
if (CollectionUtil.isEmpty(userList)) {
return new ArrayList<>();
}
return userList.stream().map(user -> {
PartUserInfoVo vo = new PartUserInfoVo();
vo.setUserId(user.getId());
vo.setRealName(user.getName());
vo.setNickName(user.getNickname());
vo.setOrganizeId(user.getOrganizeId());
vo.setOrganizeName(user.getOrganizeName());
vo.setPositionId(user.getPositionId());
vo.setPositionName(user.getPositionName());
vo.setHeadIcon(user.getHeadIcon());
vo.setMobilePhone(user.getPhone());
vo.setAccount(user.getAccount());
return vo;
}).collect(Collectors.toList());
}
/**
* 获取用户列表(包含离职用户)
* @param userIds 用户ID集合
* @param tenantId 租户ID
* @return List<PartUserInfoVo> 用户信息列表(含离职用户)
*/
public List<PartUserInfoVo> getUserListAndCopy(List<String> userIds, String tenantId) {
List<UserBoundVO> userBoundVos = v2UserApi.userListAndCopy(userIds, null, tenantId);
if (CollectionUtil.isEmpty(userBoundVos)) {
return new ArrayList<>();
}
return userBoundVos.stream().map(user -> {
PartUserInfoVo vo = new PartUserInfoVo();
vo.setUserId(user.getId());
vo.setRealName(user.getName());
vo.setNickName(user.getNickname());
vo.setOrganizeId(user.getOrganizeId());
vo.setOrganizeName(user.getOrganizeName());
vo.setPositionId(user.getPositionId());
vo.setPositionName(user.getPositionName());
vo.setHeadIcon(user.getHeadIcon());
vo.setMobilePhone(user.getPhone());
vo.setAccount(user.getAccount());
return vo;
}).collect(Collectors.toList());
}
/**
* 获取指定组织及其子组织下的所有用户
* @param organizeId 组织ID
* @return List<UserBoundVO> 用户绑定信息列表
*/
public List<UserBoundVO> getOrganizeChildUserList(String organizeId) {
return authorityApi.listTargetOrganizeIdAuthApi(organizeId, List.of(UserWorkStatusEnums.ONBOARDING_FAILED,
UserWorkStatusEnums.PENDING_RESIGNATION,
UserWorkStatusEnums.RESIGNED));
}
}

View File

@@ -0,0 +1,37 @@
package jnpf.util;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;
/**
* 验证类
*
* @author lx
* @version 1.0
* @since 2021-10-26
*/
public class ValidatorUtils {
private static Validator validator;
static {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
/**
* 校验对象
*
* @param object 待校验对象
* @param groups 待校验的组
* @throws ServiceException 校验不通过,则报自定义异常
*/
public static void validateEntity(Object object, Class<?>... groups)
throws ServiceException {
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty()) {
ConstraintViolation<Object> constraint = constraintViolations.iterator().next();
throw new ServiceException(constraint.getMessage(), HttpStatus.BAD_REQUEST);
}
}
}

View File

@@ -0,0 +1,244 @@
package jnpf.util.attendance;
import cn.hutool.core.collection.CollUtil;
import jnpf.entity.AttendanceGroupUser;
import jnpf.enums.attendance.GroupUserTypeEnum;
import jnpf.model.attendance.vo.SecondmentDateVo;
import jnpf.util.DateDetail;
import jnpf.util.JsonUtil;
import jnpf.util.StringUtil;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 考勤组成员在组 / 借调 / 离组状态判断工具。
* <p>从 {@link jnpf.attendance.service.impl.AttendanceDailyRuleServiceImpl} 抽取,
* 供排班、考勤本、统计等模块统一复用。</p>
*/
public final class AttendanceGroupUserStatusUtil {
/**
* 查询当日该用户是否存在于当前考勤组(详细状态)。
*
* @return -1离组 0未入 1在组 2借调
*/
public static Integer findUserIsExistsStatusByDay(List<AttendanceGroupUser> users, Date date) {
Date start = cn.hutool.core.date.DateUtil.beginOfDay(date);
Date end = cn.hutool.core.date.DateUtil.endOfDay(date);
return findUserIsExistsByDay(users, start, end);
}
/**
* 查询当日该用户是否存在于当前考勤组简化1存在0不存在
*/
public static Integer findUserIsExistsByDay(List<AttendanceGroupUser> users, Date date) {
return findUserIsExistsStatusByDay(users, date) != 1 ? 0 : 1;
}
/**
* 查询时间范围内该用户是否存在于当前考勤组。
*
* @return -1离组 0未入 1在组 2借调
*/
public static Integer findUserIsExistsByDay(List<AttendanceGroupUser> users, Date start, Date end) {
if (CollUtil.isEmpty(users)) {
return 0;
}
boolean isInGroup = users.stream().anyMatch(user -> Objects.isNull(user.getRemoveTime())
? Boolean.TRUE
: user.getRemoveTime().compareTo(start) > 0);
if (!isInGroup) {
return -1;
}
isInGroup = users.stream().anyMatch(user -> user.getUserGroupType() == 2
? Boolean.TRUE
: Objects.isNull(user.getCreatorTime()) ? Boolean.FALSE : end.compareTo(user.getCreatorTime()) >= 0);
if (!isInGroup) {
return 0;
}
boolean isInSecondment = users.stream()
.filter(user -> Objects.equals(user.getType(), GroupUserTypeEnum.CUR.getCode()))
.anyMatch(user -> {
List<SecondmentDateVo> secondmentDateVos = parseSecondmentDates(user.getTimeJson());
if (CollUtil.isEmpty(secondmentDateVos)) {
return Boolean.FALSE;
}
return secondmentDateVos.stream().anyMatch(dateVo ->
DateDetail.checkTimeBetween(start, dateVo.getStartTime(), dateVo.getEndTime())
&& DateDetail.checkTimeBetween(end, dateVo.getStartTime(), dateVo.getEndTime()));
});
List<AttendanceGroupUser> borrowUsers = users.stream()
.filter(user -> Objects.equals(user.getType(), GroupUserTypeEnum.BORROW.getCode()))
.collect(Collectors.toList());
boolean isSecondment = borrowUsers.stream().anyMatch(user -> {
List<SecondmentDateVo> secondmentDateVos = parseSecondmentDates(user.getTimeJson());
if (CollUtil.isEmpty(secondmentDateVos)) {
return Boolean.FALSE;
}
return Objects.nonNull(getSecondmentType(secondmentDateVos, start, end));
});
if (users.size() == borrowUsers.size() && !isSecondment) {
if (borrowUsers.stream().allMatch(user -> {
List<SecondmentDateVo> secondmentDateVos = parseSecondmentDates(user.getTimeJson());
return secondmentDateVos.stream().allMatch(vo -> vo.getEndTime().before(start));
})) {
return -1;
}
return 0;
}
return !isInSecondment & (CollUtil.isEmpty(borrowUsers) ? Boolean.TRUE : !isSecondment) ? 1 : 2;
}
/**
* 批量判断用户在某时间范围内是否在组简化1存在0不存在
*/
public static Map<String, Integer> findUserIsExistsByUserList(List<AttendanceGroupUser> users, Date start, Date end) {
Map<String, List<AttendanceGroupUser>> userListMap = users.stream()
.collect(Collectors.groupingBy(AttendanceGroupUser::getUserId));
Map<String, Integer> map = new HashMap<>();
for (Map.Entry<String, List<AttendanceGroupUser>> user : userListMap.entrySet()) {
Integer existStatus = isExistStatus(user.getValue(), start, end);
map.put(user.getKey(), Objects.equals(existStatus, 1)
|| Objects.equals(existStatus, 3)
|| Objects.equals(existStatus, 4) ? 1 : 0);
}
return map;
}
/**
* 判断当天内用户的状态:-1已离 0未入 1正常 2全天被借调 3部分被借调 4借调。
*/
public static Integer isExistStatus(List<AttendanceGroupUser> users, Date date) {
return isExistStatus(users, date, cn.hutool.core.date.DateUtil.endOfDay(date));
}
/**
* 判断时间范围内用户的状态:-1已离 0未入 1正常 2全天被借调 3部分被借调 4借调。
*/
public static Integer isExistStatus(List<AttendanceGroupUser> users, Date start, Date end) {
if (CollUtil.isEmpty(users)) {
return 0;
}
List<AttendanceGroupUser> borrowUsers = users.stream()
.filter(user -> Objects.equals(user.getType(), GroupUserTypeEnum.BORROW.getCode()))
.collect(Collectors.toList());
Boolean inSecondment = isInSecondment(users, start, end);
if (CollUtil.isNotEmpty(borrowUsers)) {
List<SecondmentDateVo> borrowDates = borrowUsers.stream()
.flatMap(user -> StringUtil.isEmpty(user.getTimeJson())
? Stream.empty()
: JsonUtil.getJsonToList(user.getTimeJson(), SecondmentDateVo.class).stream())
.collect(Collectors.toList());
List<AttendanceGroupUser> curInBorrowGroup = users.stream()
.filter(vo -> borrowUsers.stream().anyMatch(b -> vo.getGroupId().equals(b.getGroupId()))
&& Objects.equals(vo.getType(), GroupUserTypeEnum.CUR.getCode()))
.collect(Collectors.toList());
if (CollUtil.isNotEmpty(curInBorrowGroup)) {
int isExist = getIsExist(curInBorrowGroup, start);
if (isExist != 0) {
return isExist;
}
}
if (inSecondment) {
return 4;
}
Date startTime = borrowDates.stream().map(SecondmentDateVo::getStartTime).max(Date::compareTo).orElse(null);
if (Objects.nonNull(startTime) && end.before(startTime)) {
return 0;
}
return -1;
}
List<SecondmentDateVo> allSecondmentDates = users.stream()
.flatMap(user -> StringUtil.isEmpty(user.getTimeJson())
? Stream.empty()
: JsonUtil.getJsonToList(user.getTimeJson(), SecondmentDateVo.class).stream())
.collect(Collectors.toList());
if (inSecondment) {
if (allSecondmentDates.stream().anyMatch(vo ->
DateDetail.checkTimeBetween(start, vo.getStartTime(), vo.getEndTime())
&& DateDetail.checkTimeBetween(end, vo.getStartTime(), vo.getEndTime()))) {
return 2;
}
return 3;
}
return getIsExist(users, start);
}
/**
* 是否借调:不管借调还是被借调,只要命中借调时间则返回 true。
*/
public static Boolean isInSecondment(List<AttendanceGroupUser> users, Date start, Date end) {
if (CollUtil.isEmpty(users)) {
return Boolean.FALSE;
}
return users.stream().anyMatch(user -> {
List<SecondmentDateVo> secondmentDateVos = parseSecondmentDates(user.getTimeJson());
if (CollUtil.isEmpty(secondmentDateVos)) {
return Boolean.FALSE;
}
return Objects.nonNull(getSecondmentType(secondmentDateVos, start, end));
});
}
/**
* 判断用户在某日是否处于「可维护考勤本」的在组状态。
* <p>existStatus = 1 视为在组3部分被借调、4借调按业务约定也视为在组。</p>
*/
public static boolean isUserInGroupOnDay(List<AttendanceGroupUser> userGroupUsers, Date day) {
if (CollUtil.isEmpty(userGroupUsers) || day == null) {
return false;
}
Integer existStatus = isExistStatus(userGroupUsers, day);
if (Objects.equals(existStatus, 4) || Objects.equals(existStatus, 3)) {
existStatus = 1;
}
return Objects.equals(existStatus, 1);
}
/**
* 获取目标时间范围涉及的借调时段。
*/
public static SecondmentDateVo getSecondmentType(List<SecondmentDateVo> secondmentDateVos, Date inPoint, Date outPoint) {
if (CollUtil.isEmpty(secondmentDateVos)) {
return null;
}
for (SecondmentDateVo dateVo : secondmentDateVos) {
if (DateDetail.checkTimeBetween(inPoint, dateVo.getStartTime(), dateVo.getEndTime())
|| DateDetail.checkTimeBetween(outPoint, dateVo.getStartTime(), dateVo.getEndTime())
|| DateDetail.checkTimeBetween(dateVo.getStartTime(), inPoint, outPoint)
|| DateDetail.checkTimeBetween(dateVo.getEndTime(), inPoint, outPoint)) {
return dateVo;
}
}
return null;
}
private static List<SecondmentDateVo> parseSecondmentDates(String timeJson) {
return StringUtil.isEmpty(timeJson)
? CollUtil.newArrayList()
: JsonUtil.getJsonToList(timeJson, SecondmentDateVo.class);
}
private static int getIsExist(List<AttendanceGroupUser> groupUserVos, Date date) {
if (Boolean.TRUE.equals(isInSecondment(groupUserVos, date, cn.hutool.core.date.DateUtil.endOfDay(date)))) {
return 1;
}
boolean isIn = groupUserVos.stream()
.allMatch(user -> cn.hutool.core.date.DateUtil.beginOfDay(user.getCreatorTime()).after(date));
boolean isOut = groupUserVos.stream().anyMatch(user -> {
if (Objects.isNull(user.getRemoveTime())) {
user.setRemoveTime(jnpf.util.DateUtil.dateAddYears(date, 1));
}
return DateDetail.checkTimeBetween(date,
cn.hutool.core.date.DateUtil.beginOfDay(user.getCreatorTime()),
cn.hutool.core.date.DateUtil.endOfDay(user.getRemoveTime()));
});
return isIn ? 0 : !isOut ? -1 : 1;
}
}

View File

@@ -0,0 +1,51 @@
package jnpf.util.attendance;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import jnpf.entity.attendance.FtbAttendanceDailyRule;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Objects;
public class DailyRuleUtil {
public static List<FtbAttendanceDailyRule> mergeRules(List<FtbAttendanceDailyRule> dayRules) {
dayRules.sort(Comparator.comparing(FtbAttendanceDailyRule::getOutPoint));
FtbAttendanceDailyRule preRule = dayRules.get(0);
List<FtbAttendanceDailyRule> mergeRules = CollUtil.newArrayList(preRule);
for (int i = 1; i < dayRules.size(); i++) {
FtbAttendanceDailyRule oldRule = preRule;
FtbAttendanceDailyRule currRule = dayRules.get(i);
if (oldRule.getOutPoint().compareTo(currRule.getInPoint()) >= 0 && oldRule.getOutPoint().compareTo(currRule.getOutPoint()) <= 0) {
oldRule.setOutPoint(currRule.getOutPoint());
continue;
}
preRule = currRule;
mergeRules.add(preRule);
}
return mergeRules;
}
/**
* 向上取半小时为最小单位的时间
*
* @param dateTime
* @return
*/
public static Date ceilToHalfHour(Date dateTime) {
if (Objects.isNull(dateTime)) {
dateTime = DateUtil.offsetMonth(DateUtil.endOfDay(new Date()), 12);
}
int minute = cn.hutool.core.date.DateUtil.minute(dateTime);
if (minute % 30 == 0) {
return dateTime;
}
int nextHalfHour = ((minute / 30) + 1) * 30;
dateTime.setMinutes(nextHalfHour);
dateTime.setSeconds(0);
return dateTime;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,136 @@
package jnpf.util.attendance;
import jnpf.util.DateUtil;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Month;
import java.time.Period;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
import java.util.Calendar;
import java.util.Date;
/**
* @author panpan
*/
@Component
public class ExpiresTimeUtil {
/**
* 计算过期时间
* @param lifespanType 生效类型 0.永久有效 1.固定天数 2.指定日期
* @param fixedDay 固定天数
* @param specifyDay 指定日期 格式MM-dd
* @return 过期时间
*/
public Date getExpiresTime(Integer lifespanType, Integer fixedDay, String specifyDay){
//永久有效 1-固定天数 (当前时间加上设置的天数) 2-指定日期 拼接今年的年判断有无过去,过去后取下一年,没有过去取今年
if (lifespanType == 0){
return null;
}
SimpleDateFormat yyyy = new SimpleDateFormat("yyyy" );
SimpleDateFormat yMd = new SimpleDateFormat("yyyy-MM-dd" );
SimpleDateFormat yMdHms = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" );
SimpleDateFormat hms = new SimpleDateFormat("HH:mm:ss" );
if (lifespanType == 1){
return DateUtil.dateAddDays(new Date(), fixedDay);
}
if (lifespanType == 2){
Date time = new Date();
String year = yyyy.format(time);
String expiresTime = year + "-" + specifyDay + " " + hms.format(time);
System.out.println("拼接时间: "+expiresTime);
String today = yMd.format(time);
System.out.println("今天: "+today);
try {
if (yMd.parse(expiresTime).getTime() <= yMd.parse(today).getTime()){
System.out.println("已过期");
// 获取下一年的指定时间
Calendar c = Calendar.getInstance();
c.setTime(yMdHms.parse(expiresTime));
c.set(Calendar.YEAR, c.get(Calendar.YEAR) + 1);
return c.getTime();
} else {
return yMdHms.parse(expiresTime);
}
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
return DateUtil.dateAddDays(new Date(), 365);
}
/**
* 计算员工从入职日期到现在的司龄返回月数
*
* @param startDate 员工入职日期
* @return 司龄(单位:月)
*/
public Integer calculateSeniorityMonth(Date startDate) {
// 参数校验
if (startDate == null ) {
return 1;
}
// 入职日期
LocalDate startLocalDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
// 当前日期
LocalDate currentDate = LocalDate.now();
// 确保开始日期不晚于当前日期
if (startLocalDate.isAfter(currentDate)) {
return 1;
}
Period period = Period.between(startLocalDate, currentDate);
int years = period.getYears();
int months = period.getMonths();
int days = period.getDays();
// 返回司龄(单位:月) 哪怕是一天,也算一个月
return years * 12 + months + (0 != days ? 1 : 0);
}
// 获取指定日期的日
/**
* 检查指定日期是否在当前日期的月份内
* @param startDate 指定日期
* @param currentDate 当前日期
* @return true 在当前日期的月份内 false 不在当前日期的月份内
*/
public boolean checkMonthDay(Date startDate,LocalDate currentDate) {
// 当前日期
int monthValue = currentDate.getMonthValue();
int day = currentDate.getDayOfMonth();
// 当前月份 最后一天的日
int monthLastDay = currentDate.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth();
// 返回入参对应的月份
LocalDate startLocalDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
int checkMonth = startLocalDate.getMonthValue();
int checkDay = startLocalDate.getDayOfMonth();
return checkMonth == monthValue && (checkDay == day || (day == monthLastDay && checkDay >= day));
}
/**
* 检查指定日期是否在当前日期的月份内
* @param startDate 指定日期
* @param monthDayStr 格式MM-dd
* @return true 在当前日期的月份内 false 不在当前日期的月份内
*/
public boolean checkMonthDayStr(Date startDate,String monthDayStr) {
String[] monthDay = monthDayStr.split("-");
int month = Integer.parseInt(monthDay[0]);
int day = Integer.parseInt(monthDay[1]);
// 获取今年对应月份的最后一天日
int monthLastDay = LocalDate.now().withMonth(month).with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth();
// 当前月份 最后一天的日
// 返回入参对应的月份
LocalDate startLocalDate = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
int checkMonth = startLocalDate.getMonthValue();
int checkDay = startLocalDate.getDayOfMonth();
return checkMonth == month && (checkDay == day || (day >= monthLastDay && checkDay >= day));
}
}

View File

@@ -0,0 +1,74 @@
package jnpf.util.attendance;
import lombok.Data;
import java.math.BigDecimal;
/**
* 票据核验-库区详情
*
* @author yew
* @version v1.0
* @date 2020-11-06 10:15
*/
@Data
public class GetPdfPrintDataDetails {
/**
* 烟叶编码
*/
private String sxCode;
/**
* 烟叶名称
*/
private String sxName;
/**
* 类型
*/
private String lxName;
/**
* 年份
*/
private String year;
/**
* 产地
*/
private String cdName;
/**
* 等级
*/
private String djName;
/**
* 品种
*/
private String pzName;
/**
* 特标
*/
private String tbName;
/**
* 批次号
*/
private String batchCode;
/**
* 数量
*/
private Integer totalNum;
/**
* 重量
*/
private BigDecimal totalWeight;
/**
* 库存区域
*/
private String dwName;
/**
* 质检结果
*/
private Integer qualityResult;
/**
* 现场抽样结果 -1 :未抽样 0不合格 1合格
*/
private Integer samplingResult;
private String remark;
}

View File

@@ -0,0 +1,56 @@
package jnpf.util.attendance;
import lombok.Data;
import java.util.List;
/**
* 票据核验-库区详情
*
* @author yew
* @version v1.0
* @date 2020-11-06 10:15
*/
@Data
public class GetPdfPrintDataModel {
/**
* 文档编号
*/
private String docCode;
/**
* 烟厂名称
*/
private String ycName;
/**
* 申报部门
*/
private String declareDepartment;
/**
* 仓库名称
*/
private String ckName;
/**
* 来货单位
*/
private String shippingUnit;
/**
* 到货日期
*/
private String arrivalDate;
/**
* 申报日期
*/
private String declareDate;
/**
* 申报人
*/
private String declareOpt;
/**
* 联系电话
*/
private String phone;
/**
* 数据集合
*/
private List<GetPdfPrintDataDetails> details;
}

View File

@@ -0,0 +1,88 @@
package jnpf.util.attendance;
import net.coobird.thumbnailator.Thumbnails;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
/**
* 图片压缩
*
* @author yanwenfu
* @create 2025-12-11
*/
public class ImageCompressUtil {
/**
* 压缩人脸图片
* @param input 原图
* @param output 输出文件
* @param targetKB 目标大小 KB
* @throws Exception 异常
*/
public static void compressFace(File input, File output, long targetKB) throws Exception {
compressResizeAndQuality(input, output, targetKB, 0.9f, 900);
}
/**
* 压缩缩略图
* @param input 原图
* @param output 输出文件
* @param targetKB 目标大小 KB
* @throws Exception 异常
*/
public static void compressThumbnail(File input, File output, long targetKB) throws Exception {
compressResizeAndQuality(input, output, targetKB, 0.7f, 300);
}
/**
* 核心压缩方法:按最大边长缩放 + 固定质量
*
* @param input 原文件
* @param output 输出文件
* @param targetKB 目标大小 KB
* @param quality JPEG质量0~1
* @param maxSide 最大边长
* @throws Exception
*/
private static void compressResizeAndQuality(File input, File output, long targetKB, float quality, int maxSide) throws Exception {
long targetBytes = targetKB * 1024;
BufferedImage img = ImageIO.read(input);
int width = img.getWidth();
int height = img.getHeight();
// 计算缩放比例
double scale = 1.0;
if (width > maxSide || height > maxSide) {
scale = Math.min((double) maxSide / width, (double) maxSide / height);
}
// 是否是 jpg
String name = input.getName().toLowerCase();
boolean isJpg = name.endsWith(".jpg") || name.endsWith(".jpeg");
// ===== 性能直通jpg + 小图 + 不缩放 =====
if (isJpg && scale == 1.0 && input.length() <= targetBytes) {
Files.copy(input.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING);
return;
}
// ===== 统一编码路径:转 RGB解决 PNG 透明) =====
BufferedImage rgbImage = new BufferedImage(
img.getWidth(),
img.getHeight(),
BufferedImage.TYPE_INT_RGB
);
Graphics2D g = rgbImage.createGraphics();
g.setColor(Color.WHITE); // 背景色,可按需要调整
g.fillRect(0, 0, width, height);
g.drawImage(img, 0, 0, null);
g.dispose();
// Thumbnails 一步到位:缩放 + 固定质量
Thumbnails.of(rgbImage)
.scale(scale)
.outputQuality(quality)
.toFile(output);
}
}

View File

@@ -0,0 +1,26 @@
package jnpf.util.attendance;
import jnpf.attendance.service.MachineStrategy;
import jnpf.enums.attendance.MachineEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 考勤机策略工厂
*
* @author yanwenfu
* @create 2021-01-26
*/
@Component
public class MachineStrategyFactory {
@Autowired
private Map<String, MachineStrategy> machineStrategyMap;
public MachineStrategy getMachineStrategy(MachineEnum machine) {
return machineStrategyMap.get(machine.getDescription());
}
}

View File

@@ -0,0 +1,181 @@
package jnpf.util.attendance;
import jnpf.config.MqttConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import java.io.UnsupportedEncodingException;
/**
* mqtt push 客户端
*
* @author yanwenfu
* @create 2024-04-09
*/
@Slf4j
public class MqttPushClient {
private static MqttClient client;
public static MqttClient getClient() {
return client;
}
public static void setClient(MqttClient client) {
MqttPushClient.client = client;
}
/**
* 编辑连接信息
* @param userName
* @param password
* @param outTime
* @param KeepAlive
* @return
*/
private MqttConnectOptions getOption(String userName, String password, int outTime, int KeepAlive) {
//MQTT连接设置
MqttConnectOptions option = new MqttConnectOptions();
//设置是否清空session,false表示服务器会保留客户端的连接记录true表示每次连接到服务器都以新的身份连接
option.setCleanSession(false);
//设置连接的用户名
option.setUserName(userName);
//设置连接的密码
option.setPassword(password.toCharArray());
//设置超时时间 单位为秒
option.setConnectionTimeout(outTime);
//设置会话心跳时间 单位为秒 服务器会每隔(1.5*keepTime)秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
option.setKeepAliveInterval(KeepAlive);
//setWill方法如果项目中需要知道客户端是否掉线可以调用该方法。设置最终端口的通知消息
//option.setWill(topic, "close".getBytes(StandardCharsets.UTF_8), 2, true);
option.setMaxInflight(1000);
return option;
}
/**
* 发起连接
*/
public void connect(MqttConfiguration mqttConfiguration) {
MqttClient client;
try {
client = new MqttClient(mqttConfiguration.getHost(), mqttConfiguration.getClientId(), new MemoryPersistence());
MqttConnectOptions options = getOption(mqttConfiguration.getUsername(), mqttConfiguration.getPassword(),
mqttConfiguration.getTimeout(), mqttConfiguration.getKeepAlive());
MqttPushClient.setClient(client);
try {
client.setCallback(new PushCallback(this, mqttConfiguration));
if (!client.isConnected()) {
client.connect(options);
log.info("================>>>MQTT连接成功<<======================");
} else {//这里的逻辑是如果连接不成功就重新连接
client.disconnect();
client.connect(options);
log.info("===================>>>MQTT断连成功<<<======================");
}
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 断线重连
*
* @throws Exception
*/
public Boolean reConnect() throws Exception {
Boolean isConnected = false;
if (null != client) {
client.connect();
if (client.isConnected()) {
isConnected = true;
}
}
return isConnected;
}
/**
* 发布默认qos为0非持久化
*
* @param topic
* @param pushMessage
*/
public void publish(String topic, String pushMessage) {
publish(0, false, topic, pushMessage);
}
/**
* 发布
*
* @param qos
* @param retained
* @param topic
* @param pushMessage
*/
public void publish(int qos, boolean retained, String topic, String pushMessage) {
MqttMessage message = new MqttMessage();
message.setQos(qos);
message.setRetained(retained);
try {
message.setPayload(pushMessage.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
MqttTopic mTopic = MqttPushClient.getClient().getTopic(topic);
if (null == mTopic) {
log.error("===============>>>MQTT topic 不存在<<=======================");
}
MqttDeliveryToken token;
try {
token = mTopic.publish(message);
token.waitForCompletion();
} catch (MqttPersistenceException e) {
e.printStackTrace();
} catch (MqttException e) {
e.printStackTrace();
}
}
/**
* 发布消息的服务质量(推荐为2-确保消息到达一次。0-至多一次到达1-至少一次到达,可能重复)
* retained 默认false-非持久化(是指一条消息消费完,就会被删除;持久化,消费完,还会保存在服务器中,当新的订阅者出现,继续给新订阅者消费)
*
* @param topic
* @param pushMessage
*/
public void publish(int qos, String topic, String pushMessage) {
publish(qos, false, topic, pushMessage);
}
/**
*
* 订阅多个主题
*
* @param topic
* @param qos
*/
public void subscribe(String[] topic, int[] qos) {
try {
MqttPushClient.getClient().unsubscribe(topic);
MqttPushClient.getClient().subscribe(topic, qos);
} catch (MqttException e) {
e.printStackTrace();
}
}
/**
* 清空主题
* @param topic
*/
public void cleanTopic(String topic) {
try {
MqttPushClient.getClient().unsubscribe(topic);
} catch (MqttException e) {
log.error(e.getMessage());
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,73 @@
package jnpf.util.attendance;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
/**
* 消息发布
*
* @author yanwenfu
* @create 2024-04-09
*/
@Component(value = "mqttSender")
@Slf4j
public class MqttSender {
@Async
public void send(String queueName, String msg) {
log.info("=====================>>>>发送主题"+queueName);
publish(2,queueName, msg);
}
/**
* 发布默认qos为0非持久化
* @param topic
* @param pushMessage
*/
public void publish(String topic, String pushMessage){
publish(1, false, topic, pushMessage);
}
/**
* 发布
* @param qos
* @param retained
* @param topic
* @param pushMessage
*/
public void publish(int qos,boolean retained,String topic,String pushMessage) {
MqttMessage message = new MqttMessage();
message.setQos(qos);
message.setRetained(retained);
message.setPayload(pushMessage.getBytes(StandardCharsets.UTF_8));
MqttTopic mTopic = MqttPushClient.getClient().getTopic(topic);
if(null == mTopic){
log.error("===================>>>MQTT topic 不存在<<=================");
}
MqttDeliveryToken token;
try {
token = mTopic.publish(message);
token.waitForCompletion();
} catch (MqttPersistenceException e) {
log.error("============>>>publish fail",e);
e.printStackTrace();
} catch (MqttException e) {
e.printStackTrace();
}
}
/**
* 发布消息的服务质量(推荐为2-确保消息到达一次。0-至多一次到达1-至少一次到达,可能重复)
* retained 默认false-非持久化(是指一条消息消费完,就会被删除;持久化,消费完,还会保存在服务器中,当新的订阅者出现,继续给新订阅者消费)
* @param topic
* @param pushMessage
*/
public void publish(int qos, String topic, String pushMessage){
publish(qos, false, topic, pushMessage);
}
}

View File

@@ -0,0 +1,35 @@
package jnpf.util.attendance;
import lombok.extern.slf4j.Slf4j;
import java.util.Base64;
@Slf4j
public class OcsWatermarkUtils {
/**
* 水印样式
*/
private static final String OCS_WATERMARK = "?watermark/2/text/%s/fill/I0Y1RjVGNQ==/fontsize/100/dissolve/17/gravity/northeast/dx/20/dy/20/batch/1/spacing/100/degree/45";
/**
* 生成样式URL
*
* @param value 水印文案
* @return 返回完整水印图片url
*/
public static String watermarkByStr(String value) {
return String.format(OCS_WATERMARK, processBase64String(convertToBase64(value)));
}
public static String convertToBase64(String imagePath) {
return Base64.getEncoder().encodeToString(imagePath.getBytes());
}
public static String processBase64String(String base64EncodedString) {
String modifiedString = base64EncodedString.replace("+", "-");
modifiedString = modifiedString.replace("/", "_");
modifiedString = modifiedString.replace("=", "");
return modifiedString;
}
}

View File

@@ -0,0 +1,19 @@
package jnpf.util.attendance;
import java.util.HashSet;
import java.util.Set;
/**
* 权限工具类
*/
public class PermissionUtil {
/** 获取权限模版*/
public static Set<String> getPermissionTemplate(String... permissions) {
Set<String> permissionSet = new HashSet<>();
permissionSet.forEach(permission -> {
permissionSet.add(permission);
});
return permissionSet;
}
}

View File

@@ -0,0 +1,76 @@
package jnpf.util.attendance;
import jnpf.config.MqttConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* PushCallback
*
* @author yanwenfu
* @create 2024-04-09
*/
@Slf4j
public class PushCallback implements MqttCallback {
private MqttPushClient client;
private MqttConfiguration mqttConfiguration;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public PushCallback(MqttPushClient client, MqttConfiguration mqttConfiguration) {
this.client = client;
this.mqttConfiguration = mqttConfiguration;
}
@Override
public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
if(client != null) {
scheduler.scheduleAtFixedRate(() -> {
try {
MqttPushClient mqttPushClient = new MqttPushClient();
mqttPushClient.connect(mqttConfiguration);
if(MqttPushClient.getClient().isConnected()) {
log.info("=============>>重连成功");
scheduler.shutdown();
}
} catch (Exception e) {
log.error("=============>>>[MQTT] 连接断开,重连失败!<<=============");
}
}, 20, 20, TimeUnit.SECONDS);
}
log.info(cause.getMessage());
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
//publish后会执行到这里
log.info("publish后会执行到这里");
log.info("pushComplete==============>>>" + token.isComplete());
}
/**
* 监听对应的主题消息
* @param topic
* @param message
* @throws Exception
*/
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
// subscribe后得到的消息会执行到这里面
String Payload = new String(message.getPayload());
log.info("============》》接收消息主题 : " + topic);
log.info("============》》接收消息Qos : " + message.getQos());
log.info("============》》接收消息内容 : " + Payload);
log.info("============》》接收ID : " + message.getId());
log.info("接收数据结束 下面可以执行数据处理操作");
}
}

View File

@@ -0,0 +1,25 @@
package jnpf.util.attendance;
import jnpf.enums.attendance.ScheduleImportEnum;
import jnpf.model.attendance.model.ScheduleImportFailModel;
import org.apache.commons.compress.utils.Lists;
import java.util.ArrayList;
import java.util.List;
public class RuleExcelImportUtil {
public static Boolean addFail(Boolean isTrue, ScheduleImportEnum scheduleImportEnum, String userName, List<ScheduleImportFailModel> failList) {
if (!isTrue) {
return false;
}
if (failList.stream().anyMatch(model -> model.getError().equals(scheduleImportEnum))) {
failList.stream().filter(model -> model.getError().equals(scheduleImportEnum)).findFirst().ifPresent(scheduleImportFailModel -> scheduleImportFailModel.getUserNames().add(userName));
return true;
}
ArrayList<String> objects = Lists.newArrayList();
objects.add(userName);
failList.add(ScheduleImportFailModel.builder().error(scheduleImportEnum).userNames(objects).build());
return true;
}
}

View File

@@ -0,0 +1,443 @@
package jnpf.util.attendance;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import jnpf.attendance.service.RuleScopeService;
import jnpf.base.ActionResult;
import jnpf.entity.attendance.AttendanceRuleScope;
import jnpf.enums.attendance.ScopeBizType;
import jnpf.exception.HandleException;
import jnpf.model.attendance.dto.UpdateChangeDto;
import jnpf.model.attendance.dto.UserOrgDto;
import jnpf.model.personnels.dto.turnover.FtbDepUserDTO;
import jnpf.permission.V2UserApi;
import jnpf.permission.eum.v2.UserWorkStatusEnums;
import jnpf.permission.vo.v2.user.UserBoundInfoVO;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.personnels.service.FtbPersonnelsTurnoverManagementService;
import jnpf.util.ConstantUtil;
import jnpf.util.FtbUtil;
import jnpf.util.StringUtil;
import jnpf.util.UserProvider;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
/**
* 规则适配范围工具类
*
* @author yanwenfu
* @create 2025-09-18
*/
@Component
public class RuleScopeUtil {
@Resource
private RuleScopeService ruleScopeService;
@Autowired
private V2UserApi v2UserApi;
@Resource
private FtbPersonnelsTurnoverManagementService ftbPersonnelsTurnoverManagementService;
public AttendanceRuleScope getRuleScope(String ruleId, String value, Integer ruleScope, ScopeBizType scopeBizType) {
return getRuleScope(ruleId, value, ruleScope, scopeBizType, null);
}
/**
* 生成适配范围
*
* @param value 值
* @param ruleScope 适配范围
* @param scopeBizType 业务类型
* @return jnpf.entity.attendance.AttendanceRuleScope
*/
public AttendanceRuleScope getRuleScope(String ruleId, String value, Integer ruleScope, ScopeBizType scopeBizType, String leaveTypeId) {
AttendanceRuleScope attendanceRuleScope = new AttendanceRuleScope();
attendanceRuleScope.setId(FtbUtil.getId());
attendanceRuleScope.setBizType(scopeBizType.getValue());
attendanceRuleScope.setRuleId(ruleId);
attendanceRuleScope.setExpand(leaveTypeId);
attendanceRuleScope.setScopeType(ruleScope);
attendanceRuleScope.setScopeValue(value);
attendanceRuleScope.setPriority(ruleScope);
attendanceRuleScope.setCreatorUserId(UserProvider.getLoginUserId());
attendanceRuleScope.setLastModifyUserId(UserProvider.getLoginUserId());
attendanceRuleScope.setDeleteMark(ConstantUtil.NUM_FALSE);
return attendanceRuleScope;
}
public void coverRuleScope(String ruleId, Integer ruleScope, MutablePair<List<String>, List<String>> dataList, ScopeBizType bizType) {
coverRuleScope(ruleId, ruleScope, dataList, bizType, null);
}
/**
* 覆盖适配范围(适用于加班&公休&假期)
*
* @param ruleId 规则id
* @param ruleScope 适配范围(0: 全部, 1: 指定组织/成员)
* @param dataList 组织列表&用户列表
* @param bizType 适配范围业务类型
*/
public void coverRuleScope(String ruleId, Integer ruleScope, MutablePair<List<String>, List<String>> dataList, ScopeBizType bizType, String leaveTypeId) {
if (ConstantUtil.SCOPE_ALL.equals(ruleScope)) {
// 删除其他相同业务类型的适配范围
ruleScopeService.remove(new LambdaQueryWrapper<AttendanceRuleScope>()
.eq(AttendanceRuleScope::getBizType, bizType.getValue())
.eq(StringUtil.isNotEmpty(leaveTypeId), AttendanceRuleScope::getExpand, leaveTypeId)
.ne(AttendanceRuleScope::getRuleId, ruleId));
} else {
if (CollectionUtils.isNotEmpty(dataList.getLeft())) {
// 查询组织下的所有人
ActionResult<List<UserBoundVO>> actionResult = v2UserApi.listTargetOrganizesOrHaveChild(dataList.getLeft(), false, List.of(UserWorkStatusEnums.RESIGNED), UserProvider.getUser().getTenantId());
if (null != actionResult && actionResult.getCode() == 200 && !actionResult.getData().isEmpty()) {
List<String> userIds = actionResult.getData().stream().map(UserBoundVO::getId).collect(Collectors.toList());
ruleScopeService.remove(new LambdaQueryWrapper<AttendanceRuleScope>()
.eq(AttendanceRuleScope::getBizType, bizType.getValue())
.eq(AttendanceRuleScope::getScopeType, ConstantUtil.RULE_SCOPE_USER)
.eq(StringUtil.isNotEmpty(leaveTypeId), AttendanceRuleScope::getExpand, leaveTypeId)
.in(AttendanceRuleScope::getScopeValue, userIds)
.ne(AttendanceRuleScope::getRuleId, ruleId));
}
ruleScopeService.remove(new LambdaQueryWrapper<AttendanceRuleScope>()
.eq(AttendanceRuleScope::getBizType, bizType.getValue())
.eq(AttendanceRuleScope::getScopeType, ConstantUtil.RULE_SCOPE_ORG)
.eq(StringUtil.isNotEmpty(leaveTypeId), AttendanceRuleScope::getExpand, leaveTypeId)
.in(AttendanceRuleScope::getScopeValue, dataList.getLeft())
.ne(AttendanceRuleScope::getRuleId, ruleId));
}
if (CollectionUtils.isNotEmpty(dataList.getRight())) {
ruleScopeService.remove(new LambdaQueryWrapper<AttendanceRuleScope>()
.eq(AttendanceRuleScope::getBizType, bizType.getValue())
.eq(AttendanceRuleScope::getScopeType, ConstantUtil.RULE_SCOPE_USER)
.eq(StringUtil.isNotEmpty(leaveTypeId), AttendanceRuleScope::getExpand, leaveTypeId)
.in(AttendanceRuleScope::getScopeValue, dataList.getRight())
.ne(AttendanceRuleScope::getRuleId, ruleId));
}
}
}
/**
* 生成适配范围列表
*
* @param ruleId 规则id
* @param ruleScope 适配范围(0: 全部, 1: 指定组织/成员)
* @param dataList 组织列表&用户列表
* @param bizType 适配范围业务类型
* @return java.util.List<jnpf.entity.attendance.AttendanceRuleScope>
*/
public List<AttendanceRuleScope> getRuleScopeList(String ruleId, Integer ruleScope, MutablePair<List<String>, List<String>> dataList, ScopeBizType bizType) throws HandleException {
return getRuleScopeList(ruleId, ruleScope, dataList, bizType, null);
}
/**
* 生成适配范围列表
*
* @param ruleId 规则id
* @param ruleScope 适配范围(0: 全部, 1: 指定组织/成员)
* @param dataList 组织列表&用户列表
* @param bizType 适配范围业务类型
* @param leaveTypeId 2.0假期类型Id列表
* @return java.util.List<jnpf.entity.attendance.AttendanceRuleScope>
*/
public List<AttendanceRuleScope> getRuleScopeList(String ruleId, Integer ruleScope, MutablePair<List<String>, List<String>> dataList, ScopeBizType bizType, String leaveTypeId) throws HandleException {
List<String> organizeList = dataList.getLeft();
List<String> userIdList = dataList.getRight();
List<AttendanceRuleScope> ruleScopeList = new ArrayList<>();
if (ruleScope.equals(ConstantUtil.SCOPE_ALL)) {
ruleScopeList.add(getRuleScope(ruleId, null, ConstantUtil.RULE_SCOPE_ALL, bizType, leaveTypeId));
} else {
if (null == organizeList) organizeList = List.of();
if (null == userIdList) userIdList = List.of();
if (organizeList.isEmpty() && userIdList.isEmpty()) {
throw new HandleException("至少选择一个组织或成员");
}
if (!organizeList.isEmpty()) {
organizeList.forEach(orgId -> ruleScopeList.add(getRuleScope(ruleId, orgId, ConstantUtil.RULE_SCOPE_ORG, bizType, leaveTypeId)));
}
if (!userIdList.isEmpty()) {
userIdList.forEach(userId -> ruleScopeList.add(getRuleScope(ruleId, userId, ConstantUtil.RULE_SCOPE_USER, bizType, leaveTypeId)));
}
}
if (bizType.equals(ScopeBizType.OVERTIME_RULE) || bizType.equals(ScopeBizType.PUBLIC_HOLIDAY_RULE )|| bizType.equals(ScopeBizType.LEAVE_RULES)) {
coverRuleScope(ruleId, ruleScope, dataList, bizType, leaveTypeId);
}
return ruleScopeList;
}
public void saveBatch(List<AttendanceRuleScope> list) {
ruleScopeService.saveBatch(list);
}
/**
* 根据适配范围的变更, 更新数据库适配范围
*
* @param ruleId 加班规则id
* @param oldScope 数据库适配范围(0: 全部, 1: 指定组织/成员)
* @param newScope 更新适配范围(0: 全部, 1: 指定组织/成员)
* @param dataList 更新组织/成员数据
* @param bizType 适配范围业务类型
* @return jnpf.model.attendance.dto.UpdateChangeDto 返回变更的组织和用户[新增, 删除]
*/
public UpdateChangeDto updateRuleScopeList(String ruleId, Integer oldScope, Integer newScope, MutablePair<List<String>, List<String>> dataList, ScopeBizType bizType) throws HandleException {
return updateRuleScopeList(ruleId, oldScope, newScope, dataList, bizType, null);
}
/**
* 根据适配范围的变更, 更新数据库适配范围
*
* @param ruleId 加班规则id
* @param oldScope 数据库适配范围(0: 全部, 1: 指定组织/成员)
* @param newScope 更新适配范围(0: 全部, 1: 指定组织/成员)
* @param dataList 更新组织/成员数据
* @param bizType 适配范围业务类型
* @return jnpf.model.attendance.dto.UpdateChangeDto 返回变更的组织和用户[新增, 删除]
*/
public UpdateChangeDto updateRuleScopeList(String ruleId, Integer oldScope, Integer newScope, MutablePair<List<String>, List<String>> dataList, ScopeBizType bizType, String leaveTypeId) throws HandleException {
// 查询已有的适配范围数据
List<AttendanceRuleScope> oldDetails = ruleScopeService.list(
new LambdaQueryWrapper<AttendanceRuleScope>().eq(AttendanceRuleScope::getRuleId, ruleId)
);
if (oldScope.equals(newScope)) {
// 0 -> 0 无需更新 1 -> 1 判定是否需要更新
if (newScope.equals(ConstantUtil.SCOPE_ORG_USER)) {
Set<String> newOrgSet = new HashSet<>(dataList.getLeft());
Set<String> newUserSet = new HashSet<>(dataList.getRight());
// 数据库组织范围
Set<String> oldOrgSet = oldDetails.stream()
.filter(d -> Objects.equals(d.getScopeType(), ConstantUtil.RULE_SCOPE_ORG))
.map(AttendanceRuleScope::getScopeValue)
.collect(Collectors.toSet());
// 数据库成员范围
Set<String> oldUserSet = oldDetails.stream()
.filter(d -> Objects.equals(d.getScopeType(), ConstantUtil.RULE_SCOPE_USER))
.map(AttendanceRuleScope::getScopeValue)
.collect(Collectors.toSet());
// 找出要删除的
Set<String> delOrgSet = new HashSet<>(oldOrgSet);
delOrgSet.removeAll(newOrgSet);
Set<String> delUserSet = new HashSet<>(oldUserSet);
delUserSet.removeAll(newUserSet);
// 找出要新增的
Set<String> addOrgSet = new HashSet<>(newOrgSet);
addOrgSet.removeAll(oldOrgSet);
Set<String> addUserSet = new HashSet<>(newUserSet);
addUserSet.removeAll(oldUserSet);
// 删除
if (!delOrgSet.isEmpty()) {
ruleScopeService.remove(new LambdaQueryWrapper<AttendanceRuleScope>()
.eq(AttendanceRuleScope::getRuleId, ruleId)
.eq(AttendanceRuleScope::getScopeType, ConstantUtil.RULE_SCOPE_ORG)
.in(AttendanceRuleScope::getScopeValue, delOrgSet));
}
if (!delUserSet.isEmpty()) {
ruleScopeService.remove(new LambdaQueryWrapper<AttendanceRuleScope>()
.eq(AttendanceRuleScope::getRuleId, ruleId)
.eq(AttendanceRuleScope::getScopeType, ConstantUtil.RULE_SCOPE_USER)
.in(AttendanceRuleScope::getScopeValue, delUserSet));
}
// 新增
List<String> addOrgList = new ArrayList<>(addOrgSet);
List<String> addUserList = new ArrayList<>(addUserSet);
if (!addOrgList.isEmpty() || !addUserList.isEmpty()) {
List<AttendanceRuleScope> insertList = getRuleScopeList(ruleId, newScope, MutablePair.of(addOrgList, addUserList), bizType, leaveTypeId);
if (!insertList.isEmpty()) {
ruleScopeService.saveBatch(insertList);
}
}
UpdateChangeDto updateChange = new UpdateChangeDto();
updateChange.getAddOrgList().addAll(addOrgList);
updateChange.getAddUserList().addAll(addUserList);
updateChange.getDelOrgList().addAll(delOrgSet);
updateChange.getDelUserList().addAll(delUserSet);
return updateChange;
}
} else {
// 范围变更
ruleScopeService.remove(new LambdaQueryWrapper<AttendanceRuleScope>()
.eq(AttendanceRuleScope::getRuleId, ruleId));
if (Objects.equals(newScope, ConstantUtil.SCOPE_ALL)) {
// 1 -> 0 删除原有记录, 生成一条全部范围的记录
AttendanceRuleScope allScope = getRuleScope(ruleId, null, ConstantUtil.RULE_SCOPE_ALL, bizType, leaveTypeId);
ruleScopeService.save(allScope);
} else if (Objects.equals(newScope, ConstantUtil.SCOPE_ORG_USER)) {
// 0 -> 1 删除原有记录, 按选择的组织和成员生成新记录
List<AttendanceRuleScope> insertList = getRuleScopeList(ruleId, newScope, dataList, bizType, leaveTypeId);
if (!insertList.isEmpty()) {
ruleScopeService.saveBatch(insertList);
}
UpdateChangeDto updateChange = new UpdateChangeDto();
updateChange.getAddOrgList().addAll(dataList.getLeft());
updateChange.getAddUserList().addAll(dataList.getRight());
return updateChange;
}
}
if (bizType.equals(ScopeBizType.OVERTIME_RULE) || bizType.equals(ScopeBizType.PUBLIC_HOLIDAY_RULE)) {
coverRuleScope(ruleId, newScope, dataList, bizType);
}
return new UpdateChangeDto();
}
/**
* 根据规则id查询适配范围[组织, 用户]
* 适配范围是全部返回null
*
* @param ruleId 规则id
* @param bizType 规则类型
* @return org.apache.commons.lang3.tuple.MutablePair<java.util.List < java.lang.String>,java.util.List<java.lang.String>>
*/
public MutablePair<List<String>, List<String>> selectScopeList(String ruleId, ScopeBizType bizType) {
List<AttendanceRuleScope> list = ruleScopeService.list(new LambdaQueryWrapper<AttendanceRuleScope>()
.eq(AttendanceRuleScope::getRuleId, ruleId)
.eq(AttendanceRuleScope::getBizType, bizType.getValue())
.orderByDesc(AttendanceRuleScope::getLastModifyTime));
return getList(list);
}
/**
* 根据规则id集合查询适配范围
* Map<规则id, [组织, 用户]>
*
* @param ruleIds 规则id集合
* @param bizType 规则类型
* @return java.util.Map<java.lang.String, org.apache.commons.lang3.tuple.MutablePair < java.util.List < java.lang.String>,java.util.List<java.lang.String>>>
*/
public Map<String, MutablePair<List<String>, List<String>>> selectScopeListBatch(List<String> ruleIds, ScopeBizType bizType) {
Map<String, MutablePair<List<String>, List<String>>> returnMap = new HashMap<>();
List<AttendanceRuleScope> list = ruleScopeService.list(new LambdaQueryWrapper<AttendanceRuleScope>()
.in(AttendanceRuleScope::getRuleId, ruleIds)
.eq(AttendanceRuleScope::getBizType, bizType.getValue())
.orderByDesc(AttendanceRuleScope::getLastModifyTime));
ConcurrentMap<String, List<AttendanceRuleScope>> map = list.stream().collect(Collectors.groupingByConcurrent(AttendanceRuleScope::getRuleId));
map.forEach((k, v) -> returnMap.put(k, getList(v)));
return returnMap;
}
private MutablePair<List<String>, List<String>> getList(List<AttendanceRuleScope> list) {
AttendanceRuleScope scope = list.stream().filter(v -> v.getScopeType().equals(ConstantUtil.RULE_SCOPE_ALL)).findFirst().orElse(null);
if (null != scope) {
return null;
}
List<String> orgList = new ArrayList<>();
List<String> userList = new ArrayList<>();
list.forEach(v -> {
if (v.getScopeType().equals(ConstantUtil.RULE_SCOPE_ORG)) {
orgList.add(v.getScopeValue());
}
if (v.getScopeType().equals(ConstantUtil.RULE_SCOPE_USER)) {
userList.add(v.getScopeValue());
}
});
return MutablePair.of(orgList, userList);
}
/**
* 查询用户生效中的规则
*
* @param userId 用户id
* @param bizType 业务类型
* @return java.util.List<jnpf.entity.attendance.AttendanceRuleScope>
*/
public List<AttendanceRuleScope> selectUserEffectList(String userId, ScopeBizType bizType, String tenantId) {
UserBoundInfoVO data = null;
ActionResult<UserBoundInfoVO> usersBoundResult = v2UserApi.getUsersBound(userId, tenantId);
if (null == usersBoundResult || null == usersBoundResult.getData()) {
FtbDepUserDTO dto = new FtbDepUserDTO();
dto.setUserIds(List.of(userId));
dto.setTenantId(tenantId);
List<UserBoundVO> person = ftbPersonnelsTurnoverManagementService.getInformationAboutTheDepartingPerson(dto);
if (null != person && !person.isEmpty()) {
data = new UserBoundInfoVO();
data.setUserId(person.get(0).getId());
data.setOrganizeId(person.get(0).getOrganizeId());
}
} else {
data = usersBoundResult.getData();
}
if (null == data) {
return List.of();
}
return ruleScopeService.selectUserEffectList(data.getUserId(), data.getOrganizeId(), bizType);
}
public List<AttendanceRuleScope> selectUserEffectList(String userId, String orgId, ScopeBizType bizType) {
return ruleScopeService.selectUserEffectList(userId, orgId, bizType);
}
/**
* 查询用户生效中的规则[批量]
*
* @param userIds 用户id集合
* @param bizType 业务类型
* @param priority 使用优先级(1: 是, 0: 否)
* @return java.util.List<jnpf.entity.attendance.AttendanceRuleScope>
*/
public List<AttendanceRuleScope> selectUserEffectListBatch(List<String> userIds, ScopeBizType bizType, Integer priority) {
return selectUserEffectListBatch(userIds, bizType, priority, null, null);
}
/**
* 查询用户生效中的规则[批量]
*
* @param userIds 用户id集合
* @param bizType 业务类型
* @param priority 使用优先级(1: 是, 0: 否)
* @return java.util.List<jnpf.entity.attendance.AttendanceRuleScope>
*/
public List<AttendanceRuleScope> selectUserEffectListBatch2(List<String> userIds, ScopeBizType bizType, Integer priority,String tenantId) {
return selectUserEffectListBatch(userIds, bizType, priority, null,tenantId);
}
public List<AttendanceRuleScope> selectUserEffectListBatch(List<String> userIds, ScopeBizType bizType, Integer priority, List<String> leaveTypeIds) {
return selectUserEffectListBatch(userIds, bizType, priority, leaveTypeIds, null);
}
/**
* 查询用户生效中的规则[批量]
*
* @param userIds 用户id集合
* @param bizType 业务类型
* @param priority 使用优先级(1: 是, 0: 否)
* @return java.util.List<jnpf.entity.attendance.AttendanceRuleScope>
*/
public List<AttendanceRuleScope> selectUserEffectListBatch(List<String> userIds, ScopeBizType bizType, Integer priority, List<String> leaveTypeIds,String tenantId) {
ActionResult<List<UserBoundVO>> batchResult = v2UserApi.getAllUserInfoBatch(userIds, Objects.requireNonNullElse(tenantId, UserProvider.getUser().getTenantId()));
if (null == batchResult || null == batchResult.getData()) {
return List.of();
}
List<UserBoundVO> data = batchResult.getData();
List<UserOrgDto> userOrgList = new ArrayList<>();
data.forEach(v -> userOrgList.add(new UserOrgDto(v.getId(), v.getOrganizeId())));
// 分批查询
List<List<UserOrgDto>> partition = Lists.partition(userOrgList, 200);
List<AttendanceRuleScope> scopeList = new ArrayList<>();
for (List<UserOrgDto> subList : partition) {
List<AttendanceRuleScope> partResult = ruleScopeService.selectUserEffectListBatch(
subList, bizType, priority, leaveTypeIds
);
if (partResult != null && !partResult.isEmpty()) {
scopeList.addAll(partResult);
}
}
return scopeList;
}
/**
* 查询组织生效中假期的规则[批量]
*
* @param oldOrganizeId 上级组织id
* @return java.util.List<jnpf.entity.attendance.AttendanceRuleScope>
*/
public List<AttendanceRuleScope> selectOrgEffectListBatch(String oldOrganizeId) {
return ruleScopeService.selectOrgEffectListBatch(oldOrganizeId);
}
}

View File

@@ -0,0 +1,50 @@
package jnpf.util.attendance;
import cn.hutool.core.collection.CollUtil;
import jnpf.entity.AttendanceGroupUser;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* 借调类型secondmentType解析工具。
* <p>统一封装 {@link AttendanceGroupUserStatusUtil#isExistStatus} 与 VO 字段 secondmentType 的映射口径。</p>
*/
public final class SecondmentTypeUtil {
private SecondmentTypeUtil() {
}
/**
* 将 {@code isExistStatus} 返回值映射为 secondmentType。
*
* @param existStatus isExistStatus 返回值(-1已离 0未入 1正常 2全天被借调 3部分被借调 4借调
* @return 0无借调 1借调入 2借调出
*/
public static int toSecondmentType(Integer existStatus) {
return Objects.equals(existStatus, 4)
? 2
: (Objects.equals(existStatus, 2) || Objects.equals(existStatus, 3) ? 1 : 0);
}
/**
* 根据考勤组成员与单日解析 secondmentType。
*/
public static int resolveSecondmentType(List<AttendanceGroupUser> users, Date date) {
if (CollUtil.isEmpty(users) || date == null) {
return 0;
}
return toSecondmentType(AttendanceGroupUserStatusUtil.isExistStatus(users, date));
}
/**
* 根据考勤组成员与时间范围解析 secondmentType。
*/
public static int resolveSecondmentType(List<AttendanceGroupUser> users, Date start, Date end) {
if (CollUtil.isEmpty(users) || start == null || end == null) {
return 0;
}
return toSecondmentType(AttendanceGroupUserStatusUtil.isExistStatus(users, start, end));
}
}

View File

@@ -0,0 +1,176 @@
package jnpf.util.attendance;
import com.alibaba.nacos.common.utils.CollectionUtils;
import jnpf.model.attendance.model.WebExportNewDetailsModel;
import jnpf.model.attendance.model.WebExportNewModel;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
public class UnionQualityExcelUtil {
/**
* 生成sheet
*
* @return
*/
public static void generateSheet(Workbook workbook, List<WebExportNewModel> exportNewModelList) {
Sheet sheet = workbook.createSheet("考勤月度统计报表");
sheet.setDisplayGridlines(true);
sheet.getPrintSetup().setLandscape(true);
CellStyle headStyle = workbook.createCellStyle();
Font headFont = workbook.createFont();
headStyle = setCellStyle(headStyle, headFont, (short) 12, true, true, HorizontalAlignment.CENTER);
//创建表头
String[] heads = {"用户名", "名称", "应出勤天数", "实际出勤天数", "有效出勤天数", "应出勤工时", "实际出勤工时", "有效出勤工时", "计薪假抵扣时长",
"不计薪假扣时长", "请假次数", "请假时长", "未抵扣请假时长", "加班次数", "加班时长", "外勤打卡次数", "缺勤次数", "缺卡次数", "补卡次数", "迟到次数"
, "迟到累计分钟", "早退次数", "早退累计分钟"};
Row headRow = sheet.createRow(0);
headRow.setHeightInPoints(25);
for (int cn = 0; cn < heads.length; cn++) {
Cell cell = headRow.createCell(cn);
cell.setCellValue(heads[cn]);
cell.setCellStyle(headStyle);
}
Font contentFont = workbook.createFont();
CellStyle contentStyle = workbook.createCellStyle();
contentStyle = setCellStyle(contentStyle, contentFont, (short) 10, true, true, HorizontalAlignment.CENTER);
CellStyle contentDetailsStyle = workbook.createCellStyle();
contentDetailsStyle = setCellStyle(contentDetailsStyle, contentFont, (short) 9, false, true, HorizontalAlignment.CENTER);
if (CollectionUtils.isNotEmpty(exportNewModelList)) {
//外层用户循环
for (int index = 0; index < exportNewModelList.size(); index++) {
WebExportNewModel newModel = exportNewModelList.get(index);
String[] a = createString(newModel);
int temp = sheet.getLastRowNum() + 1;
Row row = sheet.createRow(temp);
row.setHeightInPoints(25);
for (int i = 0; i < a.length; i++) {
Cell cell = row.createCell(i);
cell.setCellValue(a[i]);
cell.setCellStyle(contentStyle);
}
if (CollectionUtils.isNotEmpty(newModel.getDetails())) {
for (int detailIndex = 0; detailIndex < newModel.getDetails().size(); detailIndex++) {
String[] details = createString1(newModel.getDetails().get(detailIndex));
int detailsTemp = sheet.getLastRowNum() + 1;
Row detailsRow = sheet.createRow(detailsTemp);
detailsRow.setHeightInPoints(25);
for (int i = 0; i < details.length; i++) {
Cell cell = detailsRow.createCell(i);
cell.setCellValue(details[i]);
cell.setCellStyle(contentDetailsStyle);
}
}
sheet.addMergedRegion(new CellRangeAddress(temp, temp + newModel.getDetails().size(), 0, 0));
}
}
}
sheet.setColumnWidth(0, 15 * 256);
sheet.setColumnWidth(1, 15 * 256);
sheet.setColumnWidth(2, 15 * 256);
sheet.setColumnWidth(3, 17 * 256);
sheet.setColumnWidth(4, 17 * 256);
sheet.setColumnWidth(5, 17 * 256);
sheet.setColumnWidth(6, 17 * 256);
sheet.setColumnWidth(7, 17 * 256);
sheet.setColumnWidth(8, 18 * 256);
sheet.setColumnWidth(9, 18 * 256);
sheet.setColumnWidth(10, 18 * 256);
sheet.setColumnWidth(11, 18 * 256);
sheet.setColumnWidth(12, 18 * 256);
sheet.setColumnWidth(13, 17 * 256);
sheet.setColumnWidth(14, 17 * 256);
sheet.setColumnWidth(15, 17 * 256);
sheet.setColumnWidth(16, 17 * 256);
sheet.setColumnWidth(17, 17 * 256);
sheet.setColumnWidth(18, 17 * 256);
sheet.setColumnWidth(19, 17 * 256);
sheet.setColumnWidth(20, 17 * 256);
sheet.setColumnWidth(21, 17 * 256);
sheet.setColumnWidth(22, 17 * 256);
sheet.setColumnWidth(23, 17 * 256);
}
/**
* 设置单元格样式
*
* @param style
* @param font
* @param fontSize
* @param bold
* @param isBoder
* @param horizontalAlignment
* @return
*/
public static CellStyle setCellStyle(CellStyle style, Font font, short fontSize, Boolean bold, Boolean isBoder, HorizontalAlignment horizontalAlignment) {
short stringFormat = (short) BuiltinFormats.getBuiltinFormat("TEXT");
// 将字体大小设置为9px
font.setFontHeightInPoints(fontSize);
// 将字体设置为加粗
font.setBold(bold);
//字体样式
font.setFontName("宋体");
//字体大小
style.setFont(font);
// 设置单元格内容是否自动换行
style.setWrapText(true);
if (isBoder) {
//下边框
style.setBorderBottom(BorderStyle.THIN);
//左边框
style.setBorderLeft(BorderStyle.THIN);
//上边框
style.setBorderTop(BorderStyle.THIN);
//右边框
style.setBorderRight(BorderStyle.THIN);
}
//水平居中
style.setAlignment(horizontalAlignment);
//上下居中
style.setVerticalAlignment(VerticalAlignment.CENTER);
//设置自动换行
style.setWrapText(true);
style.setDataFormat(stringFormat);
return style;
}
private static String[] createString(WebExportNewModel exportDTO) {
String[] a = {exportDTO.getUserName(), exportDTO.getName(), exportDTO.getShouldAttendDays().toString(), compareNumber(exportDTO.getActualAttendDays()),
compareNumber(exportDTO.getEffectiveAttendDays()), compareNumber(exportDTO.getShouldAttendHours()), compareNumber(exportDTO.getActualAttendHours()),
compareNumber(exportDTO.getEffectiveAttendHours()), compareNumber(exportDTO.getPaidLeaveHours()), compareNumber(exportDTO.getUnpaidLeaveHours()),
exportDTO.getLeaveTimes().toString(), compareNumber(exportDTO.getLeaveHours()), compareNumber(exportDTO.getUnDeductedLeaveHours()),
exportDTO.getOvertimeTimes().toString(), compareNumber(exportDTO.getOvertimeHours()), exportDTO.getOutworkClockTimes().toString(), exportDTO.getAbsenceTimes().toString(),
exportDTO.getAbsenceCardTimes().toString(), exportDTO.getMakeUpCardTimes().toString(), exportDTO.getLateTimes().toString(), exportDTO.getLateAccumulatedMinutes().toString(),
exportDTO.getEarlyLeaveTimes().toString(), exportDTO.getEarlyLeaveAccumulatedMinutes().toString()
};
return a;
}
private static String[] createString1(WebExportNewDetailsModel exportDTO) {
String[] a = {exportDTO.getUserName(), exportDTO.getName(), exportDTO.getShouldAttendDays().toString(), compareNumber(exportDTO.getActualAttendDays()),
compareNumber(exportDTO.getEffectiveAttendDays()), compareNumber(exportDTO.getShouldAttendHours()), compareNumber(exportDTO.getActualAttendHours()),
compareNumber(exportDTO.getEffectiveAttendHours()), compareNumber(exportDTO.getPaidLeaveHours()), compareNumber(exportDTO.getUnpaidLeaveHours()),
exportDTO.getLeaveTimes().toString(), compareNumber(exportDTO.getLeaveHours()), compareNumber(exportDTO.getUnDeductedLeaveHours()),
exportDTO.getOvertimeTimes().toString(), compareNumber(exportDTO.getOvertimeHours()), exportDTO.getOutworkClockTimes().toString(), exportDTO.getAbsenceTimes().toString(),
exportDTO.getAbsenceCardTimes().toString(), exportDTO.getMakeUpCardTimes().toString(), exportDTO.getLateTimes().toString(), exportDTO.getLateAccumulatedMinutes().toString(),
exportDTO.getEarlyLeaveTimes().toString(), exportDTO.getEarlyLeaveAccumulatedMinutes().toString()
};
return a;
}
public static String compareNumber(BigDecimal number) {
if (!"".equals(number) && number != null) {
if (new BigDecimal(number.intValue()).compareTo(number) == 0) {
//整数
return String.valueOf(number.intValue());
} else {
//小数
return String.valueOf(number.setScale(2, RoundingMode.HALF_UP));
}
}
return "";
}
}

View File

@@ -0,0 +1,23 @@
package jnpf.util.auth;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* 对外 HTTP 与 Feign 契约:{@link V2AuthPermissionApi}
*/
@RestController
@RequestMapping("/permission/auth")
public class V2AuthPermissionController implements V2AuthPermissionApi {
@Resource
private V2AuthPermissionUtils v2AuthPermissionUtils;
@Override
public List<String> getLoginUserAuthOrganizeIds() {
return v2AuthPermissionUtils.getLoginUserAuthOrganizeIds();
}
}

View File

@@ -0,0 +1,414 @@
package jnpf.util.auth;
import cn.hutool.core.collection.CollUtil;
import jnpf.authority.utils.PermissionsApplicableEnums;
import jnpf.authority.utils.PermissionsApplicableObject;
import jnpf.authority.utils.PermissionsUtils;
import jnpf.base.ActionResult;
import jnpf.cultivate.utils.UserApiV2Util;
import jnpf.permission.V2OrganizeApi;
import jnpf.permission.V2UserApi;
import jnpf.permission.dto.v2.organzie.QueryOrganizeListTargetTypesDTO;
import jnpf.permission.eum.v2.OrganizeCategoryEnums;
import jnpf.permission.eum.v2.TargetAuthEnums;
import jnpf.permission.eum.v2.UserWorkStatusEnums;
import jnpf.permission.vo.v2.TargetAuthIdsVO;
import jnpf.permission.vo.v2.organzie.OrganizeGeneralDetailVO;
import jnpf.permission.vo.v2.user.UserBoundInfoVO;
import jnpf.permission.vo.v2.user.UserBoundVO;
import jnpf.permission.vo.v2.user.UserPageListVO;
import jnpf.util.StringUtil;
import jnpf.util.UserProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 权限校验公共工具
*
* @author Flynn Chan
* @create 2025-05-20
*/
@Component
@Slf4j
public class V2AuthPermissionUtils {
@Resource
private PermissionsUtils permissionsUtils;
@Resource
private V2OrganizeApi organizeV2Api;
@Resource
private V2UserApi userV2Api;
/**
* 获取登录人权限范围内的门店id/组织id, null为全部,[]为无, 人的权限都算[]
*/
public List<String> getLoginUserAuthOrganizeIds() {
String userId = UserProvider.getLoginUserId();
//超级管理员也返回null,也获取全部
if (UserProvider.getUser().getIsAdministrator()) {
return null;
}
PermissionsApplicableObject applicableObject = permissionsUtils.obtainTheScopeOfUserPermissionsEnums(userId);
if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.ALL) {
//全部返回null
return null;
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_ORGANIZATION_AND_SUBORDINATE_EMPLOYEES) {
UserBoundInfoVO usersBound = userV2Api.getUsersBound(userId, null).getData();
if (null != usersBound) {
List<OrganizeGeneralDetailVO> organizeGeneralDetailVOS = organizeV2Api.organizesOrHaveChildByOrganizeIds(List.of(usersBound.getOrganizeId()), true, null).getData();
//过滤班组
organizeGeneralDetailVOS = organizeGeneralDetailVOS.stream().filter(ctx -> !OrganizeCategoryEnums.TEAM.equals(ctx.getOrganizeCategoryEnums())).collect(Collectors.toList());
return organizeGeneralDetailVOS.stream().map(OrganizeGeneralDetailVO::getId).collect(Collectors.toList());
} else {
return new ArrayList<>();
}
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_ORGANIZATION_EMPLOYEES) {
return new ArrayList<>();
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_SUBORDINATE) {
return new ArrayList<>();
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_SPECIFIC_ORGANIZATION) {
return applicableObject.getOrgIds();
} else {
log.error("未得到用户[" + userId + "]对应权限!");
return new ArrayList<>();
}
}
/**
* 获取当前登录人权限
*
* @param sourceCategoryEnum
* @return
*/
public TargetAuthIdsVO processAuthIds() {
TargetAuthIdsVO targetAuthIdsVO = new TargetAuthIdsVO();
//停用 主动传userid
String userId = UserProvider.getLoginUserId();
//超级管理员也返回null,也获取全部
if (UserProvider.getUser().getIsAdministrator()) {
return null;
}
PermissionsApplicableObject applicableObject = permissionsUtils.obtainTheScopeOfUserPermissionsEnums(userId);
if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.ALL) {
//全部返回null
return null;
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_ORGANIZATION_AND_SUBORDINATE_EMPLOYEES) {
targetAuthIdsVO.setTargetAuthEnums(TargetAuthEnums.ORGANIZE);
UserBoundInfoVO usersBound = userV2Api.getUsersBound(userId, null).getData();
if (null != usersBound) {
List<OrganizeGeneralDetailVO> organizeGeneralDetailVOS = organizeV2Api.organizesOrHaveChildByOrganizeIds(List.of(usersBound.getOrganizeId()), true, null).getData();
//过滤班组
organizeGeneralDetailVOS = organizeGeneralDetailVOS.stream().filter(ctx -> !OrganizeCategoryEnums.TEAM.equals(ctx.getOrganizeCategoryEnums())).collect(Collectors.toList());
targetAuthIdsVO.setIds(organizeGeneralDetailVOS.stream().map(OrganizeGeneralDetailVO::getId).collect(Collectors.toList()));
} else {
targetAuthIdsVO.setIds(new ArrayList<>());
}
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_ORGANIZATION_EMPLOYEES) {
targetAuthIdsVO.setTargetAuthEnums(TargetAuthEnums.USER);
UserBoundInfoVO usersBound = userV2Api.getUsersBound(userId, null).getData();
if (null != usersBound) {
//当前组织的人 todo 这里处理冗余没效率,后期可以优化
List<UserBoundVO> userBoundVOList = userV2Api.listTargetOrganizesOrHaveChild(List.of(usersBound.getOrganizeId()), false, UserWorkStatusEnums.getAllUserWorkStatusEnums(), null).getData();
targetAuthIdsVO.setIds(userBoundVOList.stream().map(UserBoundVO::getId).distinct().collect(Collectors.toList()));
} else {
targetAuthIdsVO.setIds(new ArrayList<>());
}
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_SUBORDINATE) {
targetAuthIdsVO.setTargetAuthEnums(TargetAuthEnums.USER);
List<UserPageListVO> userPageListVO = userV2Api.listUnderlingTargetUser(userId, null).getData();
targetAuthIdsVO.setIds(userPageListVO.stream().map(UserPageListVO::getId).collect(Collectors.toList()));
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_SPECIFIC_ORGANIZATION) {
targetAuthIdsVO.setTargetAuthEnums(TargetAuthEnums.ORGANIZE);
targetAuthIdsVO.setIds(applicableObject.getOrgIds());
} else {
log.error("未得到用户[" + userId + "]对应权限!");
targetAuthIdsVO.setTargetAuthEnums(TargetAuthEnums.NONE);
targetAuthIdsVO.setIds(new ArrayList<>());
}
return targetAuthIdsVO;
}
/**
* 批量查询用户的权限范围的门店
*
* @param userIds
* @param status 状态 1禁用 0启用 -1-所有
* @return
*/
public Map<String, List<String>> batchAuthOrganizesForUserIds(List<String> userIds, Integer status) {
log.info("[批量]未登录人的门店,userIds={}", userIds);
List<String> cacheAllOrgIds = new ArrayList<>();//所有门店id
QueryOrganizeListTargetTypesDTO dto = new QueryOrganizeListTargetTypesDTO();
dto.setOrganizeCategoryEnums(List.of(OrganizeCategoryEnums.STORE));
ActionResult<List<OrganizeGeneralDetailVO>> listActionResult = organizeV2Api.listOrganizeByTargetTypes(dto);
if (listActionResult != null && CollUtil.isNotEmpty(listActionResult.getData())) {
for (OrganizeGeneralDetailVO vo : listActionResult.getData()) {
if (status.equals(-1)) {
cacheAllOrgIds.add(vo.getId());
} else if (status.equals(1)) {
if (!vo.getEnabled()) {
cacheAllOrgIds.add(vo.getId());
}
} else if (status.equals(0)) {
if (vo.getEnabled()) {
cacheAllOrgIds.add(vo.getId());
}
}
}
}
Map<String, List<String>> cacheOrgIds = new HashMap<>();
Map<String, List<String>> returnMap = new HashMap<>();
Map<String, UserBoundVO> userPrimaryBoundBatchReturnMap = getUserPrimaryBoundBatchReturnMap(userIds, UserProvider.getUser().getTenantId());
String moduleId = getModuleForHeader();
for (String userId : userIds) {
returnMap.put(userId, new ArrayList<>());
//判断用户是否是管理员
UserBoundVO usersBound = userPrimaryBoundBatchReturnMap.get(userId);
if (null == usersBound) {
continue;
}
if (usersBound.getIsAdministrator()) {
returnMap.put(userId, cacheAllOrgIds);
continue;
}
try {
PermissionsApplicableObject applicableObject = permissionsUtils.obtainTheScopeOfUserPermissionsEnums(userId, moduleId,UserProvider.getUser().getTenantId());
if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.ALL) {
returnMap.put(userId, cacheAllOrgIds);
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_ORGANIZATION_AND_SUBORDINATE_EMPLOYEES) {
List<String> cacheIds = cacheOrgIds.get(usersBound.getOrganizeId());
if (CollUtil.isNotEmpty(cacheIds)) {
returnMap.put(userId, cacheIds);
continue;
}
List<OrganizeGeneralDetailVO> organizeGeneralDetailVOS = organizeV2Api.organizesOrHaveChildByOrganizeIds(List.of(usersBound.getOrganizeId()), true, null).getData();
//过滤门店
cacheIds = new ArrayList<>();
if (CollUtil.isNotEmpty(organizeGeneralDetailVOS)) {
for (OrganizeGeneralDetailVO organizeGeneralDetailVO : organizeGeneralDetailVOS) {
if (OrganizeCategoryEnums.STORE.equals(organizeGeneralDetailVO.getOrganizeCategoryEnums())) {
if (status.equals(-1)) {
cacheIds.add(organizeGeneralDetailVO.getId());
} else if (status.equals(1)) {
if (!organizeGeneralDetailVO.getEnabled()) {
cacheIds.add(organizeGeneralDetailVO.getId());
}
} else if (status.equals(0)) {
if (organizeGeneralDetailVO.getEnabled()) {
cacheIds.add(organizeGeneralDetailVO.getId());
}
}
}
}
cacheOrgIds.put(usersBound.getOrganizeId(), cacheIds);
returnMap.put(userId, cacheIds);
}
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_ORGANIZATION_EMPLOYEES) {
if (cacheAllOrgIds.contains(usersBound.getOrganizeId())) {
returnMap.put(userId, List.of(usersBound.getOrganizeId()));
}
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_SUBORDINATE) {
List<UserPageListVO> userPageListVO = userV2Api.listUnderlingTargetUser(userId, null).getData();
if(CollUtil.isNotEmpty(userPageListVO)){
List<String> cacheIds = new ArrayList<>();
for (UserPageListVO pageListVO : userPageListVO) {
if(cacheAllOrgIds.contains(pageListVO.getOrganizeId())){
cacheIds.add(pageListVO.getOrganizeId());
}
}
returnMap.put(userId, cacheIds);
}
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_SPECIFIC_ORGANIZATION) {
returnMap.put(userId, UserApiV2Util.getIntersection(applicableObject.getOrgIds(), cacheAllOrgIds));
}
} catch (Exception e) {
e.printStackTrace();
log.error("[批量]未登录人的门店,userId={},e={}", userId, e);
}
}
log.info("[批量]未登录人的门店,returnMap= {}", returnMap);
return returnMap;
}
/**
* 批量用户有权限的门店
*
* @param userIds 用户ids
* @param status 状态 1禁用 0启用 -1-所有
* @param moduleId 模块id
* @param tenantId 租户id
* @return
*/
public Map<String, List<String>> batchAuthOrganizesForUserIdsAndTenantId(List<String> userIds, Integer status, String moduleId, String tenantId) {
return batchAuthOrganizesForUserIdsAndTenantId(userIds, List.of(OrganizeCategoryEnums.STORE), status, moduleId, tenantId);
}
public Map<String, List<String>> batchAuthOrganizesAll(List<String> userIds, Integer status, String moduleId, String tenantId) {
return batchAuthOrganizesForUserIdsAndTenantId(userIds, List.of(OrganizeCategoryEnums.STORE, OrganizeCategoryEnums.DEPARTMENT, OrganizeCategoryEnums.COMPANY), status, moduleId, tenantId);
}
/**
* 批量用户有权限的门店
*
* @param userIds 用户ids
* @param status 状态 1禁用 0启用 -1-所有
* @param moduleId 模块id
* @param tenantId 租户id
* @return
*/
public Map<String, List<String>> batchAuthOrganizesForUserIdsAndTenantId(List<String> userIds, List<OrganizeCategoryEnums> organizeCategoryEnums, Integer status, String moduleId, String tenantId) {
log.info("[批量]未登录人的门店,userIds={}", userIds);
List<String> cacheAllOrgIds = new ArrayList<>();//所有门店id
QueryOrganizeListTargetTypesDTO dto = new QueryOrganizeListTargetTypesDTO();
dto.setTenantId(tenantId);
dto.setOrganizeCategoryEnums(organizeCategoryEnums);
ActionResult<List<OrganizeGeneralDetailVO>> listActionResult = organizeV2Api.listOrganizeByTargetTypes(dto);
if (listActionResult != null && CollUtil.isNotEmpty(listActionResult.getData())) {
for (OrganizeGeneralDetailVO vo : listActionResult.getData()) {
if (status.equals(-1)) {
cacheAllOrgIds.add(vo.getId());
} else if (status.equals(1)) {
if (!vo.getEnabled()) {
cacheAllOrgIds.add(vo.getId());
}
} else if (status.equals(0)) {
if (vo.getEnabled()) {
cacheAllOrgIds.add(vo.getId());
}
}
}
}
Map<String, List<String>> cacheOrgIds = new HashMap<>();
Map<String, List<String>> returnMap = new HashMap<>();
Map<String, UserBoundVO> userPrimaryBoundBatchReturnMap = getUserPrimaryBoundBatchReturnMap(userIds,tenantId);
// Map<String, PermissionsApplicableObject> objectMap = permissionsUtils.obtainTheScopeOfUserPermissionsEnums(userIds, moduleId, tenantId);
for (String userId : userIds) {
returnMap.put(userId, new ArrayList<>());
//判断用户是否是管理员
UserBoundVO usersBound = userPrimaryBoundBatchReturnMap.get(userId);
if (null == usersBound) {
continue;
}
if (usersBound.getIsAdministrator()) {
returnMap.put(userId, cacheAllOrgIds);
continue;
}
try {
PermissionsApplicableObject applicableObject = permissionsUtils.obtainTheScopeOfUserPermissionsEnums(userId, moduleId, tenantId);
log.error("permissionsApplicableEnums={}", applicableObject.getPermissionsApplicableEnums());
if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.ALL) {
returnMap.put(userId, cacheAllOrgIds);
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_ORGANIZATION_AND_SUBORDINATE_EMPLOYEES) {
List<String> cacheIds = cacheOrgIds.get(usersBound.getOrganizeId());
if (CollUtil.isNotEmpty(cacheIds)) {
returnMap.put(userId, cacheIds);
continue;
}
List<OrganizeGeneralDetailVO> organizeGeneralDetailVOS = organizeV2Api.organizesOrHaveChildByOrganizeIds(List.of(usersBound.getOrganizeId()), true, tenantId).getData();
//过滤门店
cacheIds = new ArrayList<>();
if (CollUtil.isNotEmpty(organizeGeneralDetailVOS)) {
for (OrganizeGeneralDetailVO organizeGeneralDetailVO : organizeGeneralDetailVOS) {
if (OrganizeCategoryEnums.STORE.equals(organizeGeneralDetailVO.getOrganizeCategoryEnums())) {
if (status.equals(-1)) {
cacheIds.add(organizeGeneralDetailVO.getId());
} else if (status.equals(1)) {
if (!organizeGeneralDetailVO.getEnabled()) {
cacheIds.add(organizeGeneralDetailVO.getId());
}
} else if (status.equals(0)) {
if (organizeGeneralDetailVO.getEnabled()) {
cacheIds.add(organizeGeneralDetailVO.getId());
}
}
}
}
cacheOrgIds.put(usersBound.getOrganizeId(), cacheIds);
returnMap.put(userId, cacheIds);
}
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_ORGANIZATION_EMPLOYEES) {
if (cacheAllOrgIds.contains(usersBound.getOrganizeId())) {
returnMap.put(userId, List.of(usersBound.getOrganizeId()));
}
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_SUBORDINATE) {
List<UserPageListVO> userPageListVO = userV2Api.listUnderlingTargetUser(userId, tenantId).getData();
if (CollUtil.isNotEmpty(userPageListVO)) {
List<String> cacheIds = new ArrayList<>();
for (UserPageListVO pageListVO : userPageListVO) {
if (cacheAllOrgIds.contains(pageListVO.getOrganizeId())) {
cacheIds.add(pageListVO.getOrganizeId());
}
}
returnMap.put(userId, cacheIds);
}
} else if (applicableObject.getPermissionsApplicableEnums() == PermissionsApplicableEnums.SCOPE_SPECIFIC_ORGANIZATION) {
returnMap.put(userId, UserApiV2Util.getIntersection(applicableObject.getOrgIds(), cacheAllOrgIds));
}
} catch (Exception e) {
e.printStackTrace();
log.error("[批量]未登录人的门店,userId={},e={}", userId, e);
}
}
log.info("[批量]未登录人的门店,returnMap= {}", returnMap);
return returnMap;
}
/**
* 批量获取用户信息
*
* @param userIds 用户id列表
* @param tenantId
* @return 用户信息映射key为用户IDvalue为用户信息对象
*/
public Map<String, UserBoundVO> getUserPrimaryBoundBatchReturnMap(List<String> userIds, String tenantId) {
Map<String, UserBoundVO> map = new HashMap<>();
if (CollUtil.isEmpty(userIds)) {
return map;
}
if (StringUtils.isEmpty(tenantId)) {
tenantId = UserProvider.getUser().getTenantId();
}
ActionResult<List<UserBoundVO>> userPrimaryBoundBatch = userV2Api.getUserPrimaryBoundBatch(userIds, tenantId);
if (userPrimaryBoundBatch == null || CollUtil.isEmpty(userPrimaryBoundBatch.getData())) {
return map;
}
for (UserBoundVO userPrimaryBoundVO : userPrimaryBoundBatch.getData()) {
map.put(userPrimaryBoundVO.getId(), userPrimaryBoundVO);
}
return map;
}
/**
* 从header头中获取module
*
* @return
*/
private String getModuleForHeader() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes == null) {
throw new RuntimeException("请传入权限菜单id");
}
String module = attributes.getRequest().getHeader("Module");
if (StringUtil.isEmpty(module)) {
throw new RuntimeException("请传入权限菜单id");
}
return module;
}
public Map<String, PermissionsApplicableObject> batchAuthOrganizesAllForUserIds(List<String> userIds) {
return permissionsUtils.obtainTheScopeOfUserPermissionsEnums(userIds, getModuleForHeader(), UserProvider.getUser().getTenantId());
}
}

View File

@@ -0,0 +1,359 @@
package jnpf.util.excel;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.util.MapUtils;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import jnpf.model.personnels.bo.FtbRosterImportTemplateBO;
import jnpf.personnels.listeners.BaseEasyExcelCommonListener;
import jnpf.personnels.listeners.BaseEasyExcelReadListener;
import lombok.Data;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.poifs.filesystem.FileMagic;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.UrlResource;
import org.springframework.util.ResourceUtils;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* excel工具类
*
* @author fantaibao
* @date 2024/02/02
*/
@UtilityClass
@Slf4j
public class EasyExcelUtils {
public static final int BUFFER_SIZE = 4096;
/**
* 校验上传的文件是否是Excel文件及是否是xls或xlsx格式
*
* @param file 文件
*/
public InputStream checkExcelFile(String file) {
try {
URL url = ResourceUtils.getURL(file);
UrlResource urlResource = new UrlResource(url);
// 根据Excel魔数判断该文件是否为Excel文件
InputStream inputStream = urlResource.getInputStream();
InputStream fileMagics = FileMagic.prepareToCheckMagic(inputStream);
FileMagic fileMagic = FileMagic.valueOf(fileMagics);
// FileMagic.OLE2表示xls格式 FileMagic.OOXML表示xlsx格式
if (Objects.equals(fileMagic, FileMagic.OLE2) || Objects.equals(fileMagic, FileMagic.OOXML)) {
return fileMagics;
} else {
log.error("file format error,please upload an Excel file in xls or xlsx format");
throw new RuntimeException("文件格式错误请上传xls或xlsx格式的Excel文件");
}
} catch (Exception e) {
log.error("请上传xls或xlsx格式的Excel文件", e);
throw new RuntimeException(e.getMessage());
}
}
/**
* 导出excel
* @param response 响应
* @param fileName 文件名
* @param list 数据
* @throws IOException
*/
public <T> void exportExcel(HttpServletResponse response, String fileName, List<T> list,Class<T> tClass) throws IOException {
String fileRealName = URLEncoder.encode(fileName+System.currentTimeMillis()/1000, "UTF-8").replaceAll("\\+", "%20");
try(ServletOutputStream outputStream = response.getOutputStream()){
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileRealName + ".xlsx");
EasyExcel.write(outputStream, tClass)
.registerWriteHandler(new EasyExcelUtils.CustomCellWriteHandler())
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.inMemory(true)
.sheet("sheet1").doWrite(list);
}
}
/**
* 动态表头生成(通过模板)
*/
public void dynamicHeaderGeneration(HttpServletResponse httpServletResponse,
String fileNameOriginal, String fileSource,
List<FtbRosterImportTemplateBO> ftbRosterImportTemplateBOS, String employeeType) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题请直接用浏览器或者用postman
httpServletResponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
httpServletResponse.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode(fileNameOriginal + System.currentTimeMillis(), "UTF-8").replaceAll("\\+", "%20");
httpServletResponse.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
if (fileSource.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
fileSource = fileSource.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length());
}
try (InputStream inputStream = new ClassPathResource(fileSource).getInputStream(); OutputStream outputStream = httpServletResponse.getOutputStream()) {
Map<String, Object> map = MapUtils.newHashMap();
// 员工类型
map.put("employeeType", employeeType);
ExcelWriter excelWriter = EasyExcel.write()
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(new CustomRosterCellWriteHandler())
.withTemplate(inputStream)
.file(outputStream)
.inMemory(true).build();
WriteSheet writeSheetFill = EasyExcel.writerSheet(0).build();
excelWriter.fill(map, writeSheetFill);
for (int i = 0; i < ftbRosterImportTemplateBOS.size(); i++) {
FtbRosterImportTemplateBO ftbRosterImportTemplateBO = ftbRosterImportTemplateBOS.get(i);
// 这里放入动态头,跳过第一个sheet
WriteSheet writeSheet = EasyExcel.writerSheet(ftbRosterImportTemplateBO.getSheetName())
.head(ftbRosterImportTemplateBO.getHeader()).build();
excelWriter.write(ftbRosterImportTemplateBO.getData(), writeSheet);
}
excelWriter.finish();
}
}
/**
* 动态表头生成
*/
public void dynamicHeaderGeneration(HttpServletResponse httpServletResponse,
String fileNameOriginal, List<FtbRosterImportTemplateBO> ftbRosterImportTemplateBOS) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题请直接用浏览器或者用postman
httpServletResponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
httpServletResponse.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode(fileNameOriginal + System.currentTimeMillis(), "UTF-8").replaceAll("\\+", "%20");
httpServletResponse.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
try (OutputStream outputStream = httpServletResponse.getOutputStream()) {
ExcelWriter excelWriter = EasyExcel.write()
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(new CustomCellWriteHandler())
.file(outputStream)
.inMemory(true).build();
for (int i = 0; i < ftbRosterImportTemplateBOS.size(); i++) {
FtbRosterImportTemplateBO ftbRosterImportTemplateBO = ftbRosterImportTemplateBOS.get(i);
// 这里放入动态头,跳过第一个sheet
WriteSheet writeSheet = EasyExcel.writerSheet(ftbRosterImportTemplateBO.getSheetName())
.head(ftbRosterImportTemplateBO.getHeader()).build();
excelWriter.write(ftbRosterImportTemplateBO.getData(), writeSheet);
}
excelWriter.finish();
}
}
/**
* 动态表头生成
*/
public void dynamicHeaderGenerationRoster(HttpServletResponse httpServletResponse,
String fileNameOriginal, List<FtbRosterImportTemplateBO> ftbRosterImportTemplateBOS) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题请直接用浏览器或者用postman
httpServletResponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
httpServletResponse.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode(fileNameOriginal + System.currentTimeMillis(), "UTF-8").replaceAll("\\+", "%20");
httpServletResponse.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
try (OutputStream outputStream = httpServletResponse.getOutputStream()) {
ExcelWriter excelWriter = EasyExcel.write()
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(new CustomRosterCellWriteHandler())
.file(outputStream)
.inMemory(true).build();
for (int i = 0; i < ftbRosterImportTemplateBOS.size(); i++) {
FtbRosterImportTemplateBO ftbRosterImportTemplateBO = ftbRosterImportTemplateBOS.get(i);
// 这里放入动态头,跳过第一个sheet
WriteSheet writeSheet = EasyExcel.writerSheet(ftbRosterImportTemplateBO.getSheetName())
.head(ftbRosterImportTemplateBO.getHeader()).build();
excelWriter.write(ftbRosterImportTemplateBO.getData(), writeSheet);
}
excelWriter.finish();
}
}
/**
* 动态表头生成(匹配对应sheet 对应的数据)
*/
public void dynamicHeaderGeneration(HttpServletResponse httpServletResponse,
String fileNameOriginal, String fileSource,
List<FtbRosterImportTemplateBO> ftbRosterImportTemplateBOS,
List<ExcelSelectedResolve> pullList) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题请直接用浏览器或者用postman
httpServletResponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
httpServletResponse.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode(fileNameOriginal + System.currentTimeMillis(), "UTF-8").replaceAll("\\+", "%20");
httpServletResponse.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
if (fileSource.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
fileSource = fileSource.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length());
}
// 匹配对应的数据进行处理 数据下标构建
for (ExcelSelectedResolve excelSelectedResolve : pullList){
List<String> list = new ArrayList<>();
list.add(excelSelectedResolve.getHeaderName());
int j = ftbRosterImportTemplateBOS.stream()
.filter(vo -> excelSelectedResolve.getSheetName().equals(vo.getSheetName()))
.findFirst()
.orElse(new FtbRosterImportTemplateBO())
.getHeader()
.indexOf(list);
excelSelectedResolve.setRowNum(j);
}
try (InputStream inputStream = new ClassPathResource(fileSource).getInputStream(); OutputStream outputStream = httpServletResponse.getOutputStream()) {
ExcelWriter excelWriter = EasyExcel.write()
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(new CustomCellWriteHandler())
.withTemplate(inputStream)
.file(outputStream)
.inMemory(true).build();
for (int i = 0; i < ftbRosterImportTemplateBOS.size(); i++) {
FtbRosterImportTemplateBO ftbRosterImportTemplateBO = ftbRosterImportTemplateBOS.get(i);
// 这里放入动态头,跳过第一个sheet
WriteSheet writeSheet = EasyExcel.writerSheet(ftbRosterImportTemplateBO.getSheetName())
.registerWriteHandler(new SelectedSheetWriteHandler(pullList))
.head(ftbRosterImportTemplateBO.getHeader()).build();
excelWriter.write(ftbRosterImportTemplateBO.getData(), writeSheet);
}
excelWriter.finish();
}
}
/**
* 通用导入
*/
public <T, R> void universalImport(Class<T> tClass,
InputStream inputStream,
BaseEasyExcelCommonListener<T, R> baseEasyExcelCommonListener,
Integer sheetNo,
Integer... integers) throws IOException {
int integer = 1;
if (integers.length > 0) integer = integers[0];
baseEasyExcelCommonListener.before();
EasyExcel.read(inputStream, tClass, new BaseEasyExcelReadListener<>(baseEasyExcelCommonListener))
.headRowNumber(integer)
.sheet(sheetNo).doRead();
baseEasyExcelCommonListener.after();
}
@Slf4j
public class CustomRosterCellWriteHandler implements CellWriteHandler {
@Override
public void afterCellDispose(CellWriteHandlerContext context) {
Cell cell = context.getCell();
// 表头样式判断
if (cell.getRowIndex() == 0) {
Workbook workbook = cell.getSheet().getWorkbook();
Font font = workbook.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 11);
XSSFRichTextString richTextString = new XSSFRichTextString(cell.getStringCellValue());
if (cell.getStringCellValue().contains("*")) {
font.setColor(IndexedColors.RED.getIndex());
richTextString.applyFont(0, 1, font);
font.setColor(IndexedColors.BLACK.getIndex());
richTextString.applyFont(1, cell.getStringCellValue().length(), font);
} else {
font.setColor(IndexedColors.BLACK.getIndex());
richTextString.applyFont(font);
}
cell.setCellValue(richTextString);
}
}
}
@Slf4j
public class CustomCellWriteHandler implements CellWriteHandler {
@Override
public void afterCellDispose(CellWriteHandlerContext context) {
Cell cell = context.getCell();
// 表头样式判断
if (cell.getRowIndex() == 0) {
Workbook workbook = cell.getSheet().getWorkbook();
Font font = workbook.createFont();
font.setBold(true);
font.setFontHeightInPoints((short) 11);
String cellValue = cell.getStringCellValue();
if (cellValue != null && !cellValue.isEmpty()) {
if (cellValue.contains("*")) {
font.setColor(IndexedColors.RED.getIndex());
// 使用 POI 的原生方式设置样式,避免转换为 RichTextString
CellStyle cellStyle = cell.getCellStyle();
cellStyle.setFont(font);
} else {
font.setColor(IndexedColors.BLACK.getIndex());
CellStyle cellStyle = cell.getCellStyle();
cellStyle.setFont(font);
}
}
}
}
}
@Slf4j
@Data
public class SelectedSheetWriteHandler implements SheetWriteHandler{
private final List<ExcelSelectedResolve> resolveList;
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
// 这里可以对cell进行任何操作
Sheet sheet = writeSheetHolder.getSheet();
String sheetName = sheet.getSheetName();
DataValidationHelper helper = sheet.getDataValidationHelper();
for (ExcelSelectedResolve resolve : resolveList) {
if (!sheetName.equals(resolve.getSheetName()) || resolve.getRowNum() == -1){
continue;
}
// 设置下拉列表的行: 首行,末行,首列,末列
CellRangeAddressList rangeList = new CellRangeAddressList(resolve.getFirstRow(),
resolve.getLastRow(),
resolve.getRowNum(),
resolve.getRowNum());
// 设置下拉列表的值
DataValidationConstraint constraint = helper.createExplicitListConstraint(resolve.getSource());
// 设置约束
DataValidation validation = helper.createValidation(constraint, rangeList);
// 阻止输入非下拉选项的值
validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
validation.setShowErrorBox(true);
validation.setSuppressDropDownArrow(true);
validation.createErrorBox("提示", "请输入下拉选项中的内容");
sheet.addValidationData(validation);
}
}
}
}

View File

@@ -0,0 +1,33 @@
package jnpf.util.excel;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Data
public class ExcelSelectedResolve{
/**
* 需要匹配的sheet名称
*/
private String sheetName;
/**
* 需要匹配的header名称
*/
private String headerName;
/**
* 下拉内容 自己构建
*/
private String[] source;
/**
* 设置下拉框的起始行,默认为第二行
*/
private int firstRow = 1;
/**
* 设置下拉框的结束行,默认为最后一行
*/
private int lastRow = 100;
private int rowNum;
}

View File

@@ -0,0 +1,103 @@
package jnpf.util.excel;
import cn.hutool.core.util.StrUtil;
import lombok.experimental.UtilityClass;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
/**
* 短链工具类
*
* @author fantaibao
* @date 2024/02/02
*/
@UtilityClass
public class ShortChainUtils {
/**
* 短链地址生成
*
* @param url 网址
* @return {@link String}
*/
public String[] shortUrl(String url) {
// 可以自定义生成 MD5 加密字符传前的混合 KEY
String key = "wang6ge666";
// 要使用生成 URL 的字符
String[] chars = new String[]{"a", "b", "c", "d", "e", "f", "g", "h",
"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
"I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
"U", "V", "W", "X", "Y", "Z"
};
// 对传入网址进行 MD5 加密
String hex = md5ByHex(key + url);
String[] resUrl = new String[4];
for (int i = 0; i < 4; i++) {
// 把加密字符按照 8 位一组 16 进制与 0x3FFFFFFF 进行位与运算
String sTempSubString = hex.substring(i * 8, i * 8 + 8);
// 这里需要使用 long 型来转换,因为 Inteper .parseInt() 只能处理 31 位 , 首位为符号位 , 如果不用long ,则会越界
long lHexLong = 0x3FFFFFFF & Long.parseLong(sTempSubString, 16);
String outChars = "";
for (int j = 0; j < 6; j++) {
// 把得到的值与 0x0000003D 进行位与运算,取得字符数组 chars 索引
long index = 0x0000003D & lHexLong;
// 把取得的字符相加
outChars += chars[(int) index];
// 每次循环按位右移 5 位
lHexLong = lHexLong >> 5;
}
// 把字符串存入对应索引的输出数组
resUrl[i] = outChars;
}
return resUrl;
}
public String md5ByHex(String src) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] b = src.getBytes();
md.reset();
md.update(b);
byte[] hash = md.digest();
String hs = "";
String stmp = "";
for (int i = 0; i < hash.length; i++) {
stmp = Integer.toHexString(hash[i] & 0xFF);
if (stmp.length() == 1)
hs = hs + "0" + stmp;
else {
hs = hs + stmp;
}
}
return hs.toUpperCase();
} catch (Exception e) {
return "";
}
}
/**
* 长链decode
*
* @param encodeUrl 编码
* @return {@link String}
*/
public String reverseLongChain(String encodeUrl) {
if (StrUtil.isNotBlank(encodeUrl)) {
String decode = URLDecoder.decode(encodeUrl, StandardCharsets.UTF_8);
byte[] decoded = Base64.getDecoder().decode(decode);
return new String(decoded);
}
return encodeUrl;
}
}

View File

@@ -0,0 +1,27 @@
package jnpf.util.im;
/**
* todo
*
* @author Flynn Chan
* @create 2024-04-10
*/
public class ImConst {
public static final String SYSTEM_APP_NAME = "人事管理";
public static final String HR_APP_NAME = "人事管理";
public static final String SYSTEM_LOGO = "https://jnpf-resource-1304460613.cos.ap-chengdu.myqcloud.com/jnpf-resources/UserAvatar/665ecb69e4b0ae5df114c87a.png";
public static final String HR_LOGO = "https://jnpf-resource-1304460613.cos.ap-chengdu.myqcloud.com/jnpf-resources/UserAvatar/665ecb69e4b0ae5df114c87a.png";
/**
* 转正
**/
public static String TITLE_BECOME_REGULAR_EMPLOYEE = "转正结果";
public static String SUCCESSFULLY_BECAME_A_REGULAR_EMPLOYEE = "%s, 恭喜您通过试用期考核,成为正式员工!希望今后继续努力,取得更好成绩。";
public static String FAILED_TO_BECOME_A_REGULAR_EMPLOYEE = "%s, 很抱歉通知您,因没有达到岗位期望要求,您的试用期考核结果为不合格,感谢您在公司的这段时间以来的努力和表现!";
/**
* 入职登记邀请
**/
public static String TITLE_INVITE_TO_JOIN = "邀请填写入职登记表";
public static String INVITE_TO_JOIN = "您的入职登记表还未提交,现在就去填写吧!";
public static String BUTTON_INVITE_TO_JOIN = "点击填写入职信息";
}

View File

@@ -0,0 +1,108 @@
package jnpf.util.im;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;
import jnpf.ImRobotApi;
import jnpf.from.*;
import jnpf.model.im.UserAndLinkDTO;
import jnpf.model.im.UserSuccessDTO;
import jnpf.permission.UserApi;
import jnpf.permission.entity.UserEntity;
import jnpf.util.UserProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Component
public class ImMessageNoticeUtils {
@Autowired
private UserApi userApi;
@Resource
private ImRobotApi imRobotApi;
@Resource
private UserProvider userProvider;
public void becomeRegularEmployee(List<UserSuccessDTO> successDTOS) {
successDTOS.forEach(
dto -> {
if (CollUtil.isNotEmpty(dto.getUserIds())) {
List<UserEntity> userEntityList = userApi.getUserName(dto.getUserIds());
userEntityList.forEach(userEntity -> {
SingleSendRobotNoticeForm singleSendRobotNoticeForm = new SingleSendRobotNoticeForm();
singleSendRobotNoticeForm.setRobotTypeEnum(ImRobotTypeEnum.FTB_XZS);
singleSendRobotNoticeForm.setMessageAttributionRobotMpId("__UNI__1EF82DA");
singleSendRobotNoticeForm.setTenantId(userProvider.get().getTenantId());
singleSendRobotNoticeForm.setToUserIds(List.of(userEntity.getId()));
SendRobotNoticeDataForm robotNoticeDataForm = new SendRobotNoticeDataForm();
robotNoticeDataForm.setAppName(ImConst.SYSTEM_APP_NAME);
robotNoticeDataForm.setLogo(ImConst.SYSTEM_LOGO);
robotNoticeDataForm.setTitle(ImConst.TITLE_BECOME_REGULAR_EMPLOYEE);
String content = String.format(dto.getSuccess() ? ImConst.SUCCESSFULLY_BECAME_A_REGULAR_EMPLOYEE : ImConst.FAILED_TO_BECOME_A_REGULAR_EMPLOYEE, userEntity.getRealName());
robotNoticeDataForm.setContent(content);
singleSendRobotNoticeForm.setRobotNoticeDataForm(robotNoticeDataForm);
imRobotApi.sendSingleRobotNotice(singleSendRobotNoticeForm);
});
}
}
);
}
public void inviteToJoin(List<UserAndLinkDTO> dtoList) {
if (CollUtil.isNotEmpty(dtoList)) {
List<UserEntity> userEntityList = userApi.getUserName(dtoList.stream().map(UserAndLinkDTO::getUserId).distinct().collect(Collectors.toList()));
dtoList.forEach(dto -> {
UserEntity thisUser = userEntityList.stream().filter(user -> user.getId().equals(dto.getUserId())).findFirst().orElse(null);
if (thisUser != null) {
SingleSendRobotNoticeForm singleSendRobotNoticeForm = new SingleSendRobotNoticeForm();
singleSendRobotNoticeForm.setRobotTypeEnum(ImRobotTypeEnum.FTB_XZS);
singleSendRobotNoticeForm.setTenantId(userProvider.get().getTenantId());
singleSendRobotNoticeForm.setToUserIds(List.of(thisUser.getId()));
SendRobotNoticeDataForm robotNoticeDataForm = new SendRobotNoticeDataForm();
robotNoticeDataForm.setAppName(ImConst.HR_APP_NAME);
robotNoticeDataForm.setLogo(ImConst.HR_LOGO);
robotNoticeDataForm.setTitle(ImConst.TITLE_INVITE_TO_JOIN);
String content = String.format(ImConst.INVITE_TO_JOIN);
robotNoticeDataForm.setContent(content);
if (StrUtil.isNotBlank(dto.getLinkUrl())) {
JumpUrlListModel jumpUrlListModel = new JumpUrlListModel();
jumpUrlListModel.setDisplayMethodEnum(JumpUrlListModel.DisplayMethodEnum.TRUE);
jumpUrlListModel.setButtonName(ImConst.BUTTON_INVITE_TO_JOIN);
jumpUrlListModel.setReqMethod(JumpUrlListModel.ReqMethodEnum.GET);
jumpUrlListModel.setType(1);
jumpUrlListModel.setUrl(dto.getLinkUrl());
MiniAppUrl miniAppUrl = new MiniAppUrl();
miniAppUrl.setMpId("__UNI__1EF82DA");
String[] split = dto.getLinkUrl().split("url=");
miniAppUrl.setMpPage(split[0]);
JSONObject entries = new JSONObject();
entries.set("url",split[1]);
miniAppUrl.setMpParam(JSON.toJSONString(entries));
jumpUrlListModel.setMiniAppUrl(miniAppUrl);
log.error("入职请链接生成: " + dto.getLinkUrl());
LinkedList<JumpUrlListModel> jumpUrlListModels = new LinkedList<>();
jumpUrlListModels.add(jumpUrlListModel);
robotNoticeDataForm.setJumpUrlList(jumpUrlListModels);
}
singleSendRobotNoticeForm.setRobotNoticeDataForm(robotNoticeDataForm);
imRobotApi.sendSingleRobotNotice(singleSendRobotNoticeForm);
}
});
}
}
}

View File

@@ -0,0 +1,109 @@
package jnpf.util.mapper;
import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class MybatisUtil {
/**
* 根据字段查询
* @param mapper 持久层DAO
* @param c 字段
* @param value 值
* @param <T>
* @return
*/
public static <T> T findByFiled(BaseMapper<T> mapper, SFunction<T, ?> c, Object value, Boolean isDelete) {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(c, value);
if (isDelete) {
queryWrapper.eq("F_DeleteMark", 0);
}
return mapper.selectOne(queryWrapper);
}
/**
* 根据字段查询列表
* @param mapper 持久层DAO
* @param c 字段
* @param value 值
* @param <T>
* @return
*/
public static <T> List<T> findListByFiled(BaseMapper<T> mapper, SFunction<T, ?> c, Object value, Boolean hasDelete) {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.eq(c, value);
if (hasDelete) {
queryWrapper.eq("F_DeleteMark", 0);
}
return mapper.selectList(queryWrapper);
}
/**
* 根据字段查询
* @param mapper 持久层DAO
* @param c 字段
* @param value 值
* @param <T>
* @return
*/
public static <T, V> Integer findCountByField(BaseMapper<T> mapper, SFunction<T, ?> c, V... value) {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
LambdaQueryWrapper<T> lambdaQueryWrapper = queryWrapper.lambda();
if (ArrayUtil.isNotEmpty(value)) {
for (V v : value) {
lambdaQueryWrapper.eq(c, value);
}
}
return Integer.parseInt(String.valueOf(mapper.selectCount(queryWrapper)));
}
/**
* 随机查询
*
* @param mapper 持久层DAO
* @param limit 随机条数
* @return java.util.List<T>
* @since 2021/8/10 15:30
*/
public static <T> List<T> getAny(BaseMapper<T> mapper, T condition, Integer limit) {
LambdaQueryWrapper<T> wrapper = Wrappers.lambdaQuery(condition);
Integer total = Integer.parseInt(String.valueOf(mapper.selectCount(wrapper)));
if (limit == null || limit <= 0 || total == 0) {
return Collections.emptyList();
}
List<T> list = Optional.of(limit).filter(l -> l > total).map(l -> mapper.selectList(wrapper)).orElseGet(() -> mapper.selectList(wrapper.last("LIMIT " + new SecureRandom().nextInt(total - (limit - 1)) + "," + limit)));
Collections.shuffle(list);
return list;
}
/**
* 随机查询
*
* @param mapper 持久层DAO
* @param limit 随机条数
* @return java.util.List<T>
* @since 2021/8/10 15:30
*/
public static <T> List<T> getAny(BaseMapper<T> mapper, T condition, Integer limit, QueryWrapper<T> wrapper) {
// LambdaQueryWrapper<T> wrapper = Wrappers.lambdaQuery(condition);
Integer total = Integer.parseInt(String.valueOf(mapper.selectCount(wrapper)));
if (limit == null || limit <= 0 || total == 0) {
return Collections.emptyList();
}
List<T> list = Optional.of(limit).filter(l -> l > total).map(l -> mapper.selectList(wrapper)).orElseGet(() -> mapper.selectList(wrapper.last("LIMIT " + new SecureRandom().nextInt(total - (limit - 1)) + "," + limit)));
Collections.shuffle(list);
return list;
}
}

View File

@@ -0,0 +1,263 @@
package jnpf.util.permssion;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import jnpf.model.common.CheckVo;
import jnpf.permission.model.util.IdDiffResult;
import jnpf.permission.model.util.IdPair;
import jnpf.permission.model.util.IdPairDiffResult;
import jnpf.permission.vo.TreeNodeVo;
import jnpf.util.JsonUtil;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* todo
*
* @author Flynn Chan
* @create 2025-10-02
*/
public class V2Utils {
public static <T extends TreeNodeVo<T>> List<T> changeToTreeNoParent(List<T> list, Class<T> c) {
CheckVo checkVo = new CheckVo(false);
List<T> listCopy = JsonUtil.getJsonToList(list, c);
for (T t : list) {
T tt = listCopy.stream().filter(v -> v.getId().equals(t.getId())).findFirst().orElse(null);
if (tt == null) {
continue;
}
findParent(checkVo, listCopy, tt);
if (checkVo.getFlag()) {
listCopy.removeIf(v -> v.getId().equals(t.getId()));
checkVo.setFlag(false);
}
}
return listCopy;
}
public static <T extends TreeNodeVo<T>> List<T> changeToTreeNoParentFast(List<T> list, Class<T> c) {
// 拷贝副本
List<T> listCopy = BeanUtil.copyToList(list, c);
// 构建 Map 方便快速访问
Map<String, T> nodeMap = listCopy.stream().collect(Collectors.toMap(T::getId, Function.identity()));
// 存放根节点
List<T> rootList = new ArrayList<>();
for (T node : listCopy) {
String pid = node.getPid();
if (StrUtil.isNotBlank(pid) && nodeMap.containsKey(pid)) {
T parent = nodeMap.get(pid);
parent.getChildren().add(node);
} else {
rootList.add(node);
}
}
return rootList;
}
private static <T extends TreeNodeVo<T>> void findParent(CheckVo checkVo, List<T> list, T t) {
if (!list.isEmpty()) {
for (T value : list) {
if (value.getId().equals(t.getPid())) {
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 new JSONObject(jsonToBean).toString();
}
/**
* 把逗号分隔的字符串转换为数组
*
* @param input
* @return
*/
public static List<String> convertToList(String input) {
if (input == null || input.trim().isEmpty()) {
return Collections.emptyList();
}
// 判断格式是否符合:多个用逗号隔开的非空串
if (!input.matches("^(\\S+)(,\\S+)*$")) {
return Collections.emptyList();
}
// 拆分并转为 List
return new ArrayList<>(Arrays.asList(input.split(",")));
}
/**
* 始终取list最后一条数据并且剔除,没有则空
*/
public static String pollLast(List<String> list) {
if (list == null || list.isEmpty()) {
return null;
}
// 移除并返回最后一条
return list.remove(list.size() - 1);
}
/**
* 使用 Set 比较两个 ID 列表,适用于大数据量。
*
* @param oldIds 原始 ID 列表
* @param targetIds 目标 ID 列表
* @return 包含 removedIds 和 addedIds 的结果对象
*/
public static IdDiffResult compareIdLists(List<String> oldIds, List<String> targetIds) {
oldIds = oldIds == null ? new ArrayList<>() : oldIds;
targetIds = targetIds == null ? new ArrayList<>() : targetIds;
Set<String> oldSet = new HashSet<>(oldIds);
Set<String> targetSet = new HashSet<>(targetIds);
Set<String> removedSet = new HashSet<>(oldSet);
removedSet.removeAll(targetSet);
Set<String> addedSet = new HashSet<>(targetSet);
addedSet.removeAll(oldSet);
return new IdDiffResult(new ArrayList<>(removedSet), new ArrayList<>(addedSet));
}
/**
* 获取两个列表对象(两个String属性)的差异
*
* @param oldList 旧列表
* @param targetList 新列表
*/
public static IdPairDiffResult compareIdPairs(List<IdPair> oldList, List<IdPair> targetList) {
oldList = oldList == null ? new ArrayList<>() : oldList;
targetList = targetList == null ? new ArrayList<>() : targetList;
// 用 id1-id2 作为唯一 key 做对比
Map<String, IdPair> oldMap = oldList.stream()
.collect(Collectors.toMap(V2Utils::buildKey, p -> p));
Map<String, IdPair> targetMap = targetList.stream()
.collect(Collectors.toMap(V2Utils::buildKey, p -> p));
Set<String> oldKeys = oldMap.keySet();
Set<String> targetKeys = targetMap.keySet();
// removed: old 有而 target 没有的 key
List<IdPair> removed = oldKeys.stream()
.filter(k -> !targetKeys.contains(k))
.map(oldMap::get)
.collect(Collectors.toList());
// added: target 有而 old 没有的 key
List<IdPair> added = targetKeys.stream()
.filter(k -> !oldKeys.contains(k))
.map(targetMap::get)
.collect(Collectors.toList());
return new IdPairDiffResult(removed, added);
}
private static String buildKey(IdPair pair) {
return (pair.getId1() == null ? "" : pair.getId1()) + "-" + (pair.getId2() == null ? "" : pair.getId2());
}
// 提取字符串中的第一个连续数字
public static Integer extractNumber(String str) {
if (str == null) return null;
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("\\d+");
java.util.regex.Matcher matcher = pattern.matcher(str);
if (matcher.find()) {
return Integer.parseInt(matcher.group());
}
return null;
}
/**
* 根据倒序索引获取单个组织ID
*
* @param organizeIdTree 逗号分隔的组织ID树
* @param count 倒序第几个1表示最后一个2表示倒数第二个
* @return 对应的ID如果越界返回 null
*/
public static String getIdByReverseOrder(String organizeIdTree, int count) {
if (organizeIdTree == null || organizeIdTree.isEmpty() || count <= 0) {
return null;
}
String[] ids = organizeIdTree.split(",");
int index = ids.length - count;
if (index < 0) {
return null; // 越界时返回 null
}
return ids[index];
}
/**
* 获取最后 count 个组织ID
*
* @param organizeIdTree 逗号分隔的组织ID树
* @param count 要返回的个数(从最后往前数)
* @return List<String>,如果 count 超过长度则返回全部
*/
public static List<String> getLastIds(String organizeIdTree, int count) {
if (organizeIdTree == null || organizeIdTree.isEmpty() || count <= 0) {
return Collections.emptyList();
}
String[] ids = organizeIdTree.split(",");
int length = ids.length;
int start = Math.max(length - count, 0);
return new ArrayList<>(Arrays.asList(ids).subList(start, length));
}
}