mirror of https://github.com/jeecgboot/jeecg-boot
【v3.8.3】底层core的一些功能修改
parent
152e8c7aaa
commit
a4343fc2cb
|
@ -1,5 +1,6 @@
|
|||
package org.jeecg.common.api;
|
||||
|
||||
import org.jeecg.common.api.dto.AiragFlowDTO;
|
||||
import org.jeecg.common.system.vo.*;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -144,4 +145,15 @@ public interface CommonAPI {
|
|||
List<DictModel> translateDictFromTableByKeys(String table, String text, String code, String keys, String dataSource);
|
||||
//update-end---author:chenrui ---date:20231221 for:[issues/#5643]解决分布式下表字典跨库无法查询问题------------
|
||||
|
||||
/**
|
||||
* 16 运行AIRag流程
|
||||
* for [QQYUN-13634]在baseapi里面封装方法,方便其他模块调用
|
||||
*
|
||||
* @param airagFlowDTO
|
||||
* @return 流程执行结果,可能是String或者Map
|
||||
* @author chenrui
|
||||
* @date 2025/9/2 11:43
|
||||
*/
|
||||
Object runAiragFlow(AiragFlowDTO airagFlowDTO);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package org.jeecg.common.api.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 调用AI流程入参
|
||||
* for [QQYUN-13634]在baseapi里面封装方法,方便其他模块调用
|
||||
* @author chenrui
|
||||
* @date 2025/9/2 14:11
|
||||
*/
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class AiragFlowDTO implements Serializable {
|
||||
|
||||
|
||||
private static final long serialVersionUID = 7431775881170684867L;
|
||||
|
||||
/**
|
||||
* 流程id
|
||||
*/
|
||||
private String flowId;
|
||||
|
||||
|
||||
/**
|
||||
* 输入参数
|
||||
*/
|
||||
private Map<String, Object> inputParams;
|
||||
}
|
|
@ -642,11 +642,12 @@ public interface CommonConstant {
|
|||
|
||||
|
||||
/**
|
||||
* 自定义首页关联关系(ROLE:表示角色 USER:表示用户)
|
||||
* 自定义首页关联关系(ROLE:表示角色 USER:表示用户 DEFAULT:默认首页)
|
||||
*
|
||||
*/
|
||||
String HOME_RELATION_ROLE = "ROLE";
|
||||
String HOME_RELATION_USER = "USER";
|
||||
String HOME_RELATION_DEFAULT = "DEFAULT";
|
||||
|
||||
/**
|
||||
* 是否置顶(0否 1是)
|
||||
|
@ -659,4 +660,53 @@ public interface CommonConstant {
|
|||
String FLOW_FOCUS_NOTICE_PREFIX = "flow:runtimeData:focus:notice:";
|
||||
//任务缓办时间缓存前缀
|
||||
String FLOW_TASK_DELAY_PREFIX = "flow:runtimeData:task:delay:";
|
||||
/**
|
||||
* 用户代理类型:离职:quit 代理:agent
|
||||
*/
|
||||
String USER_AGENT_TYPE_QUIT = "quit";
|
||||
String USER_AGENT_TYPE_AGENT = "agent";
|
||||
/**
|
||||
* 督办流程首节点任务taskKey
|
||||
*/
|
||||
String SUPERVISE_FIRST_TASK_KEY = "Task_1bhxpt0";
|
||||
|
||||
/**
|
||||
* wps模板预览数据缓存前缀
|
||||
*/
|
||||
String EOA_WPS_TEMPLATE_VIEW_DATA ="eoa:wps:templateViewData:";
|
||||
|
||||
/**
|
||||
* wps模板预览版本号缓存前缀
|
||||
*/
|
||||
String EOA_WPS_TEMPLATE_VIEW_VERSION ="eoa:wps:templateViewVersion:";
|
||||
/**
|
||||
* 表单设计器oa新增字段
|
||||
* x_oa_timeout_date:逾期时间
|
||||
* x_oa_archive_status:归档状态
|
||||
*/
|
||||
String X_OA_TIMEOUT_DATE ="x_oa_timeout_date";
|
||||
String X_OA_ARCHIVE_STATUS ="x_oa_archive_status";
|
||||
/**
|
||||
* 流程状态
|
||||
* 待提交: 1
|
||||
* 处理中: 2
|
||||
* 已完成: 3
|
||||
* 已作废: 4
|
||||
* 已挂起: 5
|
||||
*/
|
||||
String BPM_STATUS_1 ="1";
|
||||
String BPM_STATUS_2 ="2";
|
||||
String BPM_STATUS_3 ="3";
|
||||
String BPM_STATUS_4 ="4";
|
||||
String BPM_STATUS_5 ="5";
|
||||
|
||||
/**
|
||||
* 默认租户产品包
|
||||
*/
|
||||
String TENANT_PACK_DEFAULT = "default";
|
||||
|
||||
/**
|
||||
* 部门名称redisKey(全路径)
|
||||
*/
|
||||
String DEPART_NAME_REDIS_KEY_PRE = "sys:cache:departPathName:";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package org.jeecg.common.constant;
|
||||
|
||||
/**
|
||||
* @Description: 密码常量类
|
||||
*
|
||||
* @author: wangshuai
|
||||
* @date: 2025/8/27 20:10
|
||||
*/
|
||||
public interface PasswordConstant {
|
||||
|
||||
/**
|
||||
* 导入用户默认密码
|
||||
*/
|
||||
String DEFAULT_PASSWORD = "123456";
|
||||
}
|
|
@ -121,7 +121,7 @@ public class ProvinceCityArea {
|
|||
|
||||
public void getAreaByCode(String code,List<String> ls){
|
||||
for(Area area: areaList){
|
||||
if(area.getId().equals(code)){
|
||||
if(null != area && area.getId().equals(code)){
|
||||
String pid = area.getPid();
|
||||
ls.add(0,area.getText());
|
||||
getAreaByCode(pid,ls);
|
||||
|
|
|
@ -8,21 +8,30 @@ import java.util.List;
|
|||
|
||||
/**
|
||||
* 消息类型
|
||||
*
|
||||
* @author: jeecg-boot
|
||||
*/
|
||||
@EnumDict("messageType")
|
||||
public enum MessageTypeEnum {
|
||||
|
||||
/** 系统消息 */
|
||||
XT("system", "系统消息"),
|
||||
/** 邮件消息 */
|
||||
YJ("email", "邮件消息"),
|
||||
/** 钉钉消息 */
|
||||
/**
|
||||
* 系统消息
|
||||
*/
|
||||
XT("system", "系统消息"),
|
||||
/**
|
||||
* 邮件消息
|
||||
*/
|
||||
YJ("email", "邮件消息"),
|
||||
/**
|
||||
* 钉钉消息
|
||||
*/
|
||||
DD("dingtalk", "钉钉消息"),
|
||||
/** 企业微信 */
|
||||
/**
|
||||
* 企业微信
|
||||
*/
|
||||
QYWX("wechat_enterprise", "企业微信");
|
||||
|
||||
MessageTypeEnum(String type, String note){
|
||||
MessageTypeEnum(String type, String note) {
|
||||
this.type = type;
|
||||
this.note = note;
|
||||
}
|
||||
|
@ -56,12 +65,13 @@ public enum MessageTypeEnum {
|
|||
|
||||
/**
|
||||
* 获取字典数据
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static List<DictModel> getDictList(){
|
||||
public static List<DictModel> getDictList() {
|
||||
List<DictModel> list = new ArrayList<>();
|
||||
DictModel dictModel = null;
|
||||
for(MessageTypeEnum e: MessageTypeEnum.values()){
|
||||
for (MessageTypeEnum e : MessageTypeEnum.values()) {
|
||||
dictModel = new DictModel();
|
||||
dictModel.setValue(e.getType());
|
||||
dictModel.setText(e.getNote());
|
||||
|
|
|
@ -14,7 +14,16 @@ public enum NoticeTypeEnum {
|
|||
NOTICE_TYPE_PLAN("日程消息","plan"),
|
||||
//暂时没用到
|
||||
NOTICE_TYPE_MEETING("会议消息","meeting"),
|
||||
NOTICE_TYPE_SYSTEM("系统消息","system");
|
||||
NOTICE_TYPE_SYSTEM("系统消息","system"),
|
||||
/**
|
||||
* 协同工作
|
||||
* for [JHHB-136]【vue3】协同工作系统消息需要添加一个类型
|
||||
*/
|
||||
NOTICE_TYPE_COLLABORATION("协同工作", "collab"),
|
||||
/**
|
||||
* 督办
|
||||
*/
|
||||
NOTICE_TYPE_SUPERVISE("督办管理", "supe");
|
||||
|
||||
/**
|
||||
* 文件类型名称
|
||||
|
|
|
@ -23,7 +23,25 @@ public enum SysAnnmentTypeEnum {
|
|||
/**
|
||||
* 邀请用户跳转到个人设置
|
||||
*/
|
||||
TENANT_INVITE("tenant_invite", "url", "/system/usersetting");
|
||||
TENANT_INVITE("tenant_invite", "url", "/system/usersetting"),
|
||||
/**
|
||||
* 协同工作-待办通知
|
||||
* for [JHHB-136]【vue3】协同工作系统消息需要添加一个类型
|
||||
*/
|
||||
EOA_CO_NOTIFY("eoa_co_notify", "url", "/collaboration/pending"),
|
||||
/**
|
||||
* 协同工作-催办通知
|
||||
* for [JHHB-136]【vue3】协同工作系统消息需要添加一个类型
|
||||
*/
|
||||
EOA_CO_REMIND("eoa_co_remind", "url", "/collaboration/pending"),
|
||||
/**
|
||||
* 督办管理-催办
|
||||
*/
|
||||
EOA_SUP_REMIND("eoa_sup_remind", "url", "/superivse/list"),
|
||||
/**
|
||||
* 督办管理-通知
|
||||
*/
|
||||
EOA_SUP_NOTIFY("eoa_sup_notify", "url", "/superivse/list");
|
||||
|
||||
/**
|
||||
* 业务类型(email:邮件 bpm:流程)
|
||||
|
|
|
@ -414,9 +414,11 @@ public class QueryGenerator {
|
|||
}
|
||||
// update-begin-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
|
||||
List<QueryCondition> filterConditions = conditions.stream().filter(
|
||||
rule -> oConvertUtils.isNotEmpty(rule.getField())
|
||||
&& oConvertUtils.isNotEmpty(rule.getRule())
|
||||
&& oConvertUtils.isNotEmpty(rule.getVal())
|
||||
rule -> (oConvertUtils.isNotEmpty(rule.getField())
|
||||
&& oConvertUtils.isNotEmpty(rule.getRule())
|
||||
&& oConvertUtils.isNotEmpty(rule.getVal())
|
||||
)
|
||||
|| "empty".equals(rule.getRule())
|
||||
).collect(Collectors.toList());
|
||||
if (filterConditions.size() == 0) {
|
||||
return;
|
||||
|
@ -427,9 +429,12 @@ public class QueryGenerator {
|
|||
queryWrapper.and(andWrapper -> {
|
||||
for (int i = 0; i < filterConditions.size(); i++) {
|
||||
QueryCondition rule = filterConditions.get(i);
|
||||
if (oConvertUtils.isNotEmpty(rule.getField())
|
||||
&& oConvertUtils.isNotEmpty(rule.getRule())
|
||||
&& oConvertUtils.isNotEmpty(rule.getVal())) {
|
||||
if (
|
||||
(
|
||||
oConvertUtils.isNotEmpty(rule.getField()) && oConvertUtils.isNotEmpty(rule.getRule()) && oConvertUtils.isNotEmpty(rule.getVal())
|
||||
)
|
||||
|| "empty".equals(rule.getRule())
|
||||
) {
|
||||
|
||||
log.debug("SuperQuery ==> " + rule.toString());
|
||||
|
||||
|
@ -716,7 +721,11 @@ public class QueryGenerator {
|
|||
* @param value 查询条件值
|
||||
*/
|
||||
public static void addEasyQuery(QueryWrapper<?> queryWrapper, String name, QueryRuleEnum rule, Object value) {
|
||||
if (name==null || value == null || rule == null || oConvertUtils.isEmpty(value)) {
|
||||
if (
|
||||
(
|
||||
name==null || value == null || rule == null || oConvertUtils.isEmpty(value)
|
||||
)
|
||||
&& !QueryRuleEnum.EMPTY.equals(rule)) {
|
||||
return;
|
||||
}
|
||||
name = oConvertUtils.camelToUnderline(name);
|
||||
|
@ -728,6 +737,9 @@ public class QueryGenerator {
|
|||
case GE:
|
||||
queryWrapper.ge(name, value);
|
||||
break;
|
||||
case EMPTY:
|
||||
queryWrapper.isNull(name);
|
||||
break;
|
||||
case LT:
|
||||
queryWrapper.lt(name, value);
|
||||
break;
|
||||
|
|
|
@ -49,24 +49,24 @@ public class JwtUtil {
|
|||
* @param code
|
||||
* @param errorMsg
|
||||
*/
|
||||
public static void responseError(ServletResponse response, Integer code, String errorMsg) {
|
||||
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
// issues/I4YH95浏览器显示乱码问题
|
||||
httpServletResponse.setHeader("Content-type", "text/html;charset=UTF-8");
|
||||
Result jsonResult = new Result(code, errorMsg);
|
||||
jsonResult.setSuccess(false);
|
||||
OutputStream os = null;
|
||||
try {
|
||||
os = httpServletResponse.getOutputStream();
|
||||
httpServletResponse.setCharacterEncoding("UTF-8");
|
||||
httpServletResponse.setStatus(code);
|
||||
os.write(new ObjectMapper().writeValueAsString(jsonResult).getBytes("UTF-8"));
|
||||
os.flush();
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
public static void responseError(HttpServletResponse response, Integer code, String errorMsg) {
|
||||
try {
|
||||
Result jsonResult = new Result(code, errorMsg);
|
||||
jsonResult.setSuccess(false);
|
||||
|
||||
// 设置响应头和内容类型
|
||||
response.setStatus(code);
|
||||
response.setHeader("Content-type", "text/html;charset=UTF-8");
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
// 使用 ObjectMapper 序列化为 JSON 字符串
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String json = objectMapper.writeValueAsString(jsonResult);
|
||||
response.getWriter().write(json);
|
||||
response.getWriter().flush();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验token是否正确
|
||||
|
@ -99,7 +99,7 @@ public class JwtUtil {
|
|||
DecodedJWT jwt = JWT.decode(token);
|
||||
return jwt.getClaim("username").asString();
|
||||
} catch (JWTDecodeException e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
log.error(e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import java.time.LocalDate;
|
|||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
@ -814,4 +816,44 @@ public class DateUtils extends PropertyEditorSupport {
|
|||
return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取两个日期之间的所有日期列表,包含开始和结束日期
|
||||
*
|
||||
* @param begin
|
||||
* @param end
|
||||
* @return
|
||||
*/
|
||||
public static List<Date> getDateRangeList(Date begin, Date end) {
|
||||
List<Date> dateList = new ArrayList<>();
|
||||
if (begin == null || end == null) {
|
||||
return dateList;
|
||||
}
|
||||
|
||||
// 清除时间部分,只比较日期
|
||||
Calendar beginCal = Calendar.getInstance();
|
||||
beginCal.setTime(begin);
|
||||
beginCal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
beginCal.set(Calendar.MINUTE, 0);
|
||||
beginCal.set(Calendar.SECOND, 0);
|
||||
beginCal.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
Calendar endCal = Calendar.getInstance();
|
||||
endCal.setTime(end);
|
||||
endCal.set(Calendar.HOUR_OF_DAY, 0);
|
||||
endCal.set(Calendar.MINUTE, 0);
|
||||
endCal.set(Calendar.SECOND, 0);
|
||||
endCal.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
if (endCal.before(beginCal)) {
|
||||
return dateList;
|
||||
}
|
||||
|
||||
dateList.add(beginCal.getTime());
|
||||
while (beginCal.before(endCal)) {
|
||||
beginCal.add(Calendar.DAY_OF_YEAR, 1);
|
||||
dateList.add(beginCal.getTime());
|
||||
}
|
||||
return dateList;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,14 +1,22 @@
|
|||
package org.jeecg.common.util;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
@ -203,4 +211,150 @@ public class FileDownloadUtils {
|
|||
dir.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 下载单个文件到ZIP流
|
||||
* 核心功能:获取文件流,写入ZIP条目
|
||||
* @param fileUrl 文件URL(可以是HTTP URL或本地路径)
|
||||
* @param fileName ZIP内的文件名
|
||||
* @param zous ZIP输出流
|
||||
*/
|
||||
public static void downLoadSingleFile(String fileUrl, String fileName, String uploadUrl,ZipArchiveOutputStream zous) {
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
// 创建ZIP条目:每个文件在ZIP中都是一个独立条目
|
||||
ZipArchiveEntry entry = new ZipArchiveEntry(fileName);
|
||||
zous.putArchiveEntry(entry);
|
||||
|
||||
// 获取文件输入流:区分普通文件和快捷方式
|
||||
if (fileUrl.endsWith(".url")) {
|
||||
// 处理快捷方式:生成.url文件内容
|
||||
inputStream = FileDownloadUtils.createInternetShortcut(fileName, fileUrl, "");
|
||||
} else {
|
||||
// 普通文件下载:从URL或本地路径获取流
|
||||
inputStream = getDownInputStream(fileUrl,uploadUrl);
|
||||
}
|
||||
|
||||
if (inputStream != null) {
|
||||
// 将文件流写入ZIP
|
||||
IOUtils.copy(inputStream, zous);
|
||||
}
|
||||
// 关闭当前ZIP条目
|
||||
zous.closeArchiveEntry();
|
||||
} catch (IOException e) {
|
||||
log.error("文件下载失败: {}", e);
|
||||
} finally {
|
||||
// 确保输入流关闭
|
||||
IoUtil.close(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载文件输入流
|
||||
* 功能:根据URL类型(HTTP或本地)获取文件流
|
||||
* @param fileUrl 文件URL(支持HTTP和本地路径)
|
||||
* @return 文件输入流,失败返回null
|
||||
*/
|
||||
public static InputStream getDownInputStream(String fileUrl, String uploadUrl) {
|
||||
try {
|
||||
// 处理HTTP URL:通过网络下载
|
||||
if (oConvertUtils.isNotEmpty(fileUrl) && fileUrl.startsWith(CommonConstant.STR_HTTP)) {
|
||||
URL url = new URL(fileUrl);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setConnectTimeout(5000); // 连接超时5秒
|
||||
connection.setReadTimeout(30000); // 读取超时30秒
|
||||
return connection.getInputStream();
|
||||
} else {
|
||||
// 处理本地文件:直接读取文件系统
|
||||
String downloadFilePath = uploadUrl + File.separator + fileUrl;
|
||||
// 安全检查:防止下载危险文件类型
|
||||
SsrfFileTypeFilter.checkDownloadFileType(downloadFilePath);
|
||||
return new BufferedInputStream(new FileInputStream(downloadFilePath));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// 异常时返回null,上层会处理空流情况
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
* 功能:从文件名中提取扩展名
|
||||
* @param fileName 文件名
|
||||
* @return 文件扩展名(不含点),如"txt"、"png"
|
||||
*/
|
||||
public static String getFileExtension(String fileName) {
|
||||
int dotIndex = fileName.lastIndexOf('.');
|
||||
return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建快捷方式(.url文件内容)
|
||||
* 功能:生成Internet快捷方式文件内容
|
||||
* @param name 快捷方式名称
|
||||
* @param url 目标URL地址
|
||||
* @param icon 图标路径(可选)
|
||||
* @return 包含.url文件内容的输入流
|
||||
*/
|
||||
public static InputStream createInternetShortcut(String name, String url, String icon) {
|
||||
StringWriter sw = new StringWriter();
|
||||
try {
|
||||
// 按照Windows快捷方式格式写入内容
|
||||
sw.write("[InternetShortcut]\n");
|
||||
sw.write("URL=" + url + "\n");
|
||||
if (oConvertUtils.isNotEmpty(icon)) {
|
||||
sw.write("IconFile=" + icon + "\n");
|
||||
}
|
||||
// 将字符串内容转换为输入流
|
||||
return new ByteArrayInputStream(sw.toString().getBytes(StandardCharsets.UTF_8));
|
||||
} finally {
|
||||
IoUtil.close(sw);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 从URL中提取文件名
|
||||
* 功能:从HTTP URL或本地路径中提取纯文件名
|
||||
* @param fileUrl 文件URL
|
||||
* @return 文件名(不含路径)
|
||||
*/
|
||||
public static String getFileNameFromUrl(String fileUrl) {
|
||||
try {
|
||||
// 处理HTTP URL:从路径部分提取文件名
|
||||
if (fileUrl.startsWith(CommonConstant.STR_HTTP)) {
|
||||
URL url = new URL(fileUrl);
|
||||
String path = url.getPath();
|
||||
return path.substring(path.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
// 处理本地文件路径:从文件路径提取文件名
|
||||
return fileUrl.substring(fileUrl.lastIndexOf(File.separator) + 1);
|
||||
} catch (Exception e) {
|
||||
// 如果解析失败,使用时间戳作为文件名
|
||||
return "file_" + System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 生成ZIP中的文件名
|
||||
* 功能:避免文件名冲突,为多个文件添加序号
|
||||
* @param fileUrl 文件URL(用于提取原始文件名)
|
||||
* @param index 文件序号(从0开始)
|
||||
* @param total 文件总数
|
||||
* @return 处理后的文件名(带序号)
|
||||
*/
|
||||
public static String generateFileName(String fileUrl, int index, int total) {
|
||||
// 从URL中提取原始文件名
|
||||
String originalFileName = getFileNameFromUrl(fileUrl);
|
||||
|
||||
// 如果只有一个文件,直接使用原始文件名
|
||||
if (total == 1) {
|
||||
return originalFileName;
|
||||
}
|
||||
|
||||
// 多个文件时,使用序号+原始文件名
|
||||
String extension = getFileExtension(originalFileName);
|
||||
String nameWithoutExtension = originalFileName.replace("." + extension, "");
|
||||
|
||||
return String.format("%s_%d.%s", nameWithoutExtension, index + 1, extension);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.jeecg.common.util;
|
|||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
|
@ -16,6 +17,7 @@ import java.util.List;
|
|||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Lazy(false)
|
||||
public class PmsUtil {
|
||||
|
||||
|
||||
|
|
|
@ -221,6 +221,63 @@ public class RestUtil {
|
|||
return RT.exchange(url, method, request, responseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求(支持自定义超时时间)
|
||||
*
|
||||
* @param url 请求地址
|
||||
* @param method 请求方式
|
||||
* @param headers 请求头 可空
|
||||
* @param variables 请求url参数 可空
|
||||
* @param params 请求body参数 可空
|
||||
* @param responseType 返回类型
|
||||
* @param timeout 超时时间(毫秒),如果为0或负数则使用默认超时
|
||||
* @return ResponseEntity<responseType>
|
||||
*/
|
||||
public static <T> ResponseEntity<T> request(String url, HttpMethod method, HttpHeaders headers,
|
||||
JSONObject variables, Object params, Class<T> responseType, int timeout) {
|
||||
log.info(" RestUtil --- request --- url = "+ url + ", timeout = " + timeout);
|
||||
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
throw new RuntimeException("url 不能为空");
|
||||
}
|
||||
if (method == null) {
|
||||
throw new RuntimeException("method 不能为空");
|
||||
}
|
||||
if (headers == null) {
|
||||
headers = new HttpHeaders();
|
||||
}
|
||||
|
||||
// 创建自定义RestTemplate(如果需要设置超时)
|
||||
RestTemplate restTemplate = RT;
|
||||
if (timeout > 0) {
|
||||
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
|
||||
requestFactory.setConnectTimeout(timeout);
|
||||
requestFactory.setReadTimeout(timeout);
|
||||
restTemplate = new RestTemplate(requestFactory);
|
||||
// 解决乱码问题
|
||||
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
// 请求体
|
||||
String body = "";
|
||||
if (params != null) {
|
||||
if (params instanceof JSONObject) {
|
||||
body = ((JSONObject) params).toJSONString();
|
||||
} else {
|
||||
body = params.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// 拼接 url 参数
|
||||
if (variables != null && !variables.isEmpty()) {
|
||||
url += ("?" + asUrlVariables(variables));
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
HttpEntity<String> request = new HttpEntity<>(body, headers);
|
||||
return restTemplate.exchange(url, method, request, responseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取JSON请求头
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package org.jeecg.common.util;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* @date 2025-09-04
|
||||
* @author scott
|
||||
*
|
||||
* @Description: 支持shiro的API,获取当前登录人方法的线程池
|
||||
*/
|
||||
public class ShiroThreadPoolExecutor extends ThreadPoolExecutor {
|
||||
|
||||
public ShiroThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
|
||||
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
SecurityManager securityManager = SecurityUtils.getSecurityManager();
|
||||
super.execute(() -> {
|
||||
try {
|
||||
ThreadContext.bind(securityManager);
|
||||
ThreadContext.bind(subject);
|
||||
command.run();
|
||||
} finally {
|
||||
ThreadContext.unbindSubject();
|
||||
ThreadContext.unbindSecurityManager();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -65,6 +65,10 @@ public class TokenUtils {
|
|||
if (tenantId == null) {
|
||||
tenantId = oConvertUtils.getString(request.getHeader(CommonConstant.TENANT_ID));
|
||||
}
|
||||
|
||||
if (oConvertUtils.isNotEmpty(tenantId) && "undefined".equals(tenantId)) {
|
||||
return null;
|
||||
}
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
|
|
|
@ -474,6 +474,23 @@ public class oConvertUtils {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否为JSON格式
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
public static boolean isJson(String str) {
|
||||
if (str == null || str.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
com.alibaba.fastjson.JSON.parse(str);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Map对象
|
||||
*/
|
||||
|
@ -1132,7 +1149,15 @@ public class oConvertUtils {
|
|||
* @date 2020/9/12 15:50
|
||||
*/
|
||||
public static <T> boolean isIn(T obj, T... objs) {
|
||||
return isIn(obj, objs);
|
||||
if (isEmpty(objs)) {
|
||||
return false;
|
||||
}
|
||||
for (T obj1 : objs) {
|
||||
if (isEqual(obj, obj1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,13 +3,14 @@ package org.jeecg.config;
|
|||
import org.jeecgframework.core.util.ApplicationContextUtil;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
/**
|
||||
* @Author: Scott
|
||||
* @Date: 2018/2/7
|
||||
* @description: autopoi 配置类
|
||||
*/
|
||||
|
||||
@Lazy(false)
|
||||
@Configuration
|
||||
public class AutoPoiConfig {
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
* @Version:1.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Lazy(false)
|
||||
@Service
|
||||
public class AutoPoiDictConfig implements AutoPoiDictServiceI {
|
||||
final static String EXCEL_SPLIT_TAG = "_";
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package org.jeecg.config;
|
||||
|
||||
import org.jeecg.config.vo.GaoDeApi;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
/**
|
||||
* 高德账号配置
|
||||
*/
|
||||
@Lazy(false)
|
||||
@Configuration("jeecgGaodeBaseConfig")
|
||||
@ConfigurationProperties(prefix = "jeecg.jmreport")
|
||||
public class JeecgGaodeBaseConfig {
|
||||
|
||||
/**
|
||||
* 高德开放API配置
|
||||
*/
|
||||
private GaoDeApi gaoDeApi;
|
||||
|
||||
public GaoDeApi getGaoDeApi() {
|
||||
return gaoDeApi;
|
||||
}
|
||||
|
||||
public void setGaoDeApi(GaoDeApi gaoDeApi) {
|
||||
this.gaoDeApi = gaoDeApi;
|
||||
}
|
||||
|
||||
}
|
|
@ -2,12 +2,14 @@ package org.jeecg.config;
|
|||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 设置静态参数初始化
|
||||
* @author: jeecg-boot
|
||||
*/
|
||||
@Lazy(false)
|
||||
@Component
|
||||
@Data
|
||||
public class StaticConfig {
|
||||
|
|
|
@ -88,7 +88,7 @@ public class Swagger3Config implements WebMvcConfigurer {
|
|||
return new OpenAPI()
|
||||
.info(new Info()
|
||||
.title("JeecgBoot 后台服务API接口文档")
|
||||
.version("3.8.2")
|
||||
.version("3.8.3")
|
||||
.contact(new Contact().name("北京国炬信息技术有限公司").url("www.jeccg.com").email("jeecgos@163.com"))
|
||||
.description( "后台API接口")
|
||||
.termsOfService("NO terms of service")
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
package org.jeecg.config.mybatis;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.zhyd.oauth.log.Log;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.LongValue;
|
||||
import org.jeecg.common.config.TenantContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.TenantConstant;
|
||||
|
@ -22,14 +24,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.LongValue;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单数据源配置(jeecg.datasource.open = false时生效)
|
||||
|
|
|
@ -8,11 +8,13 @@ import org.springframework.beans.factory.annotation.Value;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
/**
|
||||
* Minio文件上传配置文件
|
||||
* @author: jeecg-boot
|
||||
*/
|
||||
@Lazy(false)
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "jeecg.minio", name = "minio_url")
|
||||
|
|
|
@ -5,11 +5,13 @@ import org.springframework.beans.factory.annotation.Value;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
/**
|
||||
* 云存储 配置
|
||||
* @author: jeecg-boot
|
||||
*/
|
||||
@Lazy(false)
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "jeecg.oss", name = "endpoint")
|
||||
public class OssConfiguration {
|
||||
|
|
|
@ -126,6 +126,7 @@ public class ShiroConfig {
|
|||
filterChainDefinitionMap.put("/**/*.ttf", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.woff", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.woff2", "anon");
|
||||
|
||||
filterChainDefinitionMap.put("/**/*.glb", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.wasm", "anon");
|
||||
//update-end--Author:scott Date:20221116 for:排除静态资源后缀
|
||||
|
@ -177,7 +178,9 @@ public class ShiroConfig {
|
|||
filterChainDefinitionMap.put("/sys/version/app3version", "anon");
|
||||
//仪表盘(按钮通信)
|
||||
filterChainDefinitionMap.put("/dragChannelSocket/**","anon");
|
||||
|
||||
//App vue3版本查询版本接口
|
||||
filterChainDefinitionMap.put("/sys/version/app3version", "anon");
|
||||
|
||||
//性能监控——安全隐患泄露TOEKN(durid连接池也有)
|
||||
//filterChainDefinitionMap.put("/actuator/**", "anon");
|
||||
//测试模块排除
|
||||
|
@ -187,7 +190,7 @@ public class ShiroConfig {
|
|||
filterChainDefinitionMap.put("/error", "anon");
|
||||
// 企业微信证书排除
|
||||
filterChainDefinitionMap.put("/WW_verify*", "anon");
|
||||
|
||||
|
||||
// 添加自己的过滤器并且取名为jwt
|
||||
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
|
||||
//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
|
||||
|
@ -228,6 +231,7 @@ public class ShiroConfig {
|
|||
registration.addUrlPatterns("/airag/chat/send");
|
||||
registration.addUrlPatterns("/airag/app/debug");
|
||||
registration.addUrlPatterns("/airag/app/prompt/generate");
|
||||
registration.addUrlPatterns("/airag/chat/receive/**");
|
||||
//支持异步
|
||||
registration.setAsyncSupported(true);
|
||||
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
|
||||
|
|
|
@ -106,8 +106,8 @@ public class ShiroRealm extends AuthorizingRealm {
|
|||
try {
|
||||
loginUser = this.checkUserTokenIsEffect(token);
|
||||
} catch (AuthenticationException e) {
|
||||
log.error("—————校验 check token 失败——————————"+ e.getMessage(), e);
|
||||
JwtUtil.responseError(SpringContextUtils.getHttpServletResponse(),401,e.getMessage());
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return new SimpleAuthenticationInfo(loginUser, token, getName());
|
||||
|
@ -122,7 +122,7 @@ public class ShiroRealm extends AuthorizingRealm {
|
|||
// 解密获得username,用于和数据库进行对比
|
||||
String username = JwtUtil.getUsername(token);
|
||||
if (username == null) {
|
||||
throw new AuthenticationException("token非法无效!");
|
||||
throw new AuthenticationException("Token非法无效!");
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
|
|
|
@ -56,7 +56,7 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
|
|||
executeLogin(request, response);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
JwtUtil.responseError(response,401,CommonConstant.TOKEN_IS_INVALID_MSG);
|
||||
JwtUtil.responseError((HttpServletResponse)response,401,CommonConstant.TOKEN_IS_INVALID_MSG);
|
||||
return false;
|
||||
//throw new AuthenticationException("Token失效,请重新登录", e);
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ public class JeecgDemoController extends JeecgController<JeecgDemo, IJeecgDemoSe
|
|||
*/
|
||||
@Operation(summary = "获取Demo数据列表")
|
||||
@GetMapping(value = "/list")
|
||||
@PermissionData(pageComponent = "jeecg/JeecgDemoList")
|
||||
@PermissionData(pageComponent = "system/examples/demo/index")
|
||||
public Result<?> list(JeecgDemo jeecgDemo, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
QueryWrapper<JeecgDemo> queryWrapper = QueryGenerator.initQueryWrapper(jeecgDemo, req.getParameterMap());
|
||||
|
|
|
@ -409,4 +409,30 @@ public class DlMockController {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取车辆最后一个位置
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/findLatestCarLngLat")
|
||||
public List findLatestCarLngLat() {
|
||||
// 模拟JSON数据路径
|
||||
String path = "classpath:org/jeecg/modules/dlglong/json/CarLngLat.json";
|
||||
// 读取JSON数据
|
||||
return readJsonData(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取车辆最后一个位置
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/findCarTrace")
|
||||
public List findCarTrace() {
|
||||
// 模拟JSON数据路径
|
||||
String path = "classpath:org/jeecg/modules/dlglong/json/CarTrace.json";
|
||||
// 读取JSON数据
|
||||
return readJsonData(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
[
|
||||
{
|
||||
"id": "6891ba44421aa907bcb7390c",
|
||||
"alarm": "0",
|
||||
"altitude": "13",
|
||||
"direction": "0",
|
||||
"latitude": "38.918739",
|
||||
"longitude": "117.758737",
|
||||
"speed": "11",
|
||||
"status": "4980739",
|
||||
"timestamp": "2025-08-05T16:01:07",
|
||||
"imei": "18441136860"
|
||||
}
|
||||
]
|
File diff suppressed because it is too large
Load Diff
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>jeecg-system-api</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>3.8.2</version>
|
||||
<version>3.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import lombok.experimental.Accessors;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 接口表
|
||||
|
|
|
@ -322,7 +322,7 @@ public class SysAnnouncementController {
|
|||
try {
|
||||
// 同步企业微信、钉钉的消息通知
|
||||
Response<String> dtResponse = dingtalkService.sendActionCardMessage(sysAnnouncement, null, true);
|
||||
wechatEnterpriseService.sendTextCardMessage(sysAnnouncement, true);
|
||||
wechatEnterpriseService.sendTextCardMessage(sysAnnouncement, null,true);
|
||||
|
||||
if (dtResponse != null && dtResponse.isSuccess()) {
|
||||
String taskId = dtResponse.getResult();
|
||||
|
@ -726,6 +726,18 @@ public class SysAnnouncementController {
|
|||
return Result.ok("公告消息访问次数+1次");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量下载文件
|
||||
* @param id
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
@GetMapping("/downLoadFiles")
|
||||
public void downLoadFiles(@RequestParam(name="id") String id,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response){
|
||||
sysAnnouncementService.downLoadFiles(id,request,response);
|
||||
}
|
||||
/**
|
||||
* 根据异常信息确定友好的错误提示
|
||||
*/
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
package org.jeecg.modules.system.service.impl;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.compress.archivers.zip.Zip64Mode;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.common.util.FileDownloadUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.config.JeecgBaseConfig;
|
||||
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
|
||||
import org.jeecg.modules.system.entity.SysAnnouncement;
|
||||
import org.jeecg.modules.system.entity.SysAnnouncementSend;
|
||||
|
@ -23,6 +28,10 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
|
@ -51,7 +60,9 @@ public class SysAnnouncementServiceImpl extends ServiceImpl<SysAnnouncementMappe
|
|||
private SysAnnouncementSendMapper sysAnnouncementSendMapper;
|
||||
@Autowired
|
||||
private ISysAnnouncementSendService sysAnnouncementSendService;
|
||||
|
||||
@Autowired
|
||||
private JeecgBaseConfig jeecgBaseConfig;
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void saveAnnouncement(SysAnnouncement sysAnnouncement) {
|
||||
|
@ -251,4 +262,64 @@ public class SysAnnouncementServiceImpl extends ServiceImpl<SysAnnouncementMappe
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量下载文件
|
||||
* @param id
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
@Override
|
||||
public void downLoadFiles(String id, HttpServletRequest request, HttpServletResponse response) {
|
||||
// 参数校验
|
||||
if (oConvertUtils.isEmpty(id)) {
|
||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取文章信息
|
||||
SysAnnouncement sysAnnouncement = this.baseMapper.selectById(id);
|
||||
if (oConvertUtils.isEmpty(sysAnnouncement)) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
//设置HTTP响应头:准备文件下载
|
||||
response.reset();
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.setContentType("application/force-download");
|
||||
ZipArchiveOutputStream zous = null;
|
||||
try {
|
||||
// 生成ZIP文件名:使用文章标题+时间戳避免重名
|
||||
String title = sysAnnouncement.getTitile() + new Date().getTime();
|
||||
String zipName = URLEncoder.encode( title + ".zip", "UTF-8").replaceAll("\\+", "%20");
|
||||
response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + zipName);
|
||||
// 创建ZIP输出流:直接输出到HTTP响应流
|
||||
zous = new ZipArchiveOutputStream(response.getOutputStream());
|
||||
zous.setUseZip64(Zip64Mode.AsNeeded);// 支持大文件
|
||||
|
||||
// 批量下载文件
|
||||
String[] fileUrls = sysAnnouncement.getFiles().split(",");
|
||||
// 遍历所有文件URL
|
||||
for (int i = 0; i < fileUrls.length; i++) {
|
||||
String fileUrl = fileUrls[i].trim();
|
||||
if (oConvertUtils.isEmpty(fileUrl)) {
|
||||
continue;
|
||||
}
|
||||
// 生成ZIP内文件名:避免重名,添加序号
|
||||
String fileName = FileDownloadUtils.generateFileName(fileUrl, i, fileUrls.length);
|
||||
String uploadUrl = jeecgBaseConfig.getPath().getUpload();
|
||||
// 下载单个文件并添加到ZIP
|
||||
FileDownloadUtils.downLoadSingleFile(fileUrl,fileName,uploadUrl, zous);
|
||||
}
|
||||
// 完成ZIP写入
|
||||
zous.finish();
|
||||
// 刷新缓冲区确保数据发送
|
||||
response.flushBuffer();
|
||||
} catch (IOException e) {
|
||||
log.error("文件下载失败"+e.getMessage(), e);
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
// 确保流关闭,防止资源泄漏
|
||||
IoUtil.close(zous);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,19 +3,21 @@ package org.jeecg.modules.openapi.test;
|
|||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
|
||||
public class SampleOpenApiTest {
|
||||
private final String base_url = "http://localhost:8080/jeecg-boot";
|
||||
private final String appKey = "ak-pFjyNHWRsJEFWlu6";
|
||||
private final String searchKey = "4hV5dBrZtmGAtPdbA5yseaeKRYNpzGsS";
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
// 根据部门ID查询用户
|
||||
|
|
Loading…
Reference in New Issue