mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2025-12-08 17:12:28 +08:00
JeecgBoot低代码平台 3.0版本发布—新里程牌开始,迎接VUE3版本到来!!
This commit is contained in:
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>jeecg-boot-base-api</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>2.4.6</version>
|
||||
<version>3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@ -8,10 +8,10 @@ import java.util.SortedMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.jeecg.common.config.mqtoken.UserTokenContext;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.DateUtils;
|
||||
import org.jeecg.common.util.PathMatcherUtil;
|
||||
import org.jeecg.common.config.mqtoken.UserTokenContext;
|
||||
import org.jeecg.config.sign.interceptor.SignAuthConfiguration;
|
||||
import org.jeecg.config.sign.util.HttpUtils;
|
||||
import org.jeecg.config.sign.util.SignUtil;
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>jeecg-boot-base-api</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>2.4.6</version>
|
||||
<version>3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>jeecg-boot-base</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>2.4.6</version>
|
||||
<version>3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-base</artifactId>
|
||||
<version>2.4.6</version>
|
||||
<version>3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -114,7 +114,7 @@
|
||||
<dependency>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>hibernate-re</artifactId>
|
||||
<version>2.4.6-beta1</version>
|
||||
<version>3.0.0-beta</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据库驱动 -->
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
package org.jeecg.common.api;
|
||||
|
||||
import org.jeecg.common.api.vo.OaWpsModel;
|
||||
|
||||
/**
|
||||
* @Description: WPS通用接口
|
||||
* @Author: wangshuai
|
||||
* @Date:20200709
|
||||
* @Version:V1.0
|
||||
*/
|
||||
public interface IWpsBaseAPI {
|
||||
|
||||
/*根据模板id获取模板信息*/
|
||||
OaWpsModel getById(String id);
|
||||
|
||||
/*根据文件路径下载文件*/
|
||||
void downloadOosFiles(String objectName, String basePath,String fileName);
|
||||
|
||||
/*WPS 设置数据存储,用于逻辑判断*/
|
||||
void context(String type,String text);
|
||||
|
||||
/*删除WPS模板相关信息*/
|
||||
void deleteById(String id);
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package org.jeecg.common.api.desform;
|
||||
|
||||
import org.jeecg.common.system.vo.DictModel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 表单设计器【System】翻译API接口
|
||||
*
|
||||
* @author sunjianlei
|
||||
*/
|
||||
public interface ISysTranslateAPI {
|
||||
|
||||
/**
|
||||
* 查询分类字典翻译
|
||||
*/
|
||||
List<String> categoryLoadDictItem(String ids);
|
||||
|
||||
/**
|
||||
* 根据字典code加载字典text
|
||||
*
|
||||
* @param dictCode 顺序:tableName,text,code
|
||||
* @param keys 要查询的key
|
||||
* @return
|
||||
*/
|
||||
List<String> dictLoadDictItem(String dictCode, String keys);
|
||||
|
||||
/**
|
||||
* 获取字典数据
|
||||
*
|
||||
* @param dictCode 顺序:tableName,text,code
|
||||
* @param dictCode 要查询的key
|
||||
* @return
|
||||
*/
|
||||
List<DictModel> dictGetDictItems(String dictCode);
|
||||
|
||||
/**
|
||||
* 【JSearchSelectTag下拉搜索组件专用接口】
|
||||
* 大数据量的字典表 走异步加载 即前端输入内容过滤数据
|
||||
*
|
||||
* @param dictCode 字典code格式:table,text,code
|
||||
* @return
|
||||
*/
|
||||
List<DictModel> dictLoadDict(String dictCode, String keyword, Integer pageSize);
|
||||
|
||||
}
|
||||
@ -1,107 +0,0 @@
|
||||
package org.jeecg.common.api.vo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @Description: 文档
|
||||
* @Author: jeecg-boot
|
||||
* @Date: 2020-06-09
|
||||
* @Version: V1.0
|
||||
*/
|
||||
@Data
|
||||
@TableName("oa_wps_file")
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@ApiModel(value = "oa_wps_file对象", description = "文档")
|
||||
public class OaWpsModel implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@ApiModelProperty(value = "id")
|
||||
private String id;
|
||||
/**
|
||||
* name
|
||||
*/
|
||||
@Excel(name = "name", width = 15)
|
||||
@ApiModelProperty(value = "name")
|
||||
private String name;
|
||||
/**
|
||||
* version
|
||||
*/
|
||||
@Excel(name = "version", width = 15)
|
||||
@ApiModelProperty(value = "version")
|
||||
private Integer version;
|
||||
/**
|
||||
* size
|
||||
*/
|
||||
@Excel(name = "size", width = 15)
|
||||
@ApiModelProperty(value = "size")
|
||||
private Integer size;
|
||||
/**
|
||||
* downloadUrl
|
||||
*/
|
||||
@Excel(name = "downloadUrl", width = 15)
|
||||
@ApiModelProperty(value = "downloadUrl")
|
||||
private String downloadUrl;
|
||||
/**
|
||||
* deleted
|
||||
*/
|
||||
@Excel(name = "deleted", width = 15)
|
||||
@ApiModelProperty(value = "deleted")
|
||||
private String deleted;
|
||||
/**
|
||||
* canDelete
|
||||
*/
|
||||
@Excel(name = "canDelete", width = 15)
|
||||
@ApiModelProperty(value = "canDelete")
|
||||
private String canDelete;
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@ApiModelProperty(value = "创建人")
|
||||
private String createBy;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
@ApiModelProperty(value = "创建时间")
|
||||
private Date createTime;
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
@ApiModelProperty(value = "更新人")
|
||||
private String updateBy;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
@ApiModelProperty(value = "更新时间")
|
||||
private Date updateTime;
|
||||
/**
|
||||
* 组织机构编码
|
||||
*/
|
||||
@ApiModelProperty(value = "组织机构编码")
|
||||
private String sysOrgCode;
|
||||
|
||||
@TableField(exist = false)
|
||||
private String userId;
|
||||
}
|
||||
@ -30,7 +30,7 @@ public class Result<T> implements Serializable {
|
||||
* 返回处理消息
|
||||
*/
|
||||
@ApiModelProperty(value = "返回处理消息")
|
||||
private String message = "操作成功!";
|
||||
private String message = "";
|
||||
|
||||
/**
|
||||
* 返回代码
|
||||
@ -51,7 +51,16 @@ public class Result<T> implements Serializable {
|
||||
private long timestamp = System.currentTimeMillis();
|
||||
|
||||
public Result() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容VUE3版token失效不跳转登录页面
|
||||
* @param code
|
||||
* @param message
|
||||
*/
|
||||
public Result(Integer code,String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Result<T> success(String message) {
|
||||
@ -66,7 +75,6 @@ public class Result<T> implements Serializable {
|
||||
Result<Object> r = new Result<Object>();
|
||||
r.setSuccess(true);
|
||||
r.setCode(CommonConstant.SC_OK_200);
|
||||
r.setMessage("成功");
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -92,7 +100,6 @@ public class Result<T> implements Serializable {
|
||||
Result<T> r = new Result<T>();
|
||||
r.setSuccess(true);
|
||||
r.setCode(CommonConstant.SC_OK_200);
|
||||
r.setMessage("成功");
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ import org.springframework.util.StringUtils;
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -261,7 +262,10 @@ public class DictAspect {
|
||||
for (DictModel dict : texts) {
|
||||
String redisKey = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, dict.getValue());
|
||||
try {
|
||||
redisTemplate.opsForValue().set(redisKey, dict.getText());
|
||||
// update-begin-author:taoyan date:20211012 for: 字典表翻译注解缓存未更新 issues/3061
|
||||
// 保留5分钟
|
||||
redisTemplate.opsForValue().set(redisKey, dict.getText(), 300, TimeUnit.SECONDS);
|
||||
// update-end-author:taoyan date:20211012 for: 字典表翻译注解缓存未更新 issues/3061
|
||||
} catch (Exception e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
}
|
||||
|
||||
@ -292,6 +292,7 @@ public interface CommonConstant {
|
||||
public final static String X_ACCESS_TOKEN = "X-Access-Token";
|
||||
public final static String X_SIGN = "X-Sign";
|
||||
public final static String X_TIMESTAMP = "X-TIMESTAMP";
|
||||
public final static String TOKEN_IS_INVALID_MSG = "Token失效,请重新登录!";
|
||||
|
||||
/**
|
||||
* 多租户 请求头
|
||||
|
||||
@ -10,6 +10,8 @@ public interface DataBaseConstant {
|
||||
public static final String DB_TYPE_POSTGRESQL = "POSTGRESQL";
|
||||
public static final String DB_TYPE_SQLSERVER = "SQLSERVER";
|
||||
public static final String DB_TYPE_MARIADB = "MARIADB";
|
||||
public static final String DB_TYPE_DB2 = "DB2";
|
||||
public static final String DB_TYPE_HSQL = "HSQL";
|
||||
|
||||
// // 数据库类型,对应 database_type 字典
|
||||
// public static final String DB_TYPE_MYSQL_NUM = "1";
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
package org.jeecg.common.exception;
|
||||
|
||||
public class JeecgBoot401Exception extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public JeecgBoot401Exception(String message){
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JeecgBoot401Exception(Throwable cause)
|
||||
{
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public JeecgBoot401Exception(String message, Throwable cause)
|
||||
{
|
||||
super(message,cause);
|
||||
}
|
||||
}
|
||||
@ -7,8 +7,10 @@ import org.jeecg.common.api.vo.Result;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.data.redis.connection.PoolException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
@ -29,11 +31,21 @@ public class JeecgBootExceptionHandler {
|
||||
* 处理自定义异常
|
||||
*/
|
||||
@ExceptionHandler(JeecgBootException.class)
|
||||
public Result<?> handleRRException(JeecgBootException e){
|
||||
public Result<?> handleJeecgBootException(JeecgBootException e){
|
||||
log.error(e.getMessage(), e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理自定义异常
|
||||
*/
|
||||
@ExceptionHandler(JeecgBoot401Exception.class)
|
||||
@ResponseStatus(HttpStatus.UNAUTHORIZED)
|
||||
public Result<?> handleJeecgBoot401Exception(JeecgBoot401Exception e){
|
||||
log.error(e.getMessage(), e);
|
||||
return new Result(401,e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(NoHandlerFoundException.class)
|
||||
public Result<?> handlerNoFoundException(Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
|
||||
@ -37,8 +37,9 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
@Slf4j
|
||||
public class JeecgController<T, S extends IService<T>> {
|
||||
//issues/2933 JeecgController注入service时改用protected修饰,能避免重复引用service
|
||||
@Autowired
|
||||
S service;
|
||||
protected S service;
|
||||
|
||||
@Value("${jeecg.path.upload}")
|
||||
private String upLoadPath;
|
||||
|
||||
@ -99,7 +99,7 @@ public class QueryGenerator {
|
||||
* <br>正确示例:QueryWrapper<JeecgDemo> queryWrapper = new QueryWrapper<JeecgDemo>();
|
||||
* <br>3.也可以不使用这个方法直接调用 {@link #initQueryWrapper}直接获取实例
|
||||
*/
|
||||
public static void installMplus(QueryWrapper<?> queryWrapper,Object searchObj,Map<String, String[]> parameterMap) {
|
||||
private static void installMplus(QueryWrapper<?> queryWrapper,Object searchObj,Map<String, String[]> parameterMap) {
|
||||
|
||||
/*
|
||||
* 注意:权限查询由前端配置数据规则 当一个人有多个所属部门时候 可以在规则配置包含条件 orgCode 包含 #{sys_org_code}
|
||||
@ -216,7 +216,7 @@ public class QueryGenerator {
|
||||
}
|
||||
|
||||
//多字段排序 TODO 需要修改前端
|
||||
public static void doMultiFieldsOrder(QueryWrapper<?> queryWrapper,Map<String, String[]> parameterMap) {
|
||||
private static void doMultiFieldsOrder(QueryWrapper<?> queryWrapper,Map<String, String[]> parameterMap) {
|
||||
String column=null,order=null;
|
||||
if(parameterMap!=null&& parameterMap.containsKey(ORDER_COLUMN)) {
|
||||
column = parameterMap.get(ORDER_COLUMN)[0];
|
||||
@ -258,7 +258,7 @@ public class QueryGenerator {
|
||||
* @param parameterMap 参数对象
|
||||
* @param fieldColumnMap 实体字段和数据库列对应的map
|
||||
*/
|
||||
public static void doSuperQuery(QueryWrapper<?> queryWrapper,Map<String, String[]> parameterMap, Map<String,String> fieldColumnMap) {
|
||||
private static void doSuperQuery(QueryWrapper<?> queryWrapper,Map<String, String[]> parameterMap, Map<String,String> fieldColumnMap) {
|
||||
if(parameterMap!=null&& parameterMap.containsKey(SUPER_QUERY_PARAMS)){
|
||||
String superQueryParams = parameterMap.get(SUPER_QUERY_PARAMS)[0];
|
||||
String superQueryMatchType = parameterMap.get(SUPER_QUERY_MATCH_TYPE) != null ? parameterMap.get(SUPER_QUERY_MATCH_TYPE)[0] : MatchTypeEnum.AND.getValue();
|
||||
@ -347,7 +347,7 @@ public class QueryGenerator {
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
private static QueryRuleEnum convert2Rule(Object value) {
|
||||
public static QueryRuleEnum convert2Rule(Object value) {
|
||||
// 避免空数据
|
||||
// update-begin-author:taoyan date:20210629 for: 查询条件输入空格导致return null后续判断导致抛出null异常
|
||||
if (value == null) {
|
||||
@ -595,7 +595,7 @@ public class QueryGenerator {
|
||||
|
||||
|
||||
/**
|
||||
* 获取请求对应的数据权限规则
|
||||
* 获取请求对应的数据权限规则 TODO 相同列权限多个 有问题
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, SysPermissionDataRuleModel> getRuleMap() {
|
||||
@ -615,30 +615,6 @@ public class QueryGenerator {
|
||||
}
|
||||
return ruleMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求对应的数据权限规则
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, SysPermissionDataRuleModel> getRuleMap(List<SysPermissionDataRuleModel> list) {
|
||||
Map<String, SysPermissionDataRuleModel> ruleMap = new HashMap<String, SysPermissionDataRuleModel>();
|
||||
if(list==null){
|
||||
list =JeecgDataAutorUtils.loadDataSearchConditon();
|
||||
}
|
||||
if(list != null&&list.size()>0){
|
||||
if(list.get(0)==null){
|
||||
return ruleMap;
|
||||
}
|
||||
for (SysPermissionDataRuleModel rule : list) {
|
||||
String column = rule.getRuleColumn();
|
||||
if(QueryRuleEnum.SQL_RULES.getValue().equals(rule.getRuleConditions())) {
|
||||
column = SQL_RULES_COLUMN+rule.getId();
|
||||
}
|
||||
ruleMap.put(column, rule);
|
||||
}
|
||||
}
|
||||
return ruleMap;
|
||||
}
|
||||
|
||||
private static void addRuleToQueryWrapper(SysPermissionDataRuleModel dataRule, String name, Class propertyType, QueryWrapper<?> queryWrapper) {
|
||||
QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions());
|
||||
@ -761,7 +737,7 @@ public class QueryGenerator {
|
||||
* @param dataBaseType
|
||||
* @return
|
||||
*/
|
||||
public static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString, String dataBaseType) {
|
||||
private static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString, String dataBaseType) {
|
||||
String res = "";
|
||||
switch (rule) {
|
||||
case GT:
|
||||
@ -813,7 +789,7 @@ public class QueryGenerator {
|
||||
* @param isString
|
||||
* @return
|
||||
*/
|
||||
public static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString) {
|
||||
private static String getSingleSqlByRule(QueryRuleEnum rule,String field,Object value,boolean isString) {
|
||||
return getSingleSqlByRule(rule, field, value, isString, null);
|
||||
}
|
||||
|
||||
|
||||
@ -5,13 +5,19 @@ import com.auth0.jwt.JWTVerifier;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.exceptions.JWTDecodeException;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Date;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.DataBaseConstant;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
@ -31,6 +37,28 @@ public class JwtUtil {
|
||||
// Token过期时间30分钟(用户登录过期时间是此时间的两倍,以token在reids缓存时间为准)
|
||||
public static final long EXPIRE_TIME = 30 * 60 * 1000;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param response
|
||||
* @param code
|
||||
* @param errorMsg
|
||||
*/
|
||||
public static void responseError(ServletResponse response, Integer code, String errorMsg) {
|
||||
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
Result jsonResult = new Result(code, errorMsg);
|
||||
OutputStream os = null;
|
||||
try {
|
||||
os = httpServletResponse.getOutputStream();
|
||||
httpServletResponse.setCharacterEncoding("UTF-8");
|
||||
httpServletResponse.setStatus(401);
|
||||
os.write(new ObjectMapper().writeValueAsString(jsonResult).getBytes("UTF-8"));
|
||||
os.flush();
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验token是否正确
|
||||
*
|
||||
|
||||
@ -1,26 +1,30 @@
|
||||
package org.jeecg.common.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
|
||||
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.DataBaseConstant;
|
||||
import org.jeecg.common.util.filter.FileTypeFilter;
|
||||
import org.jeecg.common.util.oss.OssBootUtil;
|
||||
import org.jeecgframework.poi.util.PoiPublicUtil;
|
||||
import org.springframework.jdbc.datasource.DriverManagerDataSource;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import javax.sql.DataSource;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Slf4j
|
||||
public class CommonUtils {
|
||||
@ -207,6 +211,42 @@ public class CommonUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数据源key获取DataSourceProperty
|
||||
* @param sourceKey
|
||||
* @return
|
||||
*/
|
||||
public static DataSourceProperty getDataSourceProperty(String sourceKey){
|
||||
DynamicDataSourceProperties prop = SpringContextUtils.getApplicationContext().getBean(DynamicDataSourceProperties.class);
|
||||
Map<String, DataSourceProperty> map = prop.getDatasource();
|
||||
DataSourceProperty db = (DataSourceProperty)map.get(sourceKey);
|
||||
return db;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据sourceKey 获取数据源连接
|
||||
* @param sourceKey
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static Connection getDataSourceConnect(String sourceKey) throws SQLException {
|
||||
if (oConvertUtils.isEmpty(sourceKey)) {
|
||||
sourceKey = "master";
|
||||
}
|
||||
DynamicDataSourceProperties prop = SpringContextUtils.getApplicationContext().getBean(DynamicDataSourceProperties.class);
|
||||
Map<String, DataSourceProperty> map = prop.getDatasource();
|
||||
DataSourceProperty db = (DataSourceProperty)map.get(sourceKey);
|
||||
if(db==null){
|
||||
return null;
|
||||
}
|
||||
DriverManagerDataSource ds = new DriverManagerDataSource ();
|
||||
ds.setDriverClassName(db.getDriverClassName());
|
||||
ds.setUrl(db.getUrl());
|
||||
ds.setUsername(db.getUsername());
|
||||
ds.setPassword(db.getPassword());
|
||||
return ds.getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库类型
|
||||
* @param dataSource
|
||||
|
||||
@ -38,7 +38,7 @@ public class ImportExcelUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> importDateSave(List<Object> list, Class serviceClass,List<String> errorMessage,String errorFlag) {
|
||||
public static List<String> importDateSave(List<?> list, Class serviceClass, List<String> errorMessage, String errorFlag) {
|
||||
IService bean =(IService) SpringContextUtils.getBean(serviceClass);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
try {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package org.jeecg.common.util;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.jeecg.common.constant.ServiceNameConstants;
|
||||
import org.springframework.beans.BeansException;
|
||||
@ -38,6 +39,12 @@ public class SpringContextUtils implements ApplicationContextAware {
|
||||
public static HttpServletRequest getHttpServletRequest() {
|
||||
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||
}
|
||||
/**
|
||||
* 获取HttpServletResponse
|
||||
*/
|
||||
public static HttpServletResponse getHttpServletResponse() {
|
||||
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取项目根路径 basePath
|
||||
|
||||
@ -2,9 +2,9 @@ package org.jeecg.common.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.jeecg.common.api.CommonAPI;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.exception.JeecgBoot401Exception;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
|
||||
@ -40,27 +40,57 @@ public class TokenUtils {
|
||||
String token = getTokenByRequest(request);
|
||||
|
||||
if (StringUtils.isBlank(token)) {
|
||||
throw new AuthenticationException("Token不能为空!");
|
||||
throw new JeecgBoot401Exception("Token不能为空!");
|
||||
}
|
||||
|
||||
// 解密获得username,用于和数据库进行对比
|
||||
String username = JwtUtil.getUsername(token);
|
||||
if (username == null) {
|
||||
throw new AuthenticationException("Token非法无效!");
|
||||
throw new JeecgBoot401Exception("Token非法无效!");
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
LoginUser user = commonAPI.getUserByName(username);
|
||||
if (user == null) {
|
||||
throw new AuthenticationException("用户不存在!");
|
||||
throw new JeecgBoot401Exception("用户不存在!");
|
||||
}
|
||||
// 判断用户状态
|
||||
if (user.getStatus() != 1) {
|
||||
throw new AuthenticationException("账号已锁定,请联系管理员!");
|
||||
throw new JeecgBoot401Exception("账号已锁定,请联系管理员!");
|
||||
}
|
||||
// 校验token是否超时失效 & 或者账号密码是否错误
|
||||
if (!jwtTokenRefresh(token, username, user.getPassword(), redisUtil)) {
|
||||
throw new AuthenticationException("Token失效,请重新登录");
|
||||
throw new JeecgBoot401Exception("Token失效,请重新登录");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证Token
|
||||
*/
|
||||
public static boolean verifyToken(String token, CommonAPI commonAPI, RedisUtil redisUtil) {
|
||||
if (StringUtils.isBlank(token)) {
|
||||
throw new JeecgBoot401Exception("token不能为空!");
|
||||
}
|
||||
|
||||
// 解密获得username,用于和数据库进行对比
|
||||
String username = JwtUtil.getUsername(token);
|
||||
if (username == null) {
|
||||
throw new JeecgBoot401Exception("token非法无效!");
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
LoginUser user = commonAPI.getUserByName(username);
|
||||
if (user == null) {
|
||||
throw new JeecgBoot401Exception("用户不存在!");
|
||||
}
|
||||
// 判断用户状态
|
||||
if (user.getStatus() != 1) {
|
||||
throw new JeecgBoot401Exception("账号已被锁定,请联系管理员!");
|
||||
}
|
||||
// 校验token是否超时失效 & 或者账号密码是否错误
|
||||
if (!jwtTokenRefresh(token, username, user.getPassword(), redisUtil)) {
|
||||
throw new JeecgBoot401Exception(CommonConstant.TOKEN_IS_INVALID_MSG);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -95,34 +125,4 @@ public class TokenUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证Token
|
||||
*/
|
||||
public static boolean verifyToken(String token, CommonAPI commonAPI, RedisUtil redisUtil) {
|
||||
if (StringUtils.isBlank(token)) {
|
||||
throw new AuthenticationException("token不能为空!");
|
||||
}
|
||||
|
||||
// 解密获得username,用于和数据库进行对比
|
||||
String username = JwtUtil.getUsername(token);
|
||||
if (username == null) {
|
||||
throw new AuthenticationException("token非法无效!");
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
LoginUser user = commonAPI.getUserByName(username);
|
||||
if (user == null) {
|
||||
throw new AuthenticationException("用户不存在!");
|
||||
}
|
||||
// 判断用户状态
|
||||
if (user.getStatus() != 1) {
|
||||
throw new AuthenticationException("账号已被锁定,请联系管理员!");
|
||||
}
|
||||
// 校验token是否超时失效 & 或者账号密码是否错误
|
||||
if (!jwtTokenRefresh(token, username, user.getPassword(), redisUtil)) {
|
||||
throw new AuthenticationException("Token失效,请重新登录!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
package org.jeecg.common.util.dynamic.db;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import org.jeecg.common.constant.DataBaseConstant;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 数据库类型判断
|
||||
@ -8,6 +12,34 @@ import com.baomidou.mybatisplus.annotation.DbType;
|
||||
*/
|
||||
public class DbTypeUtils {
|
||||
|
||||
public static Map<String, String> dialectMap = new HashMap<String, String>();
|
||||
static{
|
||||
dialectMap.put("mysql", "org.hibernate.dialect.MySQL5InnoDBDialect");
|
||||
dialectMap.put("mariadb", "org.hibernate.dialect.MariaDBDialect");// 1 --
|
||||
dialectMap.put("oracle", "org.hibernate.dialect.OracleDialect");//1
|
||||
dialectMap.put("oracle12c", "org.hibernate.dialect.OracleDialect"); // TODO 没找到不确定
|
||||
dialectMap.put("db2", "org.hibernate.dialect.DB2390Dialect"); // 1xx
|
||||
dialectMap.put("h2", "org.hibernate.dialect.HSQLDialect");// H2数据库
|
||||
dialectMap.put("hsql", "org.hibernate.dialect.HSQLDialect");// HSQL数据库 1
|
||||
dialectMap.put("sqlite", "org.jeecg.modules.online.config.dialect.SQLiteDialect"); //SQLite数据库 应用平台mobile
|
||||
dialectMap.put("postgresql", "org.hibernate.dialect.PostgreSQLDialect"); //1 --
|
||||
dialectMap.put("sqlserver2005", "org.hibernate.dialect.SQLServer2005Dialect");
|
||||
dialectMap.put("sqlserver", "org.hibernate.dialect.SQLServerDialect"); //1
|
||||
dialectMap.put("dm", "org.hibernate.dialect.OracleDialect");//达梦数据库 [国产] 1--
|
||||
dialectMap.put("xugu", "org.hibernate.dialect.HSQLDialect"); //虚谷数据库
|
||||
dialectMap.put("kingbasees", "org.hibernate.dialect.PostgreSQLDialect"); //人大金仓 [国产] 1
|
||||
dialectMap.put("phoenix", "org.hibernate.dialect.HSQLDialect"); // Phoenix HBase数据库
|
||||
dialectMap.put("zenith", "org.hibernate.dialect.PostgreSQLDialect"); // Gauss 数据库
|
||||
dialectMap.put("clickhouse", "org.hibernate.dialect.MySQLDialect"); //阿里云PolarDB
|
||||
dialectMap.put("gbase", "org.hibernate.dialect.PostgreSQLDialect"); // 南大通用数据库 TODO 没找到不确定
|
||||
dialectMap.put("oscar", "org.hibernate.dialect.PostgreSQLDialect"); //神通数据库 [国产] TODO 没找到不确定
|
||||
dialectMap.put("sybase", "org.hibernate.dialect.SybaseDialect"); //Sybase ASE 数据库
|
||||
dialectMap.put("oceanbase", "org.hibernate.dialect.PostgreSQLDialect"); //OceanBase 数据库 TODO 没找到不确定
|
||||
dialectMap.put("Firebird", "org.hibernate.dialect.FirebirdDialect");
|
||||
dialectMap.put("highgo", "org.hibernate.dialect.HSQLDialect"); //瀚高数据库
|
||||
dialectMap.put("other", "org.hibernate.dialect.PostgreSQLDialect");
|
||||
}
|
||||
|
||||
public static boolean dbTypeIsMySQL(DbType dbType) {
|
||||
return dbTypeIf(dbType, DbType.MYSQL, DbType.MARIADB, DbType.CLICK_HOUSE, DbType.SQLITE);
|
||||
}
|
||||
@ -24,6 +56,37 @@ public class DbTypeUtils {
|
||||
return dbTypeIf(dbType, DbType.POSTGRE_SQL, DbType.KINGBASE_ES, DbType.GAUSS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据枚举类 获取数据库类型的字符串
|
||||
* @param dbType
|
||||
* @return
|
||||
*/
|
||||
public static String getDbTypeString(DbType dbType){
|
||||
if(DbType.DB2.equals(dbType)){
|
||||
return DataBaseConstant.DB_TYPE_DB2;
|
||||
}else if(DbType.HSQL.equals(dbType)){
|
||||
return DataBaseConstant.DB_TYPE_HSQL;
|
||||
}else if(dbTypeIsOracle(dbType)){
|
||||
return DataBaseConstant.DB_TYPE_ORACLE;
|
||||
}else if(dbTypeIsSQLServer(dbType)){
|
||||
return DataBaseConstant.DB_TYPE_SQLSERVER;
|
||||
}else if(dbTypeIsPostgre(dbType)){
|
||||
return DataBaseConstant.DB_TYPE_POSTGRESQL;
|
||||
}
|
||||
return DataBaseConstant.DB_TYPE_MYSQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据枚举类 获取数据库方言字符串
|
||||
* @param dbType
|
||||
* @return
|
||||
*/
|
||||
public static String getDbDialect(DbType dbType){
|
||||
return dialectMap.get(dbType.getDb());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断数据库类型
|
||||
*/
|
||||
|
||||
@ -111,6 +111,16 @@ public class DynamicDBUtil {
|
||||
return new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数据源获取NamedParameterJdbcTemplate
|
||||
* @param dbKey
|
||||
* @return
|
||||
*/
|
||||
private static NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(String dbKey) {
|
||||
DruidDataSource dataSource = getDbSourceByDbKey(dbKey);
|
||||
return new NamedParameterJdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the SQL statement in this <code>PreparedStatement</code> object,
|
||||
* which must be an SQL Data Manipulation Language (DML) statement, such as <code>INSERT</code>, <code>UPDATE</code> or
|
||||
@ -221,6 +231,31 @@ public class DynamicDBUtil {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数量
|
||||
* @param dbKey
|
||||
* @param sql
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, Object> queryCount(String dbKey, String sql, Map<String, Object> param){
|
||||
NamedParameterJdbcTemplate npJdbcTemplate = getNamedParameterJdbcTemplate(dbKey);
|
||||
return npJdbcTemplate.queryForMap(sql, param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询列表数据
|
||||
* @param dbKey
|
||||
* @param sql
|
||||
* @param param
|
||||
* @return
|
||||
*/
|
||||
public static List<Map<String, Object>> findListByNamedParam(final String dbKey, String sql, Map<String, Object> param) {
|
||||
NamedParameterJdbcTemplate npJdbcTemplate = getNamedParameterJdbcTemplate(dbKey);
|
||||
List<Map<String, Object>> list = npJdbcTemplate.queryForList(sql, param);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 支持miniDao语法操作的查询
|
||||
*
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
package org.jeecg.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
/**
|
||||
* 加载项目配置
|
||||
*/
|
||||
@Component("jeeccgBaseConfig")
|
||||
@ConfigurationProperties(prefix = "jeecg")
|
||||
public class JeeccgBaseConfig {
|
||||
/**
|
||||
* 是否启用安全模式
|
||||
*/
|
||||
private Boolean safeMode = false;
|
||||
|
||||
public Boolean getSafeMode() {
|
||||
return safeMode;
|
||||
}
|
||||
|
||||
public void setSafeMode(Boolean safeMode) {
|
||||
this.safeMode = safeMode;
|
||||
}
|
||||
}
|
||||
@ -35,6 +35,11 @@ public class MybatisPlusSaasConfig {
|
||||
|
||||
static {
|
||||
tenantTable.add("demo");
|
||||
|
||||
// //角色、菜单、部门
|
||||
// tenantTable.add("sys_role");
|
||||
// tenantTable.add("sys_permission");
|
||||
// tenantTable.add("sys_depart");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -118,8 +118,12 @@ public class ShiroConfig {
|
||||
filterChainDefinitionMap.put("/jmreport/**", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.js.map", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.css.map", "anon");
|
||||
//大屏模板例子
|
||||
filterChainDefinitionMap.put("/test/bigScreen/**", "anon");
|
||||
|
||||
//测试示例
|
||||
filterChainDefinitionMap.put("/test/bigScreen/**", "anon"); //大屏模板例子
|
||||
//filterChainDefinitionMap.put("/test/jeecgDemo/rabbitMqClientTest/**", "anon"); //MQ测试
|
||||
//filterChainDefinitionMap.put("/test/jeecgDemo/html", "anon"); //模板页面
|
||||
//filterChainDefinitionMap.put("/test/jeecgDemo/redis/**", "anon"); //redis测试
|
||||
|
||||
//websocket排除
|
||||
filterChainDefinitionMap.put("/websocket/**", "anon");//系统通知和公告
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package org.jeecg.config.shiro;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
@ -11,7 +10,6 @@ import org.apache.shiro.authz.SimpleAuthorizationInfo;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.jeecg.common.api.CommonAPI;
|
||||
import org.jeecg.common.constant.CacheConstant;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
@ -97,7 +95,14 @@ public class ShiroRealm extends AuthorizingRealm {
|
||||
throw new AuthenticationException("token为空!");
|
||||
}
|
||||
// 校验token有效性
|
||||
LoginUser loginUser = this.checkUserTokenIsEffect(token);
|
||||
LoginUser loginUser = null;
|
||||
try {
|
||||
loginUser = this.checkUserTokenIsEffect(token);
|
||||
} catch (AuthenticationException e) {
|
||||
JwtUtil.responseError(SpringContextUtils.getHttpServletResponse(),401,e.getMessage());
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return new SimpleAuthenticationInfo(loginUser, token, getName());
|
||||
}
|
||||
|
||||
@ -125,7 +130,7 @@ public class ShiroRealm extends AuthorizingRealm {
|
||||
}
|
||||
// 校验token是否超时失效 & 或者账号密码是否错误
|
||||
if (!jwtTokenRefresh(token, username, loginUser.getPassword())) {
|
||||
throw new AuthenticationException("Token失效,请重新登录!");
|
||||
throw new AuthenticationException(CommonConstant.TOKEN_IS_INVALID_MSG);
|
||||
}
|
||||
//update-begin-author:taoyan date:20210609 for:校验用户的tenant_id和前端传过来的是否一致
|
||||
String userTenantIds = loginUser.getRelTenantIds();
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
package org.jeecg.config.shiro.filters;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.config.mybatis.TenantContext;
|
||||
import org.jeecg.config.shiro.JwtToken;
|
||||
@ -48,7 +48,9 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
|
||||
executeLogin(request, response);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new AuthenticationException("Token失效,请重新登录", e);
|
||||
JwtUtil.responseError(response,401,CommonConstant.TOKEN_IS_INVALID_MSG);
|
||||
return false;
|
||||
//throw new AuthenticationException("Token失效,请重新登录", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<artifactId>jeecg-boot-base</artifactId>
|
||||
<version>2.4.6</version>
|
||||
<version>3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<description>公共模块</description>
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
package org.jeecg.common.config.mqtoken;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 存放token到上下文供队列调用feign使用
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>jeecg-boot-parent</artifactId>
|
||||
<groupId>org.jeecgframework.boot</groupId>
|
||||
<version>2.4.6</version>
|
||||
<version>3.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user