Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/db
# Conflicts: # yudao-server/src/main/resources/application-local.yaml
This commit is contained in:
commit
213a6aa0fc
@ -23,7 +23,7 @@ public class YudaoDataSourceAutoConfiguration {
|
||||
* 创建 DruidAdRemoveFilter 过滤器,过滤 common.js 的广告
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.datasource.druid.web-stat-filter.enabled", havingValue = "true")
|
||||
@ConditionalOnProperty(name = "spring.datasource.druid.stat-view-servlet.enabled", havingValue = "true")
|
||||
public FilterRegistrationBean<DruidAdRemoveFilter> druidAdRemoveFilterFilter(DruidStatProperties properties) {
|
||||
// 获取 druid web 监控页面的参数
|
||||
DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
|
||||
|
@ -5,7 +5,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortingField;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.enums.SqlConstants;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
@ -20,6 +22,7 @@ import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力
|
||||
@ -147,6 +150,11 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
||||
* @param entities 实体们
|
||||
*/
|
||||
default Boolean insertBatch(Collection<T> entities) {
|
||||
// 特殊:SQL Server 批量插入后,获取 id 会报错,因此通过循环处理
|
||||
if (Objects.equals(SqlConstants.DB_TYPE, DbType.SQL_SERVER)) {
|
||||
entities.forEach(this::insert);
|
||||
return CollUtil.isNotEmpty(entities);
|
||||
}
|
||||
return Db.saveBatch(entities);
|
||||
}
|
||||
|
||||
@ -157,6 +165,11 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
||||
* @param size 插入数量 Db.saveBatch 默认为 1000
|
||||
*/
|
||||
default Boolean insertBatch(Collection<T> entities, int size) {
|
||||
// 特殊:SQL Server 批量插入后,获取 id 会报错,因此通过循环处理
|
||||
if (Objects.equals(SqlConstants.DB_TYPE, DbType.SQL_SERVER)) {
|
||||
entities.forEach(this::insert);
|
||||
return CollUtil.isNotEmpty(entities);
|
||||
}
|
||||
return Db.saveBatch(entities, size);
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ public class ApiAccessLogInterceptor implements HandlerInterceptor {
|
||||
log.info("[preHandle][开始请求 URL({}) 无参数]", request.getRequestURI());
|
||||
} else {
|
||||
log.info("[preHandle][开始请求 URL({}) 参数({})]", request.getRequestURI(),
|
||||
StrUtil.nullToDefault(requestBody, queryString.toString()));
|
||||
StrUtil.blankToDefault(requestBody, queryString.toString()));
|
||||
}
|
||||
// 计时
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
|
@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
@ -32,6 +33,7 @@ import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*;
|
||||
|
||||
@ -45,6 +47,11 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* 忽略的 ServiceException 错误提示,避免打印过多 logger
|
||||
*/
|
||||
public static final Set<String> IGNORE_ERROR_MESSAGES = SetUtils.asSet("无效的刷新令牌");
|
||||
|
||||
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
||||
private final String applicationName;
|
||||
|
||||
@ -199,7 +206,10 @@ public class GlobalExceptionHandler {
|
||||
*/
|
||||
@ExceptionHandler(value = ServiceException.class)
|
||||
public CommonResult<?> serviceExceptionHandler(ServiceException ex) {
|
||||
log.info("[serviceExceptionHandler]", ex);
|
||||
if (!IGNORE_ERROR_MESSAGES.contains(ex.getMessage())) {
|
||||
// 不包含的时候,才进行打印,避免 ex 堆栈过多
|
||||
log.info("[serviceExceptionHandler]", ex);
|
||||
}
|
||||
return CommonResult.error(ex.getCode(), ex.getMessage());
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ public class CrmBusinessSaveReqVO {
|
||||
private Long contactId; // 使用场景,在【联系人详情】添加商机时,如果需要关联两者,需要传递 contactId 字段
|
||||
|
||||
@Schema(description = "产品列表")
|
||||
private List<BusinessProduct> businessProducts;
|
||||
private List<BusinessProduct> products;
|
||||
|
||||
@Schema(description = "产品列表")
|
||||
@Data
|
||||
|
@ -89,7 +89,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||
success = CRM_BUSINESS_CREATE_SUCCESS)
|
||||
public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) {
|
||||
// 1.1 校验产品项的有效性
|
||||
List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(createReqVO.getBusinessProducts());
|
||||
List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(createReqVO.getProducts());
|
||||
// 1.2 校验关联字段
|
||||
validateRelationDataExists(createReqVO);
|
||||
|
||||
@ -130,7 +130,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
|
||||
// 1.1 校验存在
|
||||
CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId());
|
||||
// 1.2 校验产品项的有效性
|
||||
List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getBusinessProducts());
|
||||
List<CrmBusinessProductDO> businessProducts = validateBusinessProducts(updateReqVO.getProducts());
|
||||
// 1.3 校验关联字段
|
||||
validateRelationDataExists(updateReqVO);
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
### 请求 /infra/job/sync 接口 => 成功
|
||||
POST {{baseUrl}}/infra/job/sync
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenentId}}
|
||||
Authorization: Bearer {{token}}
|
@ -90,6 +90,14 @@ public class JobController {
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/sync")
|
||||
@Operation(summary = "同步定时任务")
|
||||
@PreAuthorize("@ss.hasPermission('infra:job:create')")
|
||||
public CommonResult<Boolean> syncJob() throws SchedulerException {
|
||||
jobService.syncJob();
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得定时任务")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
|
@ -45,6 +45,13 @@ public interface JobService {
|
||||
*/
|
||||
void triggerJob(Long id) throws SchedulerException;
|
||||
|
||||
/**
|
||||
* 同步定时任务
|
||||
*
|
||||
* 目的:自己存储的 Job 信息,强制同步到 Quartz 中
|
||||
*/
|
||||
void syncJob() throws SchedulerException;
|
||||
|
||||
/**
|
||||
* 删除定时任务
|
||||
*
|
||||
|
@ -12,11 +12,15 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO;
|
||||
import cn.iocoder.yudao.module.infra.dal.mysql.job.JobMapper;
|
||||
import cn.iocoder.yudao.module.infra.enums.job.JobStatusEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.containsAny;
|
||||
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
||||
@ -28,6 +32,7 @@ import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class JobServiceImpl implements JobService {
|
||||
|
||||
@Resource
|
||||
@ -129,6 +134,26 @@ public class JobServiceImpl implements JobService {
|
||||
schedulerManager.triggerJob(job.getId(), job.getHandlerName(), job.getHandlerParam());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void syncJob() throws SchedulerException {
|
||||
// 1. 查询 Job 配置
|
||||
List<JobDO> jobList = jobMapper.selectList();
|
||||
|
||||
// 2. 遍历处理
|
||||
for (JobDO job : jobList) {
|
||||
// 2.1 先删除,再创建
|
||||
schedulerManager.deleteJob(job.getHandlerName());
|
||||
schedulerManager.addJob(job.getId(), job.getHandlerName(), job.getHandlerParam(), job.getCronExpression(),
|
||||
job.getRetryCount(), job.getRetryInterval());
|
||||
// 2.2 如果 status 为暂停,则需要暂停
|
||||
if (Objects.equals(job.getStatus(), JobStatusEnum.STOP.getStatus())) {
|
||||
schedulerManager.pauseJob(job.getHandlerName());
|
||||
}
|
||||
log.info("[syncJob][id({}) handlerName({}) 同步完成]", job.getId(), job.getHandlerName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteJob(Long id) throws SchedulerException {
|
||||
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.report.framework.jmreport.config;
|
||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||
import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
|
||||
import cn.iocoder.yudao.module.report.framework.jmreport.core.service.JmReportTokenServiceImpl;
|
||||
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
|
||||
import org.jeecg.modules.jmreport.api.JmReportTokenServiceI;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
@ -19,8 +20,10 @@ public class JmReportConfiguration {
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
||||
public JmReportTokenServiceI jmReportTokenService(OAuth2TokenApi oAuth2TokenApi, SecurityProperties securityProperties) {
|
||||
return new JmReportTokenServiceImpl(oAuth2TokenApi, securityProperties);
|
||||
public JmReportTokenServiceI jmReportTokenService(OAuth2TokenApi oAuth2TokenApi,
|
||||
PermissionApi permissionApi,
|
||||
SecurityProperties securityProperties) {
|
||||
return new JmReportTokenServiceImpl(oAuth2TokenApi, permissionApi, securityProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,12 +11,13 @@ import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||
import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
|
||||
import cn.iocoder.yudao.module.system.api.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
|
||||
import cn.iocoder.yudao.module.system.api.permission.RoleApi;
|
||||
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
|
||||
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jeecg.modules.jmreport.api.JmReportTokenServiceI;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@ -37,6 +38,7 @@ public class JmReportTokenServiceImpl implements JmReportTokenServiceI {
|
||||
private static final String AUTHORIZATION_FORMAT = SecurityFrameworkUtils.AUTHORIZATION_BEARER + " %s";
|
||||
|
||||
private final OAuth2TokenApi oauth2TokenApi;
|
||||
private final PermissionApi permissionApi;
|
||||
|
||||
private final SecurityProperties securityProperties;
|
||||
|
||||
@ -130,9 +132,12 @@ public class JmReportTokenServiceImpl implements JmReportTokenServiceI {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getRoles(String s) {
|
||||
// 暂时不用实现,因为不用 JmReport 的角色
|
||||
return null;
|
||||
public String[] getRoles(String token) {
|
||||
// 参见文档 https://help.jeecg.com/jimureport/prodSafe.html 文档
|
||||
// 适配:如果是本系统的管理员,则转换成 jimu 报表的管理员
|
||||
Long userId = SecurityFrameworkUtils.getLoginUserId();
|
||||
return permissionApi.hasAnyRoles(userId, RoleCodeEnum.SUPER_ADMIN.getCode())
|
||||
? new String[]{"admin"} : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -40,17 +40,14 @@ spring:
|
||||
primary: master
|
||||
datasource:
|
||||
master:
|
||||
name: ruoyi-vue-pro
|
||||
url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&serverTimezone=CTT&allowPublicKeyRetrieval=true
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
|
||||
username: root
|
||||
password: 3WLiVUBEwTbvAfsh
|
||||
password: 123456
|
||||
slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改
|
||||
name: ruoyi-vue-pro
|
||||
url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&serverTimezone=CTT&allowPublicKeyRetrieval=true
|
||||
driver-class-name: com.mysql.jdbc.Driver
|
||||
lazy: true # 开启懒加载,保证启动速度
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
|
||||
username: root
|
||||
password: 3WLiVUBEwTbvAfsh
|
||||
password: 123456
|
||||
|
||||
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
|
||||
data:
|
||||
|
@ -39,39 +39,30 @@ spring:
|
||||
time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
|
||||
min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
|
||||
max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
|
||||
# validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效, 不同数据库有所不同, 建议mysql以外的先注释掉
|
||||
validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
|
||||
test-while-idle: true
|
||||
test-on-borrow: false
|
||||
test-on-return: false
|
||||
primary: master
|
||||
datasource:
|
||||
master:
|
||||
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
|
||||
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
|
||||
# url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true # PostgreSQL 连接的示例
|
||||
# url: jdbc:oracle:thin:@//127.0.0.1:1521/XEPDB1 # Oracle 连接的示例
|
||||
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例
|
||||
url: jdbc:dm://localhost:5236 # DM 连接的示例
|
||||
# username: root
|
||||
# password: 123456
|
||||
# username: sa # SQLServer 连接的示例
|
||||
# password: Yudao@2024 # SQLServer 连接的示例
|
||||
username: SYSDBA # DM 连接的示例
|
||||
password: SYSDBA001 # DM 连接的示例
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
|
||||
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例
|
||||
# url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例
|
||||
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
|
||||
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例
|
||||
# url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例
|
||||
username: root
|
||||
password: 123456
|
||||
# username: sa # SQL Server 连接的示例
|
||||
# password: Yudao@2024 # SQL Server 连接的示例
|
||||
# username: SYSDBA # DM 连接的示例
|
||||
# password: SYSDBA001 # DM 连接的示例
|
||||
slave: # 模拟从库,可根据自己需要修改
|
||||
lazy: true # 开启懒加载,保证启动速度
|
||||
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
|
||||
# url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
|
||||
# url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true # PostgreSQL 连接的示例
|
||||
# url: jdbc:oracle:thin:@//127.0.0.1:1521/XEPDB1 # Oracle 连接的示例
|
||||
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro # SQLServer 连接的示例
|
||||
# url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例
|
||||
# username: root
|
||||
# password: 123456
|
||||
username: sa # SQLServer 连接的示例
|
||||
password: Yudao@2024 # SQLServer 连接的示例
|
||||
# username: SYSDBA # DM 连接的示例
|
||||
# password: SYSDBA # DM 连接的示例
|
||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||
username: root
|
||||
password: 123456
|
||||
|
||||
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
|
||||
data:
|
||||
|
Loading…
Reference in New Issue
Block a user