【v3.8.3】底层core的一些功能修改

pull/8786/merge
JEECG 2025-09-14 11:55:31 +08:00
parent 152e8c7aaa
commit a4343fc2cb
37 changed files with 2488 additions and 64 deletions

View File

@ -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 ,StringMap
* @author chenrui
* @date 2025/9/2 11:43
*/
Object runAiragFlow(AiragFlowDTO airagFlowDTO);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -8,21 +8,30 @@ import java.util.List;
/**
*
*
* @author: jeecg-boot
*/
@EnumDict("messageType")
public enum MessageTypeEnum {
/** 系统消息 */
/**
*
*/
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());

View File

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

View File

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

View File

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

View File

@ -49,20 +49,20 @@ 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");
public static void responseError(HttpServletResponse response, Integer code, String errorMsg) {
try {
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();
// 设置响应头和内容类型
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);
}
@ -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;
}
}

View File

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

View File

@ -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 URLHTTP 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);
}
}
/**
*
* URLHTTP
* @param fileUrl URLHTTP
* @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);
}
}

View File

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

View File

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

View File

@ -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: shiroAPI线
*/
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();
}
});
}
}

View File

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

View File

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

View File

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

View File

@ -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 = "_";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,6 +178,8 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/sys/version/app3version", "anon");
//仪表盘(按钮通信)
filterChainDefinitionMap.put("/dragChannelSocket/**","anon");
//App vue3版本查询版本接口
filterChainDefinitionMap.put("/sys/version/app3version", "anon");
//性能监控——安全隐患泄露TOEKNdurid连接池也有
//filterChainDefinitionMap.put("/actuator/**", "anon");
@ -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);

View File

@ -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非法无效!");
}
// 查询用户信息

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,6 @@ import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
*

View File

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

View File

@ -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,6 +60,8 @@ public class SysAnnouncementServiceImpl extends ServiceImpl<SysAnnouncementMappe
private SysAnnouncementSendMapper sysAnnouncementSendMapper;
@Autowired
private ISysAnnouncementSendService sysAnnouncementSendService;
@Autowired
private JeecgBaseConfig jeecgBaseConfig;
@Transactional(rollbackFor = Exception.class)
@Override
@ -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);
}
}
}

View File

@ -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查询用户