mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2026-02-02 08:35:25 +08:00
v3.9.1 后台代码
This commit is contained in:
@ -33,4 +33,10 @@ public class AiragFlowDTO implements Serializable {
|
||||
* 输入参数
|
||||
*/
|
||||
private Map<String, Object> inputParams;
|
||||
|
||||
/**
|
||||
* 是否流式返回
|
||||
*/
|
||||
private boolean isStream;
|
||||
|
||||
}
|
||||
|
||||
@ -47,4 +47,8 @@ public interface TenantConstant {
|
||||
*/
|
||||
String APP_ADMIN = "appAdmin";
|
||||
|
||||
/**
|
||||
* 增加SignatureCheck注解POST请求的URL
|
||||
*/
|
||||
String[] SIGNATURE_CHECK_POST_URL = { "/sys/tenant/joinTenantByHouseNumber", "/sys/tenant/invitationUser" };
|
||||
}
|
||||
|
||||
@ -56,7 +56,9 @@ public class CommonUtils {
|
||||
public static String uploadOnlineImage(byte[] data,String basePath,String bizPath,String uploadType){
|
||||
String dbPath = null;
|
||||
String fileName = "image" + Math.round(Math.random() * 100000000000L);
|
||||
fileName += "." + PoiPublicUtil.getFileExtendName(data);
|
||||
//update-begin---author:wangshuai---date:2026-01-08---for:【QQYUN-14535】ai生成图片的后缀不一致的,导致不展示---
|
||||
fileName += "." + PoiPublicUtil.getFileExtendName(data).toLowerCase();
|
||||
//update-end---author:wangshuai---date:2026-01-08---for:【QQYUN-14535】ai生成图片的后缀不一致的,导致不展示---
|
||||
try {
|
||||
if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){
|
||||
File file = new File(basePath + File.separator + bizPath + File.separator );
|
||||
|
||||
@ -46,7 +46,7 @@ public class RestUtil {
|
||||
|
||||
public static String getBaseUrl() {
|
||||
String basepath = getDomain() + getPath();
|
||||
log.info(" RestUtil.getBaseUrl: " + basepath);
|
||||
log.debug(" RestUtil.getBaseUrl: " + basepath);
|
||||
return basepath;
|
||||
}
|
||||
|
||||
@ -199,7 +199,7 @@ public class RestUtil {
|
||||
* @return ResponseEntity<responseType>
|
||||
*/
|
||||
public static <T> ResponseEntity<T> request(String url, HttpMethod method, HttpHeaders headers, JSONObject variables, Object params, Class<T> responseType) {
|
||||
log.info(" RestUtil --- request --- url = "+ url);
|
||||
log.debug(" RestUtil --- request --- url = "+ url);
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
throw new RuntimeException("url 不能为空");
|
||||
}
|
||||
@ -230,7 +230,7 @@ public class RestUtil {
|
||||
String current = headers.getFirst(HttpHeaders.CONTENT_LENGTH);
|
||||
if (current == null || !current.equals(String.valueOf(contentLength))) {
|
||||
headers.setContentLength(contentLength);
|
||||
log.info(" RestUtil --- request --- 修正/设置 Content-Length = " + contentLength + (current!=null?" (原值="+current+")":""));
|
||||
log.debug(" RestUtil --- request --- 修正/设置 Content-Length = " + contentLength + (current!=null?" (原值="+current+")":""));
|
||||
}
|
||||
}
|
||||
// 发送请求
|
||||
@ -252,7 +252,7 @@ public class RestUtil {
|
||||
*/
|
||||
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);
|
||||
log.debug(" RestUtil --- request --- url = "+ url + ", timeout = " + timeout);
|
||||
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
throw new RuntimeException("url 不能为空");
|
||||
@ -302,7 +302,7 @@ public class RestUtil {
|
||||
String current = headers.getFirst(HttpHeaders.CONTENT_LENGTH);
|
||||
if (current == null || !current.equals(String.valueOf(contentLength))) {
|
||||
headers.setContentLength(contentLength);
|
||||
log.info(" RestUtil --- request(timeout) --- 修正/设置 Content-Length = " + contentLength + (current!=null?" (原值="+current+")":""));
|
||||
log.debug(" RestUtil --- request(timeout) --- 修正/设置 Content-Length = " + contentLength + (current!=null?" (原值="+current+")":""));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1231,5 +1231,26 @@ public class oConvertUtils {
|
||||
.filter(name -> beanWrapper.getPropertyValue(name) == null)
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* String转换long类型
|
||||
*
|
||||
* @param v
|
||||
* @param def
|
||||
* @return
|
||||
*/
|
||||
public static long getLong(Object v, long def) {
|
||||
if (v == null) {
|
||||
return def;
|
||||
};
|
||||
if (v instanceof Number) {
|
||||
return ((Number) v).longValue();
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(v.toString());
|
||||
} catch (Exception e) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package org.jeecg.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* ai配置类,通用的配置可以放到这里面
|
||||
*
|
||||
* @Author: wangshuai
|
||||
* @Date: 2025/12/17 14:00
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = AiRagConfigBean.PREFIX)
|
||||
public class AiRagConfigBean {
|
||||
public static final String PREFIX = "jeecg.airag";
|
||||
|
||||
/**
|
||||
* 敏感节点
|
||||
* stdio mpc命令行功能开启,sql:AI流程SQL节点开启
|
||||
*/
|
||||
private String allowSensitiveNodes = "";
|
||||
}
|
||||
@ -12,7 +12,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author eightmonth@qq.com
|
||||
* 启动程序修改DruidWallConfig配置
|
||||
* 允许SELECT语句的WHERE子句是一个永真条件
|
||||
* @author eightmonth
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
package org.jeecg.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
|
||||
/**
|
||||
* 任务调度器配置
|
||||
* 提供 ThreadPoolTaskScheduler Bean 用于 AI RAG 流程调度等功能
|
||||
* 仅当容器中不存在 ThreadPoolTaskScheduler 时才创建
|
||||
*
|
||||
* @author jeecg
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class TaskSchedulerConfig {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ThreadPoolTaskScheduler.class)
|
||||
public ThreadPoolTaskScheduler taskScheduler() {
|
||||
log.info("初始化定时任务调度器 ThreadPoolTaskScheduler");
|
||||
|
||||
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||
scheduler.setPoolSize(10);
|
||||
scheduler.setThreadNamePrefix("airag-scheduler-");
|
||||
scheduler.setWaitForTasksToCompleteOnShutdown(true);
|
||||
scheduler.setAwaitTerminationSeconds(60);
|
||||
return scheduler;
|
||||
}
|
||||
}
|
||||
@ -177,6 +177,8 @@ 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");
|
||||
@ -188,6 +190,8 @@ public class ShiroConfig {
|
||||
// 企业微信证书排除
|
||||
filterChainDefinitionMap.put("/WW_verify*", "anon");
|
||||
|
||||
filterChainDefinitionMap.put("/openapi/call/**", "anon");
|
||||
|
||||
// 添加自己的过滤器并且取名为jwt
|
||||
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
|
||||
//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
|
||||
@ -227,6 +231,11 @@ public class ShiroConfig {
|
||||
registration.addUrlPatterns("/airag/app/debug");
|
||||
registration.addUrlPatterns("/airag/app/prompt/generate");
|
||||
registration.addUrlPatterns("/airag/chat/receive/**");
|
||||
// 添加SSE接口的异步支持
|
||||
registration.addUrlPatterns("/airag/extData/evaluator/debug");
|
||||
registration.addUrlPatterns("/drag/onlDragDatasetHead/generateChartSse");
|
||||
registration.addUrlPatterns("/drag/onlDragDatasetHead/updateChartOptSse");
|
||||
registration.addUrlPatterns("/drag/onlDragDatasetHead/generateSqlSse");
|
||||
//支持异步
|
||||
registration.setAsyncSupported(true);
|
||||
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package org.jeecg.config.sign.aspect;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
@ -13,7 +12,12 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 基于AOP的签名验证切面
|
||||
@ -56,7 +60,22 @@ public class SignatureCheckAspect {
|
||||
log.info("签名验证已禁用,跳过");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// update-begin---author:sjlei---date:20260115 for: 查找带有@RequestBody注解的参数,解决签名校验时读取请求体为空的问题
|
||||
Object bodyParam = null;
|
||||
Object[] args = point.getArgs();
|
||||
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Object arg = args[i];
|
||||
Annotation[] annotations = parameterAnnotations[i];
|
||||
boolean hasRequestBodyAnnotation = Arrays.stream(annotations).anyMatch(annotation -> annotation.annotationType().equals(RequestBody.class));
|
||||
if (hasRequestBodyAnnotation) {
|
||||
// 捕获携带@RequestBody注解的参数,供签名校验使用
|
||||
bodyParam = arg;
|
||||
}
|
||||
}
|
||||
// update-end-----author:sjlei---date:20260115 for: 查找带有@RequestBody注解的参数,解决签名校验时读取请求体为空的问题
|
||||
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (attributes == null) {
|
||||
log.error("无法获取请求上下文");
|
||||
@ -68,7 +87,7 @@ public class SignatureCheckAspect {
|
||||
|
||||
try {
|
||||
// 直接调用SignAuthInterceptor的验证逻辑
|
||||
signAuthInterceptor.validateSignature(request);
|
||||
signAuthInterceptor.validateSignature(request, bodyParam);
|
||||
log.info("AOP签名验证通过");
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package org.jeecg.config.sign.interceptor;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.common.constant.TenantConstant;
|
||||
import org.jeecg.common.util.PathMatcherUtil;
|
||||
import org.jeecg.config.JeecgBaseConfig;
|
||||
import org.jeecg.config.filter.RequestBodyReserveFilter;
|
||||
@ -64,6 +65,8 @@ public class SignAuthConfiguration implements WebMvcConfigurer {
|
||||
//------------------------------------------------------------
|
||||
// 建议此处只添加post请求地址而不是所有的都需要走过滤器
|
||||
registration.addUrlPatterns(signUrlsArray);
|
||||
// 增加注解签名请求
|
||||
registration.addUrlPatterns(TenantConstant.SIGNATURE_CHECK_POST_URL);
|
||||
return registration;
|
||||
}
|
||||
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
package org.jeecg.config.sign.interceptor;
|
||||
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.SortedMap;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.DateUtils;
|
||||
@ -14,8 +16,9 @@ import org.jeecg.config.sign.util.HttpUtils;
|
||||
import org.jeecg.config.sign.util.SignUtil;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.SortedMap;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 签名拦截器
|
||||
@ -47,7 +50,7 @@ public class SignAuthInterceptor implements HandlerInterceptor {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 签名验证核心逻辑
|
||||
* 提取出来供AOP切面复用
|
||||
@ -55,12 +58,22 @@ public class SignAuthInterceptor implements HandlerInterceptor {
|
||||
* @throws IllegalArgumentException 验证失败时抛出异常
|
||||
*/
|
||||
public void validateSignature(HttpServletRequest request) throws IllegalArgumentException {
|
||||
validateSignature(request, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名验证核心逻辑
|
||||
* 提取出来供AOP切面复用
|
||||
* @param request HTTP请求
|
||||
* @throws IllegalArgumentException 验证失败时抛出异常
|
||||
*/
|
||||
public void validateSignature(HttpServletRequest request, Object bodyParam) throws IllegalArgumentException {
|
||||
try {
|
||||
log.debug("开始签名验证: {} {}", request.getMethod(), request.getRequestURI());
|
||||
|
||||
HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
|
||||
//获取全部参数(包括URL和body上的)
|
||||
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
|
||||
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper, bodyParam);
|
||||
log.debug("提取参数: {}", allParams);
|
||||
|
||||
//对参数进行签名验证
|
||||
|
||||
@ -35,7 +35,7 @@ public class HttpUtils {
|
||||
* @date 20210621
|
||||
* @param request
|
||||
*/
|
||||
public static SortedMap<String, String> getAllParams(HttpServletRequest request) throws IOException {
|
||||
public static SortedMap<String, String> getAllParams(HttpServletRequest request, Object bodyParam) throws IOException {
|
||||
|
||||
SortedMap<String, String> result = new TreeMap<>();
|
||||
// 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
|
||||
@ -65,7 +65,13 @@ public class HttpUtils {
|
||||
Map<String, String> allRequestParam = new HashMap<>(16);
|
||||
// get请求不需要拿body参数
|
||||
if (!HttpMethod.GET.name().equals(request.getMethod())) {
|
||||
allRequestParam = getAllRequestParam(request);
|
||||
if (bodyParam != null) {
|
||||
// update-begin---author:sjlei---date:20260115 for: 解决签名校验时读取请求体为空的问题
|
||||
allRequestParam = JSONObject.parseObject(JSONObject.toJSONString(bodyParam), Map.class);
|
||||
// update-end-----author:sjlei---date:20260115 for: 解决签名校验时读取请求体为空的问题
|
||||
} else {
|
||||
allRequestParam = getAllRequestParam(request);
|
||||
}
|
||||
}
|
||||
// 将URL的参数和body参数进行合并
|
||||
if (allRequestParam != null) {
|
||||
|
||||
@ -11,7 +11,12 @@ import org.springframework.util.DigestUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* 签名工具类
|
||||
@ -49,12 +54,7 @@ public class SignUtil {
|
||||
String paramsJsonStr = JSONObject.toJSONString(params);
|
||||
log.debug("Param paramsJsonStr : {}", paramsJsonStr);
|
||||
//设置签名秘钥
|
||||
JeecgBaseConfig jeecgBaseConfig = SpringContextUtils.getBean(JeecgBaseConfig.class);
|
||||
String signatureSecret = jeecgBaseConfig.getSignatureSecret();
|
||||
String curlyBracket = SymbolConstant.DOLLAR + SymbolConstant.LEFT_CURLY_BRACKET;
|
||||
if(oConvertUtils.isEmpty(signatureSecret) || signatureSecret.contains(curlyBracket)){
|
||||
throw new JeecgBootException("签名密钥 ${jeecg.signatureSecret} 缺少配置 !!");
|
||||
}
|
||||
String signatureSecret = SignUtil.getSignatureSecret();
|
||||
try {
|
||||
//【issues/I484RW】2.4.6部署后,下拉搜索框提示“sign签名检验失败”
|
||||
return DigestUtils.md5DigestAsHex((paramsJsonStr + signatureSecret).getBytes("UTF-8")).toUpperCase();
|
||||
@ -63,4 +63,129 @@ public class SignUtil {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过前端签名算法生成签名
|
||||
*
|
||||
* @param url 请求的完整URL(包含查询参数)
|
||||
* @param requestParams 使用 @RequestParam 获取的参数集合
|
||||
* @param requestBodyParams 使用 @RequestBody 获取的参数集合
|
||||
* @return 计算得到的签名(大写MD5),若参数不足返回 null
|
||||
*/
|
||||
public static String generateRequestSign(String url, Map<String, Object> requestParams, Map<String, Object> requestBodyParams) {
|
||||
if (oConvertUtils.isEmpty(url)) {
|
||||
return null;
|
||||
}
|
||||
// 解析URL上的查询参数与路径变量
|
||||
Map<String, String> urlParams = parseQueryString(url);
|
||||
// 合并URL参数与@RequestParam参数,确保数值和布尔类型转换为字符串
|
||||
Map<String, String> mergedParams = mergeObject(urlParams, requestParams);
|
||||
// 按需合并@RequestBody参数
|
||||
if (requestBodyParams != null && !requestBodyParams.isEmpty()) {
|
||||
mergedParams = mergeObject(mergedParams, requestBodyParams);
|
||||
}
|
||||
// 按键名升序排序,保持与前端一致的签名顺序
|
||||
SortedMap<String, String> sortedParams = new TreeMap<>(mergedParams);
|
||||
// 去除时间戳字段,避免参与签名
|
||||
sortedParams.remove("_t");
|
||||
// 序列化为JSON字符串
|
||||
String paramsJsonStr = JSONObject.toJSONString(sortedParams);
|
||||
// 读取签名秘钥
|
||||
String signatureSecret = getSignatureSecret();
|
||||
// 计算MD5摘要并转大写
|
||||
return DigestUtils.md5DigestAsHex((paramsJsonStr + signatureSecret).getBytes(StandardCharsets.UTF_8)).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析URL中的查询参数,并处理末尾逗号分隔的路径变量片段。
|
||||
*
|
||||
* @param url 请求的完整URL
|
||||
* @return 解析后的参数映射,数值与布尔类型均转换为字符串
|
||||
*/
|
||||
private static Map<String, String> parseQueryString(String url) {
|
||||
Map<String, String> result = new HashMap<>(16);
|
||||
int fragmentIndex = url.indexOf('#');
|
||||
if (fragmentIndex >= 0) {
|
||||
url = url.substring(0, fragmentIndex);
|
||||
}
|
||||
int questionIndex = url.indexOf('?');
|
||||
String paramString = null;
|
||||
if (questionIndex >= 0 && questionIndex < url.length() - 1) {
|
||||
paramString = url.substring(questionIndex + 1);
|
||||
}
|
||||
// 处理路径变量末尾以逗号分隔的段,例如 /sys/dict/getDictItems/sys_user,realname,username
|
||||
int lastSlashIndex = url.lastIndexOf(SymbolConstant.SINGLE_SLASH);
|
||||
if (lastSlashIndex >= 0 && lastSlashIndex < url.length() - 1) {
|
||||
String lastPathVariable = url.substring(lastSlashIndex + 1);
|
||||
int qIndexInPath = lastPathVariable.indexOf('?');
|
||||
if (qIndexInPath >= 0) {
|
||||
lastPathVariable = lastPathVariable.substring(0, qIndexInPath);
|
||||
}
|
||||
if (lastPathVariable.contains(SymbolConstant.COMMA)) {
|
||||
String decodedPathVariable = URLDecoder.decode(lastPathVariable, StandardCharsets.UTF_8);
|
||||
result.put(X_PATH_VARIABLE, decodedPathVariable);
|
||||
}
|
||||
}
|
||||
if (oConvertUtils.isNotEmpty(paramString)) {
|
||||
String[] pairs = paramString.split(SymbolConstant.AND);
|
||||
for (String pair : pairs) {
|
||||
int equalIndex = pair.indexOf('=');
|
||||
if (equalIndex > 0 && equalIndex < pair.length() - 1) {
|
||||
String key = pair.substring(0, equalIndex);
|
||||
String value = pair.substring(equalIndex + 1);
|
||||
// 解码并统一类型为字符串
|
||||
String decodedKey = URLDecoder.decode(key, StandardCharsets.UTF_8);
|
||||
String decodedValue = URLDecoder.decode(value, StandardCharsets.UTF_8);
|
||||
result.put(decodedKey, decodedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并两个参数映射,并保证数值与布尔类型统一转为字符串。
|
||||
*
|
||||
* @param target 初始参数映射
|
||||
* @param source 待合并的参数映射
|
||||
* @return 合并后的新映射
|
||||
*/
|
||||
private static Map<String, String> mergeObject(Map<String, String> target, Map<String, Object> source) {
|
||||
Map<String, String> merged = new HashMap<>(16);
|
||||
if (target != null && !target.isEmpty()) {
|
||||
merged.putAll(target);
|
||||
}
|
||||
if (source != null && !source.isEmpty()) {
|
||||
for (Map.Entry<String, Object> entry : source.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof Number) {
|
||||
// 数值类型转字符串,保持前后端一致
|
||||
merged.put(key, String.valueOf(value));
|
||||
} else if (value instanceof Boolean) {
|
||||
// 布尔类型转字符串,保持前后端一致
|
||||
merged.put(key, String.valueOf(value));
|
||||
} else if (value != null) {
|
||||
merged.put(key, String.valueOf(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取并校验签名秘钥配置。
|
||||
*
|
||||
* @return 有效的签名秘钥
|
||||
*/
|
||||
private static String getSignatureSecret() {
|
||||
JeecgBaseConfig jeecgBaseConfig = SpringContextUtils.getBean(JeecgBaseConfig.class);
|
||||
String signatureSecret = jeecgBaseConfig.getSignatureSecret();
|
||||
String curlyBracket = SymbolConstant.DOLLAR + SymbolConstant.LEFT_CURLY_BRACKET;
|
||||
if (oConvertUtils.isEmpty(signatureSecret) || signatureSecret.contains(curlyBracket)) {
|
||||
throw new JeecgBootException("签名密钥 ${jeecg.signatureSecret} 缺少配置 !!");
|
||||
}
|
||||
return signatureSecret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user