infra:初始化 infra 服务的启动,还有一些报错,正在处理

This commit is contained in:
YunaiV 2022-06-08 10:16:04 +08:00
parent 213ec8bd72
commit 6d5d72999b
58 changed files with 793 additions and 2778 deletions

View File

@ -11,8 +11,8 @@
"appTenentId": "1"
},
"gateway": {
"baseUrl": "http://127.0.0.1:8888/admin-api",
"systemBaseUrl": "http://127.0.0.1:8888/admin-api",
"baseUrl": "http://127.0.0.1:48080/admin-api",
"systemBaseUrl": "http://127.0.0.1:48080/admin-api",
"token": "test1",
"adminTenentId": "1",

View File

@ -50,6 +50,21 @@
<artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行 Token 的校验 -->
<version>${revision}</version>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.context.SecurityContextHolder;
@ -32,6 +33,7 @@ import javax.annotation.Resource;
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SecurityProperties.class)
@EnableFeignClients(clients = OAuth2TokenApi.class)
public class YudaoSecurityAutoConfiguration {
@Resource

View File

@ -1,2 +0,0 @@
server:
port: 8888

View File

@ -4,3 +4,6 @@ spring:
profiles:
active: local
server:
port: 48080

View File

@ -19,6 +19,13 @@
</description>
<dependencies>
<!-- Spring Cloud 基础 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- 依赖服务 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-system-api</artifactId>
@ -57,14 +64,35 @@
<artifactId>yudao-spring-boot-starter-redis</artifactId>
</dependency>
<!-- Config 配置中心相关 -->
<!-- Job 定时任务相关 -->
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-job</artifactId>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<!-- Registry 注册中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Job 定时任务相关 TODO 芋艿:暂时去掉 -->
<!-- <dependency>-->
<!-- <groupId>cn.iocoder.cloud</groupId>-->
<!-- <artifactId>yudao-spring-boot-starter-job</artifactId>-->
<!-- </dependency>-->
<!-- 消息队列相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>

View File

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.infra;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
// TODO 芋艿修改启动文档的地址
/**
* 项目的启动类
*
* 如果你碰到启动的问题请认真阅读 https://doc.iocoder.cn/quick-start/ 文章
* 如果你碰到启动的问题请认真阅读 https://doc.iocoder.cn/quick-start/ 文章
* 如果你碰到启动的问题请认真阅读 https://doc.iocoder.cn/quick-start/ 文章
*
* @author 芋道源码
*/
@SpringBootApplication
public class InfraServerApplication {
public static void main(String[] args) {
// 如果你碰到启动的问题请认真阅读 https://doc.iocoder.cn/quick-start/ 文章
// 如果你碰到启动的问题请认真阅读 https://doc.iocoder.cn/quick-start/ 文章
// 如果你碰到启动的问题请认真阅读 https://doc.iocoder.cn/quick-start/ 文章
SpringApplication.run(InfraServerApplication.class, args);
// 如果你碰到启动的问题请认真阅读 https://doc.iocoder.cn/quick-start/ 文章
// 如果你碰到启动的问题请认真阅读 https://doc.iocoder.cn/quick-start/ 文章
// 如果你碰到启动的问题请认真阅读 https://doc.iocoder.cn/quick-start/ 文章
}
}

View File

@ -1,105 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.config;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.*;
import cn.iocoder.yudao.module.infra.convert.config.ConfigConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;
import cn.iocoder.yudao.module.infra.service.config.ConfigService;
import cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Api(tags = "管理后台 - 参数配置")
@RestController
@RequestMapping("/infra/config")
@Validated
public class ConfigController {
@Resource
private ConfigService configService;
@PostMapping("/create")
@ApiOperation("创建参数配置")
@PreAuthorize("@ss.hasPermission('infra:config:create')")
public CommonResult<Long> createConfig(@Valid @RequestBody ConfigCreateReqVO reqVO) {
return success(configService.createConfig(reqVO));
}
@PutMapping("/update")
@ApiOperation("修改参数配置")
@PreAuthorize("@ss.hasPermission('infra:config:update')")
public CommonResult<Boolean> updateConfig(@Valid @RequestBody ConfigUpdateReqVO reqVO) {
configService.updateConfig(reqVO);
return success(true);
}
@DeleteMapping("/delete")
@ApiOperation("删除参数配置")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('infra:config:delete')")
public CommonResult<Boolean> deleteConfig(@RequestParam("id") Long id) {
configService.deleteConfig(id);
return success(true);
}
@GetMapping(value = "/get")
@ApiOperation("获得参数配置")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('infra:config:query')")
public CommonResult<ConfigRespVO> getConfig(@RequestParam("id") Long id) {
return success(ConfigConvert.INSTANCE.convert(configService.getConfig(id)));
}
@GetMapping(value = "/get-value-by-key")
@ApiOperation(value = "根据参数键名查询参数值", notes = "不可见的配置,不允许返回给前端")
@ApiImplicitParam(name = "key", value = "参数键", required = true, example = "yunai.biz.username", dataTypeClass = String.class)
public CommonResult<String> getConfigKey(@RequestParam("key") String key) {
ConfigDO config = configService.getConfigByKey(key);
if (config == null) {
return null;
}
if (config.getVisible()) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.CONFIG_GET_VALUE_ERROR_IF_VISIBLE);
}
return success(config.getValue());
}
@GetMapping("/page")
@ApiOperation("获取参数配置分页")
@PreAuthorize("@ss.hasPermission('infra:config:query')")
public CommonResult<PageResult<ConfigRespVO>> getConfigPage(@Valid ConfigPageReqVO reqVO) {
PageResult<ConfigDO> page = configService.getConfigPage(reqVO);
return success(ConfigConvert.INSTANCE.convertPage(page));
}
@GetMapping("/export")
@ApiOperation("导出参数配置")
@PreAuthorize("@ss.hasPermission('infra:config:export')")
@OperateLog(type = EXPORT)
public void exportSysConfig(@Valid ConfigExportReqVO reqVO,
HttpServletResponse response) throws IOException {
List<ConfigDO> list = configService.getConfigList(reqVO);
// 拼接数据
List<ConfigExcelVO> datas = ConfigConvert.INSTANCE.convertList(list);
// 输出
ExcelUtils.write(response, "参数配置.xls", "数据", ConfigExcelVO.class, datas);
}
}

View File

@ -1,40 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.config.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* 参数配置 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class ConfigBaseVO {
@ApiModelProperty(value = "参数分组", required = true, example = "biz")
@NotEmpty(message = "参数分组不能为空")
@Size(max = 50, message = "参数名称不能超过50个字符")
private String category;
@ApiModelProperty(value = "参数名称", required = true, example = "数据库名")
@NotBlank(message = "参数名称不能为空")
@Size(max = 100, message = "参数名称不能超过100个字符")
private String name;
@ApiModelProperty(value = "参数键值", required = true, example = "1024")
@NotBlank(message = "参数键值不能为空")
@Size(max = 500, message = "参数键值长度不能超过500个字符")
private String value;
@ApiModelProperty(value = "是否敏感", required = true, example = "true")
@NotNull(message = "是否敏感不能为空")
private Boolean visible;
@ApiModelProperty(value = "备注", example = "备注一下很帅气!")
private String remark;
}

View File

@ -1,21 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.config.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
@ApiModel("管理后台 - 参数配置创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class ConfigCreateReqVO extends ConfigBaseVO {
@ApiModelProperty(value = "参数键名", required = true, example = "yunai.db.username")
@NotBlank(message = "参数键名长度不能为空")
@Size(max = 100, message = "参数键名长度不能超过100个字符")
private String key;
}

View File

@ -1,46 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.config.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.util.Date;
/**
* 参数配置 Excel 导出响应 VO
*/
@Data
public class ConfigExcelVO {
@ExcelProperty("参数配置序号")
private Long id;
@ExcelProperty("参数键名")
private String key;
@ExcelProperty("参数分组")
private String group;
@ExcelProperty("参数名称")
private String name;
@ExcelProperty("参数键值")
private String value;
@ExcelProperty(value = "参数类型", converter = DictConvert.class)
@DictFormat(DictTypeConstants.CONFIG_TYPE)
private Integer type;
@ExcelProperty(value = "是否敏感", converter = DictConvert.class)
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
private Boolean sensitive;
@ExcelProperty("备注")
private String remark;
@ExcelProperty("创建时间")
private Date createTime;
}

View File

@ -1,33 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.config.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("管理后台 - 参数配置导出 Request VO")
@Data
public class ConfigExportReqVO {
@ApiModelProperty(value = "参数名称", example = "模糊匹配")
private String name;
@ApiModelProperty(value = "参数键名", example = "yunai.db.username", notes = "模糊匹配")
private String key;
@ApiModelProperty(value = "参数类型", example = "1", notes = "参见 SysConfigTypeEnum 枚举")
private Integer type;
@ApiModelProperty(value = "开始时间", example = "2020-10-24 00:00:00")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date beginTime;
@ApiModelProperty(value = "结束时间", example = "2020-10-24 23:59:59")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date endTime;
}

View File

@ -1,38 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.config.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("管理后台 - 参数配置分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ConfigPageReqVO extends PageParam {
@ApiModelProperty(value = "数据源名称", example = "模糊匹配")
private String name;
@ApiModelProperty(value = "参数键名", example = "yunai.db.username", notes = "模糊匹配")
private String key;
@ApiModelProperty(value = "参数类型", example = "1", notes = "参见 SysConfigTypeEnum 枚举")
private Integer type;
@ApiModelProperty(value = "开始时间", example = "2020-10-24 00:00:00")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date beginTime;
@ApiModelProperty(value = "结束时间", example = "2020-10-24 23:59:59")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date endTime;
}

View File

@ -1,31 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.config.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.Date;
@ApiModel("管理后台 - 参数配置信息 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class ConfigRespVO extends ConfigBaseVO {
@ApiModelProperty(value = "参数配置序号", required = true, example = "1024")
private Long id;
@ApiModelProperty(value = "参数键名", required = true, example = "yunai.db.username")
@NotBlank(message = "参数键名长度不能为空")
@Size(max = 100, message = "参数键名长度不能超过100个字符")
private String key;
@ApiModelProperty(value = "参数类型", required = true, example = "1", notes = "参见 SysConfigTypeEnum 枚举")
private Integer type;
@ApiModelProperty(value = "创建时间", required = true, example = "时间戳格式")
private Date createTime;
}

View File

@ -1,21 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.config.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@ApiModel("管理后台 - 参数配置创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ConfigUpdateReqVO extends ConfigBaseVO {
@ApiModelProperty(value = "参数配置序号", required = true, example = "1024")
@NotNull(message = "参数配置编号不能为空")
private Long id;
}

View File

@ -1,145 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.framework.quartz.core.util.CronUtils;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.*;
import cn.iocoder.yudao.module.infra.convert.job.JobConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO;
import cn.iocoder.yudao.module.infra.service.job.JobService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.quartz.SchedulerException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Api(tags = "管理后台 - 定时任务")
@RestController
@RequestMapping("/infra/job")
@Validated
public class JobController {
@Resource
private JobService jobService;
@PostMapping("/create")
@ApiOperation("创建定时任务")
@PreAuthorize("@ss.hasPermission('infra:job:create')")
public CommonResult<Long> createJob(@Valid @RequestBody JobCreateReqVO createReqVO)
throws SchedulerException {
return success(jobService.createJob(createReqVO));
}
@PutMapping("/update")
@ApiOperation("更新定时任务")
@PreAuthorize("@ss.hasPermission('infra:job:update')")
public CommonResult<Boolean> updateJob(@Valid @RequestBody JobUpdateReqVO updateReqVO)
throws SchedulerException {
jobService.updateJob(updateReqVO);
return success(true);
}
@PutMapping("/update-status")
@ApiOperation("更新定时任务的状态")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class),
@ApiImplicitParam(name = "status", value = "状态", required = true, example = "1", dataTypeClass = Integer.class),
})
@PreAuthorize("@ss.hasPermission('infra:job:update')")
public CommonResult<Boolean> updateJobStatus(@RequestParam(value = "id") Long id, @RequestParam("status") Integer status)
throws SchedulerException {
jobService.updateJobStatus(id, status);
return success(true);
}
@DeleteMapping("/delete")
@ApiOperation("删除定时任务")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('infra:job:delete')")
public CommonResult<Boolean> deleteJob(@RequestParam("id") Long id)
throws SchedulerException {
jobService.deleteJob(id);
return success(true);
}
@PutMapping("/trigger")
@ApiOperation("触发定时任务")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('infra:job:trigger')")
public CommonResult<Boolean> triggerJob(@RequestParam("id") Long id) throws SchedulerException {
jobService.triggerJob(id);
return success(true);
}
@GetMapping("/get")
@ApiOperation("获得定时任务")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<JobRespVO> getJob(@RequestParam("id") Long id) {
JobDO job = jobService.getJob(id);
return success(JobConvert.INSTANCE.convert(job));
}
@GetMapping("/list")
@ApiOperation("获得定时任务列表")
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, dataTypeClass = List.class)
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<List<JobRespVO>> getJobList(@RequestParam("ids") Collection<Long> ids) {
List<JobDO> list = jobService.getJobList(ids);
return success(JobConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@ApiOperation("获得定时任务分页")
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<PageResult<JobRespVO>> getJobPage(@Valid JobPageReqVO pageVO) {
PageResult<JobDO> pageResult = jobService.getJobPage(pageVO);
return success(JobConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/export-excel")
@ApiOperation("导出定时任务 Excel")
@PreAuthorize("@ss.hasPermission('infra:job:export')")
@OperateLog(type = EXPORT)
public void exportJobExcel(@Valid JobExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<JobDO> list = jobService.getJobList(exportReqVO);
// 导出 Excel
List<JobExcelVO> datas = JobConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "定时任务.xls", "数据", JobExcelVO.class, datas);
}
@GetMapping("/get_next_times")
@ApiOperation("获得定时任务的下 n 次执行时间")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class),
@ApiImplicitParam(name = "count", value = "数量", example = "5", dataTypeClass = Long.class)
})
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<List<Date>> getJobNextTimes(@RequestParam("id") Long id,
@RequestParam(value = "count", required = false, defaultValue = "5") Integer count) {
JobDO job = jobService.getJob(id);
if (job == null) {
return success(Collections.emptyList());
}
return success(CronUtils.getNextTimes(job.getCronExpression(), count));
}
}

View File

@ -1,81 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogExcelVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogPageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogRespVO;
import cn.iocoder.yudao.module.infra.convert.job.JobLogConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobLogDO;
import cn.iocoder.yudao.module.infra.service.job.JobLogService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Api(tags = "管理后台 - 定时任务日志")
@RestController
@RequestMapping("/infra/job-log")
@Validated
public class JobLogController {
@Resource
private JobLogService jobLogService;
@GetMapping("/get")
@ApiOperation("获得定时任务日志")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<JobLogRespVO> getJobLog(@RequestParam("id") Long id) {
JobLogDO jobLog = jobLogService.getJobLog(id);
return success(JobLogConvert.INSTANCE.convert(jobLog));
}
@GetMapping("/list")
@ApiOperation("获得定时任务日志列表")
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<List<JobLogRespVO>> getJobLogList(@RequestParam("ids") Collection<Long> ids) {
List<JobLogDO> list = jobLogService.getJobLogList(ids);
return success(JobLogConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@ApiOperation("获得定时任务日志分页")
@PreAuthorize("@ss.hasPermission('infra:job:query')")
public CommonResult<PageResult<JobLogRespVO>> getJobLogPage(@Valid JobLogPageReqVO pageVO) {
PageResult<JobLogDO> pageResult = jobLogService.getJobLogPage(pageVO);
return success(JobLogConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/export-excel")
@ApiOperation("导出定时任务日志 Excel")
@PreAuthorize("@ss.hasPermission('infra:job:export')")
@OperateLog(type = EXPORT)
public void exportJobLogExcel(@Valid JobLogExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<JobLogDO> list = jobLogService.getJobLogList(exportReqVO);
// 导出 Excel
List<JobLogExcelVO> datas = JobLogConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "任务日志.xls", "数据", JobLogExcelVO.class, datas);
}
}

View File

@ -1,37 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job.vo.job;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* 定时任务 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class JobBaseVO {
@ApiModelProperty(value = "任务名称", required = true, example = "测试任务")
@NotNull(message = "任务名称不能为空")
private String name;
@ApiModelProperty(value = "处理器的参数", example = "yudao")
private String handlerParam;
@ApiModelProperty(value = "CRON 表达式", required = true, example = "0/10 * * * * ? *")
@NotNull(message = "CRON 表达式不能为空")
private String cronExpression;
@ApiModelProperty(value = "重试次数", required = true, example = "3")
@NotNull(message = "重试次数不能为空")
private Integer retryCount;
@ApiModelProperty(value = "重试间隔", required = true, example = "1000")
@NotNull(message = "重试间隔不能为空")
private Integer retryInterval;
@ApiModelProperty(value = "监控超时时间", example = "1000")
private Integer monitorTimeout;
}

View File

@ -1,21 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job.vo.job;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@ApiModel("管理后台 - 定时任务创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class JobCreateReqVO extends JobBaseVO {
@ApiModelProperty(value = "处理器的名字", required = true, example = "sysUserSessionTimeoutJob")
@NotNull(message = "处理器的名字不能为空")
private String handlerName;
}

View File

@ -1,56 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job.vo.job;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.util.Date;
/**
* 定时任务 Excel VO
*
* @author 芋道源码
*/
@Data
public class JobExcelVO {
@ExcelProperty("任务编号")
private Long id;
@ExcelProperty("任务名称")
private String name;
@ExcelProperty(value = "任务状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.JOB_STATUS)
private Integer status;
@ExcelProperty("处理器的名字")
private String handlerName;
@ExcelProperty("处理器的参数")
private String handlerParam;
@ExcelProperty("CRON 表达式")
private String cronExpression;
@ExcelProperty("最后一次执行的开始时间")
private Date executeBeginTime;
@ExcelProperty("最后一次执行的结束时间")
private Date executeEndTime;
@ExcelProperty("上一次触发时间")
private Date firePrevTime;
@ExcelProperty("下一次触发时间")
private Date fireNextTime;
@ExcelProperty("监控超时时间")
private Integer monitorTimeout;
@ExcelProperty("创建时间")
private Date createTime;
}

View File

@ -1,20 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job.vo.job;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ApiModel(value = "管理后台 - 定时任务 Excel 导出 Request VO", description = "参数和 JobPageReqVO 是一致的")
@Data
public class JobExportReqVO {
@ApiModelProperty(value = "任务名称", example = "测试任务", notes = "模糊匹配")
private String name;
@ApiModelProperty(value = "任务状态", example = "1", notes = "参见 JobStatusEnum 枚举")
private Integer status;
@ApiModelProperty(value = "处理器的名字", example = "UserSessionTimeoutJob", notes = "模糊匹配")
private String handlerName;
}

View File

@ -1,25 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job.vo.job;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ApiModel("管理后台 - 定时任务分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class JobPageReqVO extends PageParam {
@ApiModelProperty(value = "任务名称", example = "测试任务", notes = "模糊匹配")
private String name;
@ApiModelProperty(value = "任务状态", example = "1", notes = "参见 JobStatusEnum 枚举")
private Integer status;
@ApiModelProperty(value = "处理器的名字", example = "sysUserSessionTimeoutJob", notes = "模糊匹配")
private String handlerName;
}

View File

@ -1,31 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job.vo.job;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
import java.util.Date;
@ApiModel("管理后台 - 定时任务 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class JobRespVO extends JobBaseVO {
@ApiModelProperty(value = "任务编号", required = true, example = "1024")
private Long id;
@ApiModelProperty(value = "任务状态", required = true, example = "1")
private Integer status;
@ApiModelProperty(value = "处理器的名字", required = true, example = "sysUserSessionTimeoutJob")
@NotNull(message = "处理器的名字不能为空")
private String handlerName;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@ -1,21 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job.vo.job;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@ApiModel("管理后台 - 定时任务更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class JobUpdateReqVO extends JobBaseVO {
@ApiModelProperty(value = "任务编号", required = true, example = "1024")
@NotNull(message = "任务编号不能为空")
private Long id;
}

View File

@ -1,53 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job.vo.log;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* 定时任务日志 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class JobLogBaseVO {
@ApiModelProperty(value = "任务编号", required = true, example = "1024")
@NotNull(message = "任务编号不能为空")
private Long jobId;
@ApiModelProperty(value = "处理器的名字", required = true, example = "sysUserSessionTimeoutJob")
@NotNull(message = "处理器的名字不能为空")
private String handlerName;
@ApiModelProperty(value = "处理器的参数", example = "yudao")
private String handlerParam;
@ApiModelProperty(value = "第几次执行", required = true, example = "1")
@NotNull(message = "第几次执行不能为空")
private Integer executeIndex;
@ApiModelProperty(value = "开始执行时间", required = true)
@NotNull(message = "开始执行时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date beginTime;
@ApiModelProperty(value = "结束执行时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date endTime;
@ApiModelProperty(value = "执行时长", example = "123")
private Integer duration;
@ApiModelProperty(value = "任务状态", required = true, example = "1", notes = "参见 JobLogStatusEnum 枚举")
@NotNull(message = "任务状态不能为空")
private Integer status;
@ApiModelProperty(value = "结果数据", example = "执行成功")
private String result;
}

View File

@ -1,53 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job.vo.log;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.infra.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.util.Date;
/**
* 定时任务 Excel VO
*
* @author 芋艿
*/
@Data
public class JobLogExcelVO {
@ExcelProperty("日志编号")
private Long id;
@ExcelProperty("任务编号")
private Long jobId;
@ExcelProperty("处理器的名字")
private String handlerName;
@ExcelProperty("处理器的参数")
private String handlerParam;
@ExcelProperty("第几次执行")
private Integer executeIndex;
@ExcelProperty("开始执行时间")
private Date beginTime;
@ExcelProperty("结束执行时间")
private Date endTime;
@ExcelProperty("执行时长")
private Integer duration;
@ExcelProperty(value = "任务状态", converter = DictConvert.class)
@DictFormat(DictTypeConstants.JOB_STATUS)
private Integer status;
@ExcelProperty("结果数据")
private String result;
@ExcelProperty("创建时间")
private Date createTime;
}

View File

@ -1,33 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job.vo.log;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel(value = "管理后台 - 定时任务 Excel 导出 Request VO", description = "参数和 JobLogPageReqVO 是一致的")
@Data
public class JobLogExportReqVO {
@ApiModelProperty(value = "任务编号", example = "10")
private Long jobId;
@ApiModelProperty(value = "处理器的名字", notes = "模糊匹配")
private String handlerName;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "开始执行时间")
private Date beginTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "结束执行时间")
private Date endTime;
@ApiModelProperty(value = "任务状态", notes = "参见 JobLogStatusEnum 枚举")
private Integer status;
}

View File

@ -1,38 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job.vo.log;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("管理后台 - 定时任务日志分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class JobLogPageReqVO extends PageParam {
@ApiModelProperty(value = "任务编号", example = "10")
private Long jobId;
@ApiModelProperty(value = "处理器的名字", notes = "模糊匹配")
private String handlerName;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "开始执行时间")
private Date beginTime;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ApiModelProperty(value = "结束执行时间")
private Date endTime;
@ApiModelProperty(value = "任务状态", notes = "参见 JobLogStatusEnum 枚举")
private Integer status;
}

View File

@ -1,23 +0,0 @@
package cn.iocoder.yudao.module.infra.controller.admin.job.vo.log;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.util.Date;
@ApiModel("管理后台 - 定时任务日志 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class JobLogRespVO extends JobLogBaseVO {
@ApiModelProperty(value = "日志编号", required = true, example = "1024")
private Long id;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@ -1,33 +0,0 @@
package cn.iocoder.yudao.module.infra.convert.config;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigExcelVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigUpdateReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface ConfigConvert {
ConfigConvert INSTANCE = Mappers.getMapper(ConfigConvert.class);
PageResult<ConfigRespVO> convertPage(PageResult<ConfigDO> page);
@Mapping(source = "configKey", target = "key")
ConfigRespVO convert(ConfigDO bean);
@Mapping(source = "key", target = "configKey")
ConfigDO convert(ConfigCreateReqVO bean);
ConfigDO convert(ConfigUpdateReqVO bean);
@Mapping(source = "configKey", target = "key")
List<ConfigExcelVO> convertList(List<ConfigDO> list);
}

View File

@ -1,36 +0,0 @@
package cn.iocoder.yudao.module.infra.convert.job;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobExcelVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobRespVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobUpdateReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 定时任务 Convert
*
* @author 芋道源码
*/
@Mapper
public interface JobConvert {
JobConvert INSTANCE = Mappers.getMapper(JobConvert.class);
JobDO convert(JobCreateReqVO bean);
JobDO convert(JobUpdateReqVO bean);
JobRespVO convert(JobDO bean);
List<JobRespVO> convertList(List<JobDO> list);
PageResult<JobRespVO> convertPage(PageResult<JobDO> page);
List<JobExcelVO> convertList02(List<JobDO> list);
}

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.module.infra.convert.job;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogExcelVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogRespVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobLogDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 定时任务日志 Convert
*
* @author 芋艿
*/
@Mapper
public interface JobLogConvert {
JobLogConvert INSTANCE = Mappers.getMapper(JobLogConvert.class);
JobLogRespVO convert(JobLogDO bean);
List<JobLogRespVO> convertList(List<JobLogDO> list);
PageResult<JobLogRespVO> convertPage(PageResult<JobLogDO> page);
List<JobLogExcelVO> convertList02(List<JobLogDO> list);
}

View File

@ -1,64 +0,0 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.config;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.infra.enums.config.ConfigTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 参数配置表
*
* @author 芋道源码
*/
@TableName("infra_config")
@KeySequence("infra_config_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ConfigDO extends BaseDO {
/**
* 参数主键
*/
@TableId
private Long id;
/**
* 参数分类
*/
private String category;
/**
* 参数名称
*/
private String name;
/**
* 参数键名
*
* 支持多 DB 类型时无法直接使用 key + @TableField("config_key") 来实现转换原因是 "config_key" AS key 而存在报错
*/
private String configKey;
/**
* 参数键值
*/
private String value;
/**
* 参数类型
*
* 枚举 {@link ConfigTypeEnum}
*/
private Integer type;
/**
* 是否可见
*
* 不可见的参数一般是敏感参数前端不可获取
*/
private Boolean visible;
/**
* 备注
*/
private String remark;
}

View File

@ -1,74 +0,0 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.job;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.infra.enums.job.JobStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* 定时任务 DO
*
* @author 芋道源码
*/
@TableName("infra_job")
@KeySequence("infra_job_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class JobDO extends BaseDO {
/**
* 任务编号
*/
@TableId
private Long id;
/**
* 任务名称
*/
private String name;
/**
* 任务状态
*
* 枚举 {@link JobStatusEnum}
*/
private Integer status;
/**
* 处理器的名字
*/
private String handlerName;
/**
* 处理器的参数
*/
private String handlerParam;
/**
* CRON 表达式
*/
private String cronExpression;
// ========== 重试相关字段 ==========
/**
* 重试次数
* 如果不重试则设置为 0
*/
private Integer retryCount;
/**
* 重试间隔单位毫秒
* 如果没有间隔则设置为 0
*/
private Integer retryInterval;
// ========== 监控相关字段 ==========
/**
* 监控超时时间单位毫秒
* 为空时表示不监控
*
* 注意这里的超时的目的不是进行任务的取消而是告警任务的执行时间过长
*/
private Integer monitorTimeout;
}

View File

@ -1,82 +0,0 @@
package cn.iocoder.yudao.module.infra.dal.dataobject.job;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.module.infra.enums.job.JobLogStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.util.Date;
/**
* 定时任务的执行日志
*
* @author 芋道源码
*/
@TableName("infra_job_log")
@KeySequence("infra_job_log_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class JobLogDO extends BaseDO {
/**
* 日志编号
*/
private Long id;
/**
* 任务编号
*
* 关联 {@link JobDO#getId()}
*/
private Long jobId;
/**
* 处理器的名字
*
* 冗余字段 {@link JobDO#getHandlerName()}
*/
private String handlerName;
/**
* 处理器的参数
*
* 冗余字段 {@link JobDO#getHandlerParam()}
*/
private String handlerParam;
/**
* 第几次执行
*
* 用于区分是不是重试执行如果是重试执行 index 大于 1
*/
private Integer executeIndex;
/**
* 开始执行时间
*/
private Date beginTime;
/**
* 结束执行时间
*/
private Date endTime;
/**
* 执行时长单位毫秒
*/
private Integer duration;
/**
* 状态
*
* 枚举 {@link JobLogStatusEnum}
*/
private Integer status;
/**
* 结果数据
*
* 成功时使用 {@link JobHandler#execute(String)} 的结果
* 失败时使用 {@link JobHandler#execute(String)} 的异常堆栈
*/
private String result;
}

View File

@ -1,43 +0,0 @@
package cn.iocoder.yudao.module.infra.dal.mysql.job;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogPageReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobLogDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 任务日志 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface JobLogMapper extends BaseMapperX<JobLogDO> {
default PageResult<JobLogDO> selectPage(JobLogPageReqVO reqVO) {
return selectPage(reqVO, new QueryWrapperX<JobLogDO>()
.eqIfPresent("job_id", reqVO.getJobId())
.likeIfPresent("handler_name", reqVO.getHandlerName())
.geIfPresent("begin_time", reqVO.getBeginTime())
.leIfPresent("end_time", reqVO.getEndTime())
.eqIfPresent("status", reqVO.getStatus())
.orderByDesc("id") // ID 倒序
);
}
default List<JobLogDO> selectList(JobLogExportReqVO reqVO) {
return selectList(new QueryWrapperX<JobLogDO>()
.eqIfPresent("job_id", reqVO.getJobId())
.likeIfPresent("handler_name", reqVO.getHandlerName())
.geIfPresent("begin_time", reqVO.getBeginTime())
.leIfPresent("end_time", reqVO.getEndTime())
.eqIfPresent("status", reqVO.getStatus())
.orderByDesc("id") // ID 倒序
);
}
}

View File

@ -1,41 +0,0 @@
package cn.iocoder.yudao.module.infra.dal.mysql.job;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobPageReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 定时任务 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface JobMapper extends BaseMapperX<JobDO> {
default JobDO selectByHandlerName(String handlerName) {
return selectOne(JobDO::getHandlerName, handlerName);
}
default PageResult<JobDO> selectPage(JobPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<JobDO>()
.likeIfPresent(JobDO::getName, reqVO.getName())
.eqIfPresent(JobDO::getStatus, reqVO.getStatus())
.likeIfPresent(JobDO::getHandlerName, reqVO.getHandlerName())
);
}
default List<JobDO> selectList(JobExportReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<JobDO>()
.likeIfPresent(JobDO::getName, reqVO.getName())
.eqIfPresent(JobDO::getStatus, reqVO.getStatus())
.likeIfPresent(JobDO::getHandlerName, reqVO.getHandlerName())
);
}
}

View File

@ -1,21 +0,0 @@
package cn.iocoder.yudao.module.infra.enums.config;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum ConfigTypeEnum {
/**
* 系统配置
*/
SYSTEM(1),
/**
* 自定义配置
*/
CUSTOM(2);
private final Integer type;
}

View File

@ -1,24 +0,0 @@
package cn.iocoder.yudao.module.infra.enums.job;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 任务日志的状态枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum JobLogStatusEnum {
RUNNING(0), // 运行中
SUCCESS(1), // 成功
FAILURE(2); // 失败
/**
* 状态
*/
private final Integer status;
}

View File

@ -1,42 +0,0 @@
package cn.iocoder.yudao.module.infra.enums.job;
import com.google.common.collect.Sets;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.quartz.impl.jdbcjobstore.Constants;
import java.util.Collections;
import java.util.Set;
/**
* 任务状态的枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum JobStatusEnum {
/**
* 初始化中
*/
INIT(0, Collections.emptySet()),
/**
* 开启
*/
NORMAL(1, Sets.newHashSet(Constants.STATE_WAITING, Constants.STATE_ACQUIRED, Constants.STATE_BLOCKED)),
/**
* 暂停
*/
STOP(2, Sets.newHashSet(Constants.STATE_PAUSED, Constants.STATE_PAUSED_BLOCKED));
/**
* 状态
*/
private final Integer status;
/**
* 对应的 Quartz 触发器的状态集合
*/
private final Set<String> quartzStates;
}

View File

@ -1,75 +0,0 @@
package cn.iocoder.yudao.module.infra.service.config;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigPageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigUpdateReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;
import javax.validation.Valid;
import java.util.List;
/**
* 参数配置 Service 接口
*
* @author 芋道源码
*/
public interface ConfigService {
/**
* 创建参数配置
*
* @param reqVO 创建信息
* @return 配置编号
*/
Long createConfig(@Valid ConfigCreateReqVO reqVO);
/**
* 更新参数配置
*
* @param reqVO 更新信息
*/
void updateConfig(@Valid ConfigUpdateReqVO reqVO);
/**
* 删除参数配置
*
* @param id 配置编号
*/
void deleteConfig(Long id);
/**
* 获得参数配置
*
* @param id 配置编号
* @return 参数配置
*/
ConfigDO getConfig(Long id);
/**
* 根据参数键获得参数配置
*
* @param key 配置键
* @return 参数配置
*/
ConfigDO getConfigByKey(String key);
/**
* 获得参数配置分页列表
*
* @param reqVO 分页条件
* @return 分页列表
*/
PageResult<ConfigDO> getConfigPage(@Valid ConfigPageReqVO reqVO);
/**
* 获得参数配置列表
*
* @param reqVO 列表
* @return 列表
*/
List<ConfigDO> getConfigList(@Valid ConfigExportReqVO reqVO);
}

View File

@ -1,132 +0,0 @@
package cn.iocoder.yudao.module.infra.service.config;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigPageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigUpdateReqVO;
import cn.iocoder.yudao.module.infra.convert.config.ConfigConvert;
import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;
import cn.iocoder.yudao.module.infra.dal.mysql.config.ConfigMapper;
import cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.infra.enums.config.ConfigTypeEnum;
import cn.iocoder.yudao.module.infra.mq.producer.config.ConfigProducer;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.List;
/**
* 参数配置 Service 实现类
*/
@Service
@Slf4j
@Validated
public class ConfigServiceImpl implements ConfigService {
@Resource
private ConfigMapper configMapper;
@Resource
private ConfigProducer configProducer;
@Override
public Long createConfig(ConfigCreateReqVO reqVO) {
// 校验正确性
checkCreateOrUpdate(null, reqVO.getKey());
// 插入参数配置
ConfigDO config = ConfigConvert.INSTANCE.convert(reqVO);
config.setType(ConfigTypeEnum.CUSTOM.getType());
configMapper.insert(config);
// 发送刷新消息
configProducer.sendConfigRefreshMessage();
return config.getId();
}
@Override
public void updateConfig(ConfigUpdateReqVO reqVO) {
// 校验正确性
checkCreateOrUpdate(reqVO.getId(), null); // 不允许更新 key
// 更新参数配置
ConfigDO updateObj = ConfigConvert.INSTANCE.convert(reqVO);
configMapper.updateById(updateObj);
// 发送刷新消息
configProducer.sendConfigRefreshMessage();
}
@Override
public void deleteConfig(Long id) {
// 校验配置存在
ConfigDO config = checkConfigExists(id);
// 内置配置不允许删除
if (ConfigTypeEnum.SYSTEM.getType().equals(config.getType())) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE);
}
// 删除
configMapper.deleteById(id);
// 发送刷新消息
configProducer.sendConfigRefreshMessage();
}
@Override
public ConfigDO getConfig(Long id) {
return configMapper.selectById(id);
}
@Override
public ConfigDO getConfigByKey(String key) {
return configMapper.selectByKey(key);
}
@Override
public PageResult<ConfigDO> getConfigPage(ConfigPageReqVO reqVO) {
return configMapper.selectPage(reqVO);
}
@Override
public List<ConfigDO> getConfigList(ConfigExportReqVO reqVO) {
return configMapper.selectList(reqVO);
}
private void checkCreateOrUpdate(Long id, String key) {
// 校验自己存在
checkConfigExists(id);
// 校验参数配置 key 的唯一性
if (StrUtil.isNotEmpty(key)) {
checkConfigKeyUnique(id, key);
}
}
@VisibleForTesting
public ConfigDO checkConfigExists(Long id) {
if (id == null) {
return null;
}
ConfigDO config = configMapper.selectById(id);
if (config == null) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.CONFIG_NOT_EXISTS);
}
return config;
}
@VisibleForTesting
public void checkConfigKeyUnique(Long id, String key) {
ConfigDO config = configMapper.selectByKey(key);
if (config == null) {
return;
}
// 如果 id 为空说明不用比较是否为相同 id 的参数配置
if (id == null) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.CONFIG_KEY_DUPLICATE);
}
if (!config.getId().equals(id)) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.CONFIG_KEY_DUPLICATE);
}
}
}

View File

@ -1,51 +0,0 @@
package cn.iocoder.yudao.module.infra.service.job;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.quartz.core.service.JobLogFrameworkService;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogPageReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobLogDO;
import java.util.Collection;
import java.util.List;
/**
* Job 日志 Service 接口
*
* @author 芋道源码
*/
public interface JobLogService extends JobLogFrameworkService {
/**
* 获得定时任务
*
* @param id 编号
* @return 定时任务
*/
JobLogDO getJobLog(Long id);
/**
* 获得定时任务列表
*
* @param ids 编号
* @return 定时任务列表
*/
List<JobLogDO> getJobLogList(Collection<Long> ids);
/**
* 获得定时任务分页
*
* @param pageReqVO 分页查询
* @return 定时任务分页
*/
PageResult<JobLogDO> getJobLogPage(JobLogPageReqVO pageReqVO);
/**
* 获得定时任务列表, 用于 Excel 导出
*
* @param exportReqVO 查询条件
* @return 定时任务分页
*/
List<JobLogDO> getJobLogList(JobLogExportReqVO exportReqVO);
}

View File

@ -1,73 +0,0 @@
package cn.iocoder.yudao.module.infra.service.job;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogPageReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobLogDO;
import cn.iocoder.yudao.module.infra.dal.mysql.job.JobLogMapper;
import cn.iocoder.yudao.module.infra.enums.job.JobLogStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
* Job 日志 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
@Slf4j
public class JobLogServiceImpl implements JobLogService {
@Resource
private JobLogMapper jobLogMapper;
@Override
public Long createJobLog(Long jobId, Date beginTime, String jobHandlerName, String jobHandlerParam, Integer executeIndex) {
JobLogDO log = JobLogDO.builder().jobId(jobId).handlerName(jobHandlerName).handlerParam(jobHandlerParam).executeIndex(executeIndex)
.beginTime(beginTime).status(JobLogStatusEnum.RUNNING.getStatus()).build();
jobLogMapper.insert(log);
return log.getId();
}
@Override
@Async
public void updateJobLogResultAsync(Long logId, Date endTime, Integer duration, boolean success, String result) {
try {
JobLogDO updateObj = JobLogDO.builder().id(logId).endTime(endTime).duration(duration)
.status(success ? JobLogStatusEnum.SUCCESS.getStatus() : JobLogStatusEnum.FAILURE.getStatus()).result(result).build();
jobLogMapper.updateById(updateObj);
} catch (Exception ex) {
log.error("[updateJobLogResultAsync][logId({}) endTime({}) duration({}) success({}) result({})]",
logId, endTime, duration, success, result);
}
}
@Override
public JobLogDO getJobLog(Long id) {
return jobLogMapper.selectById(id);
}
@Override
public List<JobLogDO> getJobLogList(Collection<Long> ids) {
return jobLogMapper.selectBatchIds(ids);
}
@Override
public PageResult<JobLogDO> getJobLogPage(JobLogPageReqVO pageReqVO) {
return jobLogMapper.selectPage(pageReqVO);
}
@Override
public List<JobLogDO> getJobLogList(JobLogExportReqVO exportReqVO) {
return jobLogMapper.selectList(exportReqVO);
}
}

View File

@ -1,91 +0,0 @@
package cn.iocoder.yudao.module.infra.service.job;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobPageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobUpdateReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobDO;
import org.quartz.SchedulerException;
import javax.validation.Valid;
import java.util.Collection;
import java.util.List;
/**
* 定时任务 Service 接口
*
* @author 芋道源码
*/
public interface JobService {
/**
* 创建定时任务
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createJob(@Valid JobCreateReqVO createReqVO) throws SchedulerException;
/**
* 更新定时任务
*
* @param updateReqVO 更新信息
*/
void updateJob(@Valid JobUpdateReqVO updateReqVO) throws SchedulerException;
/**
* 更新定时任务的状态
*
* @param id 任务编号
* @param status 状态
*/
void updateJobStatus(Long id, Integer status) throws SchedulerException;
/**
* 触发定时任务
*
* @param id 任务编号
*/
void triggerJob(Long id) throws SchedulerException;
/**
* 删除定时任务
*
* @param id 编号
*/
void deleteJob(Long id) throws SchedulerException;
/**
* 获得定时任务
*
* @param id 编号
* @return 定时任务
*/
JobDO getJob(Long id);
/**
* 获得定时任务列表
*
* @param ids 编号
* @return 定时任务列表
*/
List<JobDO> getJobList(Collection<Long> ids);
/**
* 获得定时任务分页
*
* @param pageReqVO 分页查询
* @return 定时任务分页
*/
PageResult<JobDO> getJobPage(JobPageReqVO pageReqVO);
/**
* 获得定时任务列表, 用于 Excel 导出
*
* @param exportReqVO 查询条件
* @return 定时任务分页
*/
List<JobDO> getJobList(JobExportReqVO exportReqVO);
}

View File

@ -1,173 +0,0 @@
package cn.iocoder.yudao.module.infra.service.job;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.quartz.core.scheduler.SchedulerManager;
import cn.iocoder.yudao.framework.quartz.core.util.CronUtils;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobPageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobUpdateReqVO;
import cn.iocoder.yudao.module.infra.convert.job.JobConvert;
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 org.quartz.SchedulerException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.containsAny;
/**
* 定时任务 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class JobServiceImpl implements JobService {
@Resource
private JobMapper jobMapper;
@Resource
private SchedulerManager schedulerManager;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createJob(JobCreateReqVO createReqVO) throws SchedulerException {
validateCronExpression(createReqVO.getCronExpression());
// 校验唯一性
if (jobMapper.selectByHandlerName(createReqVO.getHandlerName()) != null) {
throw exception(JOB_HANDLER_EXISTS);
}
// 插入
JobDO job = JobConvert.INSTANCE.convert(createReqVO);
job.setStatus(JobStatusEnum.INIT.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 添加 Job Quartz
schedulerManager.addJob(job.getId(), job.getHandlerName(), job.getHandlerParam(), job.getCronExpression(),
createReqVO.getRetryCount(), createReqVO.getRetryInterval());
// 更新
JobDO updateObj = JobDO.builder().id(job.getId()).status(JobStatusEnum.NORMAL.getStatus()).build();
jobMapper.updateById(updateObj);
// 返回
return job.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateJob(JobUpdateReqVO updateReqVO) throws SchedulerException {
validateCronExpression(updateReqVO.getCronExpression());
// 校验存在
JobDO job = this.validateJobExists(updateReqVO.getId());
// 只有开启状态才可以修改.原因是如果出暂停状态修改 Quartz Job 会导致任务又开始执行
if (!job.getStatus().equals(JobStatusEnum.NORMAL.getStatus())) {
throw exception(JOB_UPDATE_ONLY_NORMAL_STATUS);
}
// 更新
JobDO updateObj = JobConvert.INSTANCE.convert(updateReqVO);
fillJobMonitorTimeoutEmpty(updateObj);
jobMapper.updateById(updateObj);
// 更新 Job Quartz
schedulerManager.updateJob(job.getHandlerName(), updateReqVO.getHandlerParam(), updateReqVO.getCronExpression(),
updateReqVO.getRetryCount(), updateReqVO.getRetryInterval());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateJobStatus(Long id, Integer status) throws SchedulerException {
// 校验 status
if (!containsAny(status, JobStatusEnum.NORMAL.getStatus(), JobStatusEnum.STOP.getStatus())) {
throw exception(JOB_CHANGE_STATUS_INVALID);
}
// 校验存在
JobDO job = this.validateJobExists(id);
// 校验是否已经为当前状态
if (job.getStatus().equals(status)) {
throw exception(JOB_CHANGE_STATUS_EQUALS);
}
// 更新 Job 状态
JobDO updateObj = JobDO.builder().id(id).status(status).build();
jobMapper.updateById(updateObj);
// 更新状态 Job Quartz
if (JobStatusEnum.NORMAL.getStatus().equals(status)) { // 开启
schedulerManager.resumeJob(job.getHandlerName());
} else { // 暂停
schedulerManager.pauseJob(job.getHandlerName());
}
}
@Override
public void triggerJob(Long id) throws SchedulerException {
// 校验存在
JobDO job = this.validateJobExists(id);
// 触发 Quartz 中的 Job
schedulerManager.triggerJob(job.getId(), job.getHandlerName(), job.getHandlerParam());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteJob(Long id) throws SchedulerException {
// 校验存在
JobDO job = this.validateJobExists(id);
// 更新
jobMapper.deleteById(id);
// 删除 Job Quartz
schedulerManager.deleteJob(job.getHandlerName());
}
private JobDO validateJobExists(Long id) {
JobDO job = jobMapper.selectById(id);
if (job == null) {
throw exception(JOB_NOT_EXISTS);
}
return job;
}
private void validateCronExpression(String cronExpression) {
if (!CronUtils.isValid(cronExpression)) {
throw exception(JOB_CRON_EXPRESSION_VALID);
}
}
@Override
public JobDO getJob(Long id) {
return jobMapper.selectById(id);
}
@Override
public List<JobDO> getJobList(Collection<Long> ids) {
return jobMapper.selectBatchIds(ids);
}
@Override
public PageResult<JobDO> getJobPage(JobPageReqVO pageReqVO) {
return jobMapper.selectPage(pageReqVO);
}
@Override
public List<JobDO> getJobList(JobExportReqVO exportReqVO) {
return jobMapper.selectList(exportReqVO);
}
private static void fillJobMonitorTimeoutEmpty(JobDO job) {
if (job.getMonitorTimeout() == null) {
job.setMonitorTimeout(0);
}
}
}

View File

@ -0,0 +1,204 @@
server:
port: 48082
--- #################### 数据库相关配置 ####################
spring:
# 数据源配置项
autoconfigure:
exclude:
- com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源
datasource:
druid: # Druid 【监控】相关的全局配置
web-stat-filter:
enabled: true
stat-view-servlet:
enabled: true
allow: # 设置白名单,不填则允许所有访问
url-pattern: /druid/*
login-username: # 控制台管理用户名和密码
login-password:
filter:
stat:
enabled: true
log-slow-sql: true # 慢 SQL 记录
slow-sql-millis: 100
merge-sql: true
wall:
config:
multi-statement-allow: true
dynamic: # 多数据源配置
druid: # Druid 【连接池】相关的全局配置
initial-size: 5 # 初始连接数
min-idle: 10 # 最小连接池数量
max-active: 20 # 最大连接池数量
max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
validation-query: SELECT 1 # 配置检测连接是否有效
test-while-idle: true
test-on-borrow: false
test-on-return: false
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&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT&nullCatalogMeansCurrent=true
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 3WLiVUBEwTbvAfsh
slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改
name: ruoyi-vue-pro
url: jdbc:mysql://400-infra.server.iocoder.cn:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT&nullCatalogMeansCurrent=true
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 3WLiVUBEwTbvAfsh
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
redis:
host: 400-infra.server.iocoder.cn # 地址
port: 6379 # 端口
database: 1 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
jasypt:
encryptor:
password: yuanma # 加解密的秘钥
--- #################### 定时任务相关配置 ####################
# Quartz 配置项,对应 QuartzProperties 配置类
spring:
quartz:
auto-startup: true # 测试环境,需要开启 Job
scheduler-name: schedulerName # Scheduler 名字。默认为 schedulerName
job-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。
wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
properties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档
org:
quartz:
# Scheduler 相关配置
scheduler:
instanceName: schedulerName
instanceId: AUTO # 自动生成 instance ID
# JobStore 相关配置
jobStore:
# JobStore 实现类。可见博客https://blog.csdn.net/weixin_42458219/article/details/122247162
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
isClustered: true # 是集群模式
clusterCheckinInterval: 15000 # 集群检查频率,单位:毫秒。默认为 15000即 15 秒
misfireThreshold: 60000 # misfire 阀值,单位:毫秒。
# 线程池相关配置
threadPool:
threadCount: 25 # 线程池大小。默认为 10 。
threadPriority: 5 # 线程优先级
class: org.quartz.simpl.SimpleThreadPool # 线程池类型
jdbc: # 使用 JDBC 的 JobStore 的时候JDBC 的配置
initialize-schema: NEVER # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。
--- #################### 配置中心相关配置 ####################
# Apollo 配置中心
apollo:
bootstrap:
enabled: true # 设置 Apollo 在启动阶段生效
eagerLoad:
enabled: true # 设置 Apollo 在日志初始化前生效,可以实现日志的动态级别配置
jdbc: # 自定义的 JDBC 配置项,用于数据库的地址
dao: cn.iocoder.yudao.module.infra.dal.mysql.config.ConfigDAOImpl
url: ${spring.datasource.dynamic.datasource.master.url}
username: ${spring.datasource.dynamic.datasource.master.username}
password: ${spring.datasource.dynamic.datasource.master.password}
--- #################### 服务保障相关配置 ####################
# Lock4j 配置项
lock4j:
acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
# Resilience4j 配置项
resilience4j:
ratelimiter:
instances:
backendA:
limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50
limit-refresh-period: 60s # 每个周期的时长,单位:微秒。默认为 500
timeout-duration: 1s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s
register-health-indicator: true # 是否注册到健康监测
--- #################### 监控相关配置 ####################
# Actuator 监控端点的配置项
management:
endpoints:
web:
base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
exposure:
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
# Spring Boot Admin 配置项
spring:
boot:
admin:
# Spring Boot Admin Client 客户端的相关配置
client:
url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址
instance:
prefer-ip: true # 注册实例时,优先使用 IP
# Spring Boot Admin Server 服务端的相关配置
context-path: /admin # 配置 Spring
# 日志文件配置
logging:
file:
name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径
--- #################### 微信公众号相关配置 ####################
wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档
mp:
# 公众号配置(必填)
app-id: wx041349c6f39b268b
secret: 5abee519483bc9f8cb37ce280e814bd0
# 存储配置,解决 AccessToken 的跨节点的共享
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis会自动从 Spring 中获取
key-prefix: wx # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置
http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
--- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置
yudao:
security:
token-header: Authorization
mock-enable: true
mock-secret: test
xss:
enable: false
exclude-urls: # 如下两个 url仅仅是为了演示去掉配置也没关系
- ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
pay:
pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
pay-return-url: http://niubi.natapp1.cc/api/pay/order/return
refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify
demo: true # 开启演示模式
justauth:
enabled: true
type:
DINGTALK: # 钉钉
client-id: dingvrnreaje3yqvzhxg
client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI
ignore-check-redirect-uri: true
WECHAT_ENTERPRISE: # 企业微信
client-id: wwd411c69a39ad2e54
client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw
agent-id: 1000004
ignore-check-redirect-uri: true
cache:
type: REDIS
prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE::
timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟

View File

@ -0,0 +1,220 @@
spring:
# 数据源配置项
autoconfigure:
exclude:
- com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源
datasource:
druid: # Druid 【监控】相关的全局配置
web-stat-filter:
enabled: true
stat-view-servlet:
enabled: true
allow: # 设置白名单,不填则允许所有访问
url-pattern: /druid/*
login-username: # 控制台管理用户名和密码
login-password:
filter:
stat:
enabled: true
log-slow-sql: true # 慢 SQL 记录
slow-sql-millis: 100
merge-sql: true
wall:
config:
multi-statement-allow: true
dynamic: # 多数据源配置
druid: # Druid 【连接池】相关的全局配置
initial-size: 5 # 初始连接数
min-idle: 10 # 最小连接池数量
max-active: 20 # 最大连接池数量
max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
test-while-idle: true
test-on-borrow: false
test-on-return: false
primary: master
datasource:
master:
name: ruoyi-vue-pro
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
# url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
# url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.master.name} # SQLServer 连接的示例
username: root
password: 123456
# username: sa
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
slave: # 模拟从库,可根据自己需要修改
name: ruoyi-vue-pro
url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例
# url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
# url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例
# url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例
# url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.slave.name} # SQLServer 连接的示例
username: root
password: 123456
# username: sa
# password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
redis:
host: 127.0.0.1 # 地址
port: 6379 # 端口
database: 0 # 数据库索引
# password: 123456 # 密码,建议生产环境开启
jasypt:
encryptor:
password: yuanma # 加解密的秘钥
--- #################### 定时任务相关配置 ####################
# Quartz 配置项,对应 QuartzProperties 配置类
spring:
quartz:
auto-startup: false # 本地开发环境,尽量不要开启 Job
scheduler-name: schedulerName # Scheduler 名字。默认为 schedulerName
job-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。
wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
properties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档
org:
quartz:
# Scheduler 相关配置
scheduler:
instanceName: schedulerName
instanceId: AUTO # 自动生成 instance ID
# JobStore 相关配置
jobStore:
# JobStore 实现类。可见博客https://blog.csdn.net/weixin_42458219/article/details/122247162
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
isClustered: true # 是集群模式
clusterCheckinInterval: 15000 # 集群检查频率,单位:毫秒。默认为 15000即 15 秒
misfireThreshold: 60000 # misfire 阀值,单位:毫秒。
# 线程池相关配置
threadPool:
threadCount: 25 # 线程池大小。默认为 10 。
threadPriority: 5 # 线程优先级
class: org.quartz.simpl.SimpleThreadPool # 线程池类型
jdbc: # 使用 JDBC 的 JobStore 的时候JDBC 的配置
initialize-schema: NEVER # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。
--- #################### 配置中心相关配置 ####################
# Apollo 配置中心
apollo:
bootstrap:
enabled: true # 设置 Apollo 在启动阶段生效
eagerLoad:
enabled: true # 设置 Apollo 在日志初始化前生效,可以实现日志的动态级别配置
jdbc: # 自定义的 JDBC 配置项,用于数据库的地址
dao: cn.iocoder.yudao.module.infra.dal.mysql.config.ConfigDAOImpl
url: ${spring.datasource.dynamic.datasource.master.url}
username: ${spring.datasource.dynamic.datasource.master.username}
password: ${spring.datasource.dynamic.datasource.master.password}
--- #################### 服务保障相关配置 ####################
# Lock4j 配置项
lock4j:
acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒
expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒
# Resilience4j 配置项
resilience4j:
ratelimiter:
instances:
backendA:
limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50
limit-refresh-period: 60s # 每个周期的时长,单位:微秒。默认为 500
timeout-duration: 1s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s
register-health-indicator: true # 是否注册到健康监测
--- #################### 监控相关配置 ####################
# Actuator 监控端点的配置项
management:
endpoints:
web:
base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator
exposure:
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
# Spring Boot Admin 配置项
spring:
boot:
admin:
# Spring Boot Admin Client 客户端的相关配置
client:
url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # 设置 Spring Boot Admin Server 地址
instance:
prefer-ip: true # 注册实例时,优先使用 IP
# Spring Boot Admin Server 服务端的相关配置
context-path: /admin # 配置 Spring
# 日志文件配置
logging:
file:
name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径
level:
# 配置自己写的 MyBatis Mapper 打印日志
cn.iocoder.yudao.module.bpm.dal.mysql: debug
cn.iocoder.yudao.module.infra.dal.mysql: debug
cn.iocoder.yudao.module.pay.dal.mysql: debug
cn.iocoder.yudao.module.system.dal.mysql: debug
cn.iocoder.yudao.module.tool.dal.mysql: debug
cn.iocoder.yudao.module.member.dal.mysql: debug
--- #################### 微信公众号相关配置 ####################
wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档
mp:
# 公众号配置(必填)
app-id: wx041349c6f39b268b
secret: 5abee519483bc9f8cb37ce280e814bd0
# 存储配置,解决 AccessToken 的跨节点的共享
config-storage:
type: RedisTemplate # 采用 RedisTemplate 操作 Redis会自动从 Spring 中获取
key-prefix: wx # Redis Key 的前缀 TODO 芋艿:解决下 Redis key 管理的配置
http-client-type: HttpClient # 采用 HttpClient 请求微信公众号平台
--- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置
yudao:
captcha:
enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试
security:
token-header: Authorization
mock-enable: true
mock-secret: test
xss:
enable: false
exclude-urls: # 如下两个 url仅仅是为了演示去掉配置也没关系
- ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
pay:
pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
pay-return-url: http://niubi.natapp1.cc/api/pay/order/return
refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify
demo: false # 关闭演示模式
justauth:
enabled: true
type:
DINGTALK: # 钉钉
client-id: dingvrnreaje3yqvzhxg
client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI
ignore-check-redirect-uri: true
WECHAT_ENTERPRISE: # 企业微信
client-id: wwd411c69a39ad2e54
client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw
agent-id: 1000004
ignore-check-redirect-uri: true
cache:
type: REDIS
prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE::
timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟

View File

@ -0,0 +1,146 @@
spring:
main:
allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。
# Servlet 配置
servlet:
# 文件上传相关配置项
multipart:
max-file-size: 16MB # 单个文件大小
max-request-size: 32MB # 设置总上传的文件大小
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER # 解决 SpringFox 与 SpringBoot 2.6.x 不兼容的问题,参见 SpringFoxHandlerProviderBeanPostProcessor 类
# Jackson 配置项
jackson:
serialization:
write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳
write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401
write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳
fail-on-empty-beans: false # 允许序列化无属性的 Bean
# Cache 配置项
cache:
type: REDIS
redis:
time-to-live: 1h # 设置过期时间为 1 小时
# 工作流 Activiti 配置
activiti:
# 1. false: 默认值activiti启动时对比数据库表中保存的版本如果不匹配。将抛出异常
# 2. true: 启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表
# 3. create_drop: 启动时自动创建表,关闭时自动删除表
# 4. drop_create: 启动时,删除旧表,再创建新表
database-schema-update: true # 设置为 false可通过 sql/activiti.sql 初始化
db-history-used: true # activiti7 默认 false 不生成历史信息表,需手动设置开启
check-process-definitions: false # 设置为 false禁用 /resources/processes 自动部署 BPMN XML 流程
history-level: full # full保存历史数据的最高级别可保存全部流程相关细节包括流程流转各节点参数
# 工作流 Flowable 配置
flowable:
# 1. false: 默认值Flowable 启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常
# 2. true: 启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表
# 3. create_drop: 启动时自动创建表,关闭时自动删除表
# 4. drop_create: 启动时,删除旧表,再创建新表
database-schema-update: true # 设置为 false可通过 https://github.com/flowable/flowable-sql 初始化
db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置
check-process-definitions: false # 设置为 false禁用 /resources/processes 自动部署 BPMN XML 流程
history-level: full # full保存历史数据的最高级别可保存全部流程相关细节包括流程流转各节点参数
# MyBatis Plus 的配置项
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
global-config:
db-config:
id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。
# id-type: AUTO # 自增 ID适合 MySQL 等直接自增的数据库
# id-type: INPUT # 用户输入 ID适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
# id-type: ASSIGN_ID # 分配 ID默认使用雪花算法。注意Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
type-aliases-package: ${yudao.info.base-package}.dal.dataobject
--- #################### RPC 远程调用相关配置 ####################
dubbo:
scan:
base-packages: ${yudao.info.base-package}.api # 指定 Dubbo 服务实现类的扫描基准包
protocol:
name: dubbo # 协议名称
port: -1 # 协议端口,-1 表示自增端口,从 20880 开始
registry:
address: spring-cloud://localhost # 设置使用 Spring Cloud 注册中心
--- #################### 芋道相关配置 ####################
yudao:
info:
version: 1.0.0
base-package: cn.iocoder.yudao.module.infra
web:
admin-api:
prefix: /admin-api
controller: '**.controller.admin.**'
app-api:
prefix: /app-api
controller: '**.controller.app.**'
admin-ui:
url: http://dashboard.yudao.iocoder.cn # Admin 管理后台 UI 的地址
swagger:
title: 管理后台
description: 提供管理员管理的所有功能
version: ${yudao.info.version}
base-package: ${yudao.info.base-package}
captcha:
timeout: 5m
width: 160
height: 60
codegen:
base-package: ${yudao.info.base-package}
db-schemas: ${spring.datasource.dynamic.datasource.master.name}
error-code: # 错误码相关配置项
constants-class-list:
- cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.member.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.system.enums.ErrorCodeConstants
tenant: # 多租户相关配置项
enable: true
ignore-urls:
- /admin-api/system/tenant/get-id-by-name # 基于名字获取租户,不许带租户编号
- /admin-api/system/captcha/get-image # 获取图片验证码,和租户无关
- /admin-api/infra/file/*/get/** # 获取图片,和租户无关
- /admin-api/system/sms/callback/* # 短信回调接口,无法带上租户编号
ignore-tables:
- system_tenant
- system_tenant_package
- system_dict_data
- system_dict_type
- system_error_code
- system_menu
- system_sms_channel
- system_sms_template
- system_sms_log
- system_sensitive_word
- system_oauth2_client
- infra_codegen_column
- infra_codegen_table
- infra_test_demo
- infra_config
- infra_file_config
- infra_file
- infra_file_content
- infra_job
- infra_job_log
- infra_job_log
- infra_data_source_config
sms-code: # 短信验证码相关的配置项
expire-times: 10m
send-frequency: 1m
send-maximum-quantity-per-day: 10
begin-code: 9999 # 这里配置 9999 的原因是,测试方便。
end-code: 9999 # 这里配置 9999 的原因是,测试方便。
debug: false

View File

@ -0,0 +1,17 @@
芋道源码 http://www.iocoder.cn
Application Version: ${yudao.info.version}
Spring Boot Version: ${spring-boot.version}
.__ __. ______ .______ __ __ _______
| \ | | / __ \ | _ \ | | | | / _____|
| \| | | | | | | |_) | | | | | | | __
| . ` | | | | | | _ < | | | | | | |_ |
| |\ | | `--' | | |_) | | `--' | | |__| |
|__| \__| \______/ |______/ \______/ \______|
███╗ ██╗ ██████╗ ██████╗ ██╗ ██╗ ██████╗
████╗ ██║██╔═══██╗ ██╔══██╗██║ ██║██╔════╝
██╔██╗ ██║██║ ██║ ██████╔╝██║ ██║██║ ███╗
██║╚██╗██║██║ ██║ ██╔══██╗██║ ██║██║ ██║
██║ ╚████║╚██████╔╝ ██████╔╝╚██████╔╝╚██████╔╝
╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝

View File

@ -0,0 +1,23 @@
--- #################### 注册中心相关配置 ####################
spring:
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
namespace: dev # 命名空间。这里使用 dev 开发环境
metadata:
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
--- #################### 配置中心相关配置 ####################
spring:
cloud:
nacos:
# Nacos Config 配置项,对应 NacosConfigProperties 配置属性类
config:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
namespace: dev # 命名空间。这里使用 dev 开发环境
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
name: # 使用的 Nacos 配置集的 dataId默认为 spring.application.name
file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名,同时也是 Nacos 配置集的配置格式,默认为 properties

View File

@ -0,0 +1,16 @@
spring:
application:
name: infra-server
profiles:
active: local
server:
port: 48082
yudao:
security: # TODO 芋艿,发现一定要配置,需要找下原因
token-header: Authorization
mock-enable: true
mock-secret: test

View File

@ -0,0 +1,76 @@
<configuration>
<!-- 引用 Spring Boot 的 logback 基础配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<!-- 变量 yudao.info.base-package基础业务包 -->
<springProperty scope="context" name="yudao.info.base-package" source="yudao.info.base-package"/>
<!-- 格式化输出:%d 表示日期,%X{tid} SkWalking 链路追踪编号,%thread 表示线程名,%-5level级别从左显示 5 个字符宽度,%msg日志消息%n是换行符 -->
<property name="PATTERN_DEFAULT" value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%thread] [%tid] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
<!-- 控制台 Appender -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">     
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>${PATTERN_DEFAULT}</pattern>
</layout>
</encoder>
</appender>
<!-- 文件 Appender -->
<!-- 参考 Spring Boot 的 file-appender.xml 编写 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>${PATTERN_DEFAULT}</pattern>
</layout>
</encoder>
<!-- 日志文件名 -->
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 滚动后的日志文件名 -->
<fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern>
<!-- 启动服务时,是否清理历史日志,一般不建议清理 -->
<cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
<!-- 日志文件,到达多少容量,进行滚动 -->
<maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize>
<!-- 日志文件的总大小0 表示不限制 -->
<totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0}</totalSizeCap>
<!-- 日志文件的保留天数 -->
<maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30}</maxHistory>
</rollingPolicy>
</appender>
<!-- 异步写入日志,提升性能 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志。默认的,如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能。默认值为 256 -->
<queueSize>256</queueSize>
<appender-ref ref="FILE"/>
</appender>
<!-- SkyWalking GRPC 日志收集实现日志中心。注意SkyWalking 8.4.0 版本开始支持 -->
<appender name="GRPC" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>${PATTERN_DEFAULT}</pattern>
</layout>
</encoder>
</appender>
<!-- 本地环境 -->
<springProfile name="local">
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="GRPC"/> <!-- 本地环境下,如果不想接入 SkyWalking 日志服务,可以注释掉本行 -->
<appender-ref ref="ASYNC"/> <!-- 本地环境下,如果不想打印日志,可以注释掉本行 -->
</root>
</springProfile>
<!-- 其它环境 -->
<springProfile name="dev,default">
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="ASYNC"/>
<appender-ref ref="GRPC"/>
</root>
</springProfile>
</configuration>

View File

@ -1,252 +0,0 @@
package cn.iocoder.yudao.module.infra.service.config;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.framework.test.core.util.RandomUtils;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigPageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.config.vo.ConfigUpdateReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.config.ConfigDO;
import cn.iocoder.yudao.module.infra.dal.mysql.config.ConfigMapper;
import cn.iocoder.yudao.module.infra.enums.config.ConfigTypeEnum;
import cn.iocoder.yudao.module.infra.mq.producer.config.ConfigProducer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@Import(ConfigServiceImpl.class)
public class ConfigServiceTest extends BaseDbUnitTest {
@Resource
private ConfigServiceImpl configService;
@Resource
private ConfigMapper configMapper;
@MockBean
private ConfigProducer configProducer;
@Test
public void testCreateConfig_success() {
// 准备参数
ConfigCreateReqVO reqVO = randomPojo(ConfigCreateReqVO.class);
// 调用
Long configId = configService.createConfig(reqVO);
// 断言
assertNotNull(configId);
// 校验记录的属性是否正确
ConfigDO config = configMapper.selectById(configId);
assertPojoEquals(reqVO, config);
Assertions.assertEquals(ConfigTypeEnum.CUSTOM.getType(), config.getType());
// 校验调用
verify(configProducer, times(1)).sendConfigRefreshMessage();
}
@Test
public void testUpdateConfig_success() {
// mock 数据
ConfigDO dbConfig = randomConfigDO();
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
// 准备参数
ConfigUpdateReqVO reqVO = randomPojo(ConfigUpdateReqVO.class, o -> {
o.setId(dbConfig.getId()); // 设置更新的 ID
});
// 调用
configService.updateConfig(reqVO);
// 校验是否更新正确
ConfigDO config = configMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, config);
// 校验调用
verify(configProducer, times(1)).sendConfigRefreshMessage();
}
@Test
public void testDeleteConfig_success() {
// mock 数据
ConfigDO dbConfig = randomConfigDO(o -> {
o.setType(ConfigTypeEnum.CUSTOM.getType()); // 只能删除 CUSTOM 类型
});
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbConfig.getId();
// 调用
configService.deleteConfig(id);
// 校验数据不存在了
assertNull(configMapper.selectById(id));
// 校验调用
verify(configProducer, times(1)).sendConfigRefreshMessage();
}
@Test
public void testDeleteConfig_canNotDeleteSystemType() {
// mock 数据
ConfigDO dbConfig = randomConfigDO(o -> {
o.setType(ConfigTypeEnum.SYSTEM.getType()); // SYSTEM 不允许删除
});
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbConfig.getId();
// 调用, 并断言异常
assertServiceException(() -> configService.deleteConfig(id), CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE);
}
@Test
public void testCheckConfigExists_success() {
// mock 数据
ConfigDO dbConfigDO = randomConfigDO();
configMapper.insert(dbConfigDO);// @Sql: 先插入出一条存在的数据
// 调用成功
configService.checkConfigExists(dbConfigDO.getId());
}
@Test
public void testCheckConfigExist_notExists() {
assertServiceException(() -> configService.checkConfigExists(randomLongId()), CONFIG_NOT_EXISTS);
}
@Test
public void testCheckConfigKeyUnique_success() {
// 调用成功
configService.checkConfigKeyUnique(randomLongId(), randomString());
}
@Test
public void testCheckConfigKeyUnique_keyDuplicateForCreate() {
// 准备参数
String key = randomString();
// mock 数据
configMapper.insert(randomConfigDO(o -> o.setConfigKey(key)));
// 调用校验异常
assertServiceException(() -> configService.checkConfigKeyUnique(null, key),
CONFIG_KEY_DUPLICATE);
}
@Test
public void testCheckConfigKeyUnique_keyDuplicateForUpdate() {
// 准备参数
Long id = randomLongId();
String key = randomString();
// mock 数据
configMapper.insert(randomConfigDO(o -> o.setConfigKey(key)));
// 调用校验异常
assertServiceException(() -> configService.checkConfigKeyUnique(id, key),
CONFIG_KEY_DUPLICATE);
}
@Test
public void testGetConfigPage() {
// mock 数据
ConfigDO dbConfig = randomConfigDO(o -> { // 等会查询到
o.setName("芋艿");
o.setConfigKey("yunai");
o.setType(ConfigTypeEnum.SYSTEM.getType());
o.setCreateTime(buildTime(2021, 2, 1));
});
configMapper.insert(dbConfig);
// 测试 name 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setName("土豆")));
// 测试 key 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setConfigKey("tudou")));
// 测试 type 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setType(ConfigTypeEnum.CUSTOM.getType())));
// 测试 createTime 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setCreateTime(buildTime(2021, 1, 1))));
// 准备参数
ConfigPageReqVO reqVO = new ConfigPageReqVO();
reqVO.setName("");
reqVO.setKey("nai");
reqVO.setType(ConfigTypeEnum.SYSTEM.getType());
reqVO.setBeginTime(buildTime(2021, 1, 15));
reqVO.setEndTime(buildTime(2021, 2, 15));
// 调用
PageResult<ConfigDO> pageResult = configService.getConfigPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbConfig, pageResult.getList().get(0));
}
@Test
public void testGetConfigList() {
// mock 数据
ConfigDO dbConfig = randomConfigDO(o -> { // 等会查询到
o.setName("芋艿");
o.setConfigKey("yunai");
o.setType(ConfigTypeEnum.SYSTEM.getType());
o.setCreateTime(buildTime(2021, 2, 1));
});
configMapper.insert(dbConfig);
// 测试 name 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setName("土豆")));
// 测试 key 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setConfigKey("tudou")));
// 测试 type 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setType(ConfigTypeEnum.CUSTOM.getType())));
// 测试 createTime 不匹配
configMapper.insert(ObjectUtils.cloneIgnoreId(dbConfig, o -> o.setCreateTime(buildTime(2021, 1, 1))));
// 准备参数
ConfigExportReqVO reqVO = new ConfigExportReqVO();
reqVO.setName("");
reqVO.setKey("nai");
reqVO.setType(ConfigTypeEnum.SYSTEM.getType());
reqVO.setBeginTime(buildTime(2021, 1, 15));
reqVO.setEndTime(buildTime(2021, 2, 15));
// 调用
List<ConfigDO> list = configService.getConfigList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbConfig, list.get(0));
}
@Test
public void testGetConfigByKey() {
// mock 数据
ConfigDO dbConfig = randomConfigDO();
configMapper.insert(dbConfig);// @Sql: 先插入出一条存在的数据
// 准备参数
String key = dbConfig.getConfigKey();
// 调用
ConfigDO config = configService.getConfigByKey(key);
// 断言
assertNotNull(config);
assertPojoEquals(dbConfig, config);
}
// ========== 随机对象 ==========
@SafeVarargs
private static ConfigDO randomConfigDO(Consumer<ConfigDO>... consumers) {
Consumer<ConfigDO> consumer = (o) -> {
o.setType(randomEle(ConfigTypeEnum.values()).getType()); // 保证 key 的范围
};
return RandomUtils.randomPojo(ConfigDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@ -1,163 +0,0 @@
package cn.iocoder.yudao.module.infra.service.job;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogPageReqVO;
import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobLogDO;
import cn.iocoder.yudao.module.infra.dal.mysql.job.JobLogMapper;
import cn.iocoder.yudao.module.infra.enums.job.JobLogStatusEnum;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@Import(JobLogServiceImpl.class)
public class JobLogServiceTest extends BaseDbUnitTest {
@Resource
private JobLogServiceImpl jobLogService;
@Resource
private JobLogMapper jobLogMapper;
@Test
public void testCreateJobLog_success() {
// 准备参数
JobLogDO reqVO = randomPojo(JobLogDO.class, o -> {
o.setExecuteIndex(1);
});
// 调用
Long jobLogId = jobLogService.createJobLog(reqVO.getJobId(), reqVO.getBeginTime(), reqVO.getHandlerName(), reqVO.getHandlerParam(), reqVO.getExecuteIndex());
// 断言
assertNotNull(jobLogId);
// 校验记录的属性是否正确
JobLogDO job = jobLogMapper.selectById(jobLogId);
assertEquals(JobLogStatusEnum.RUNNING.getStatus(), job.getStatus());
}
@Test
public void testUpdateJobLogResultAsync_success() {
// 准备参数
JobLogDO reqVO = randomPojo(JobLogDO.class, o -> {
o.setExecuteIndex(1);
});
JobLogDO log = JobLogDO.builder().jobId(reqVO.getJobId()).handlerName(reqVO.getHandlerName()).handlerParam(reqVO.getHandlerParam()).executeIndex(reqVO.getExecuteIndex())
.beginTime(reqVO.getBeginTime()).status(JobLogStatusEnum.RUNNING.getStatus()).build();
jobLogMapper.insert(log);
// 调用
jobLogService.updateJobLogResultAsync(log.getId(), reqVO.getBeginTime(), reqVO.getDuration(), true,reqVO.getResult());
// 校验记录的属性是否正确
JobLogDO job = jobLogMapper.selectById(log.getId());
assertEquals(JobLogStatusEnum.SUCCESS.getStatus(), job.getStatus());
// 调用
jobLogService.updateJobLogResultAsync(log.getId(), reqVO.getBeginTime(), reqVO.getDuration(), false,reqVO.getResult());
// 校验记录的属性是否正确
JobLogDO job2 = jobLogMapper.selectById(log.getId());
assertEquals(JobLogStatusEnum.FAILURE.getStatus(), job2.getStatus());
}
@Test
public void testGetJobLogListByIds_success() {
// mock 数据
JobLogDO dbJobLog = randomPojo(JobLogDO.class, o -> {
o.setExecuteIndex(1);
o.setStatus(randomEle(JobLogStatusEnum.values()).getStatus()); // 保证 status 的范围
});
JobLogDO cloneJobLog = ObjectUtils.cloneIgnoreId(dbJobLog, o -> o.setHandlerName(randomString()));
jobLogMapper.insert(dbJobLog);
// 测试 handlerName 不匹配
jobLogMapper.insert(cloneJobLog);
// 准备参数
ArrayList ids = new ArrayList<>();
ids.add(dbJobLog.getId());
ids.add(cloneJobLog.getId());
// 调用
List<JobLogDO> list = jobLogService.getJobLogList(ids);
// 断言
assertEquals(2, list.size());
assertPojoEquals(dbJobLog, list.get(0));
}
@Test
public void testGetJobPage_success() {
// mock 数据
JobLogDO dbJobLog = randomPojo(JobLogDO.class, o -> {
o.setExecuteIndex(1);
o.setHandlerName("handlerName 单元测试");
o.setStatus(JobLogStatusEnum.SUCCESS.getStatus());
o.setBeginTime(buildTime(2021, 1, 8));
o.setEndTime(buildTime(2021, 1, 8));
});
jobLogMapper.insert(dbJobLog);
// 测试 jobId 不匹配
jobLogMapper.insert(ObjectUtils.cloneIgnoreId(dbJobLog, o -> o.setJobId(randomLongId())));
// 测试 handlerName 不匹配
jobLogMapper.insert(ObjectUtils.cloneIgnoreId(dbJobLog, o -> o.setHandlerName(randomString())));
// 测试 beginTime 不匹配
jobLogMapper.insert(ObjectUtils.cloneIgnoreId(dbJobLog, o -> o.setBeginTime(buildTime(2021, 1, 7))));
// 测试 endTime 不匹配
jobLogMapper.insert(ObjectUtils.cloneIgnoreId(dbJobLog, o -> o.setEndTime(buildTime(2021, 1, 9))));
// 测试 status 不匹配
jobLogMapper.insert(ObjectUtils.cloneIgnoreId(dbJobLog, o -> o.setStatus(JobLogStatusEnum.FAILURE.getStatus())));
// 准备参数
JobLogPageReqVO reqVo = new JobLogPageReqVO();
reqVo.setJobId(dbJobLog.getJobId());
reqVo.setHandlerName("单元");
reqVo.setBeginTime(dbJobLog.getBeginTime());
reqVo.setEndTime(dbJobLog.getEndTime());
reqVo.setStatus(JobLogStatusEnum.SUCCESS.getStatus());
// 调用
PageResult<JobLogDO> pageResult = jobLogService.getJobLogPage(reqVo);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbJobLog, pageResult.getList().get(0));
}
@Test
public void testGetJobListForExport_success() {
// mock 数据
JobLogDO dbJobLog = randomPojo(JobLogDO.class, o -> {
o.setExecuteIndex(1);
o.setHandlerName("handlerName 单元测试");
o.setStatus(JobLogStatusEnum.SUCCESS.getStatus());
o.setBeginTime(buildTime(2021, 1, 8));
o.setEndTime(buildTime(2021, 1, 8));
});
jobLogMapper.insert(dbJobLog);
// 测试 jobId 不匹配
jobLogMapper.insert(ObjectUtils.cloneIgnoreId(dbJobLog, o -> o.setJobId(randomLongId())));
// 测试 handlerName 不匹配
jobLogMapper.insert(ObjectUtils.cloneIgnoreId(dbJobLog, o -> o.setHandlerName(randomString())));
// 测试 beginTime 不匹配
jobLogMapper.insert(ObjectUtils.cloneIgnoreId(dbJobLog, o -> o.setBeginTime(buildTime(2021, 1, 7))));
// 测试 endTime 不匹配
jobLogMapper.insert(ObjectUtils.cloneIgnoreId(dbJobLog, o -> o.setEndTime(buildTime(2021, 1, 9))));
// 测试 status 不匹配
jobLogMapper.insert(ObjectUtils.cloneIgnoreId(dbJobLog, o -> o.setStatus(JobLogStatusEnum.FAILURE.getStatus())));
// 准备参数
JobLogExportReqVO reqVo = new JobLogExportReqVO();
reqVo.setJobId(dbJobLog.getJobId());
reqVo.setHandlerName("单元");
reqVo.setBeginTime(dbJobLog.getBeginTime());
reqVo.setEndTime(dbJobLog.getEndTime());
reqVo.setStatus(JobLogStatusEnum.SUCCESS.getStatus());
// 调用
List<JobLogDO> list = jobLogService.getJobLogList(reqVo);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbJobLog, list.get(0));
}
}

View File

@ -1,294 +0,0 @@
package cn.iocoder.yudao.module.infra.service.job;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.quartz.core.scheduler.SchedulerManager;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobCreateReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobExportReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobPageReqVO;
import cn.iocoder.yudao.module.infra.controller.admin.job.vo.job.JobUpdateReqVO;
import cn.iocoder.yudao.module.infra.convert.job.JobConvert;
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 org.junit.jupiter.api.Test;
import org.quartz.SchedulerException;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@Import(JobServiceImpl.class)
public class JobServiceTest extends BaseDbUnitTest {
@Resource
private JobServiceImpl jobService;
@Resource
private JobMapper jobMapper;
@MockBean
private SchedulerManager schedulerManager;
@Test
public void testCreateJob_cronExpressionValid() {
// 准备参数Cron 表达式为 String 类型默认随机字符串
JobCreateReqVO reqVO = randomPojo(JobCreateReqVO.class);
// 调用并断言异常
assertServiceException(() -> jobService.createJob(reqVO), JOB_CRON_EXPRESSION_VALID);
}
@Test
public void testCreateJob_jobHandlerExists() throws SchedulerException {
// 准备参数 指定 Cron 表达式
JobCreateReqVO reqVO = randomPojo(JobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
// 调用
jobService.createJob(reqVO);
// 调用并断言异常
assertServiceException(() -> jobService.createJob(reqVO), JOB_HANDLER_EXISTS);
}
@Test
public void testCreateJob_success() throws SchedulerException {
// 准备参数 指定 Cron 表达式
JobCreateReqVO reqVO = randomPojo(JobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
// 调用
Long jobId = jobService.createJob(reqVO);
// 断言
assertNotNull(jobId);
// 校验记录的属性是否正确
JobDO job = jobMapper.selectById(jobId);
assertPojoEquals(reqVO, job);
assertEquals(JobStatusEnum.NORMAL.getStatus(), job.getStatus());
// 校验调用
verify(schedulerManager, times(1)).addJob(eq(job.getId()), eq(job.getHandlerName()), eq(job.getHandlerParam()), eq(job.getCronExpression()),
eq(reqVO.getRetryCount()), eq(reqVO.getRetryInterval()));
}
@Test
public void testUpdateJob_jobNotExists(){
// 准备参数
JobUpdateReqVO reqVO = randomPojo(JobUpdateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
// 调用并断言异常
assertServiceException(() -> jobService.updateJob(reqVO), JOB_NOT_EXISTS);
}
@Test
public void testUpdateJob_onlyNormalStatus(){
// mock 数据
JobCreateReqVO createReqVO = randomPojo(JobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
JobDO job = JobConvert.INSTANCE.convert(createReqVO);
job.setStatus(JobStatusEnum.INIT.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 准备参数
JobUpdateReqVO updateReqVO = randomPojo(JobUpdateReqVO.class, o -> {
o.setId(job.getId());
o.setName(createReqVO.getName());
o.setCronExpression(createReqVO.getCronExpression());
});
// 调用并断言异常
assertServiceException(() -> jobService.updateJob(updateReqVO), JOB_UPDATE_ONLY_NORMAL_STATUS);
}
@Test
public void testUpdateJob_success() throws SchedulerException {
// mock 数据
JobCreateReqVO createReqVO = randomPojo(JobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
JobDO job = JobConvert.INSTANCE.convert(createReqVO);
job.setStatus(JobStatusEnum.NORMAL.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 准备参数
JobUpdateReqVO updateReqVO = randomPojo(JobUpdateReqVO.class, o -> {
o.setId(job.getId());
o.setName(createReqVO.getName());
o.setCronExpression(createReqVO.getCronExpression());
});
// 调用
jobService.updateJob(updateReqVO);
// 校验记录的属性是否正确
JobDO updateJob = jobMapper.selectById(updateReqVO.getId());
assertPojoEquals(updateReqVO, updateJob);
// 校验调用
verify(schedulerManager, times(1)).updateJob(eq(job.getHandlerName()), eq(updateReqVO.getHandlerParam()), eq(updateReqVO.getCronExpression()),
eq(updateReqVO.getRetryCount()), eq(updateReqVO.getRetryInterval()));
}
@Test
public void testUpdateJobStatus_changeStatusInvalid() {
// 调用并断言异常
assertServiceException(() -> jobService.updateJobStatus(1L, JobStatusEnum.INIT.getStatus()), JOB_CHANGE_STATUS_INVALID);
}
@Test
public void testUpdateJobStatus_changeStatusEquals() {
// mock 数据
JobCreateReqVO createReqVO = randomPojo(JobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
JobDO job = JobConvert.INSTANCE.convert(createReqVO);
job.setStatus(JobStatusEnum.NORMAL.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 调用并断言异常
assertServiceException(() -> jobService.updateJobStatus(job.getId(), job.getStatus()), JOB_CHANGE_STATUS_EQUALS);
}
@Test
public void testUpdateJobStatus_NormalToStop_success() throws SchedulerException {
// mock 数据
JobCreateReqVO createReqVO = randomPojo(JobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
JobDO job = JobConvert.INSTANCE.convert(createReqVO);
job.setStatus(JobStatusEnum.NORMAL.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 调用
jobService.updateJobStatus(job.getId(), JobStatusEnum.STOP.getStatus());
// 校验记录的属性是否正确
JobDO updateJob = jobMapper.selectById(job.getId());
assertEquals(JobStatusEnum.STOP.getStatus(), updateJob.getStatus());
// 校验调用
verify(schedulerManager, times(1)).pauseJob(eq(job.getHandlerName()));
}
@Test
public void testUpdateJobStatus_StopToNormal_success() throws SchedulerException {
// mock 数据
JobCreateReqVO createReqVO = randomPojo(JobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
JobDO job = JobConvert.INSTANCE.convert(createReqVO);
job.setStatus(JobStatusEnum.STOP.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 调用
jobService.updateJobStatus(job.getId(), JobStatusEnum.NORMAL.getStatus());
// 校验记录的属性是否正确
JobDO updateJob = jobMapper.selectById(job.getId());
assertEquals(JobStatusEnum.NORMAL.getStatus(), updateJob.getStatus());
// 校验调用
verify(schedulerManager, times(1)).resumeJob(eq(job.getHandlerName()));
}
@Test
public void testTriggerJob_success() throws SchedulerException {
// mock 数据
JobCreateReqVO createReqVO = randomPojo(JobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
JobDO job = JobConvert.INSTANCE.convert(createReqVO);
job.setStatus(JobStatusEnum.NORMAL.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 调用
jobService.triggerJob(job.getId());
// 校验调用
verify(schedulerManager, times(1)).triggerJob(eq(job.getId()), eq(job.getHandlerName()), eq(job.getHandlerParam()));
}
@Test
public void testDeleteJob_success() throws SchedulerException {
// mock 数据
JobCreateReqVO createReqVO = randomPojo(JobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
JobDO job = JobConvert.INSTANCE.convert(createReqVO);
job.setStatus(JobStatusEnum.NORMAL.getStatus());
fillJobMonitorTimeoutEmpty(job);
jobMapper.insert(job);
// 调用 UPDATE inf_job SET deleted=1 WHERE id=? AND deleted=0
jobService.deleteJob(job.getId());
// 校验数据不存在了 WHERE id=? AND deleted=0 查询为空正常
assertNull(jobMapper.selectById(job.getId()));
// 校验调用
verify(schedulerManager, times(1)).deleteJob(eq(job.getHandlerName()));
}
@Test
public void testGetJobListByIds_success() {
// mock 数据
JobDO dbJob = randomPojo(JobDO.class, o -> {
o.setStatus(randomEle(JobStatusEnum.values()).getStatus()); // 保证 status 的范围
});
JobDO cloneJob = ObjectUtils.cloneIgnoreId(dbJob, o -> o.setHandlerName(randomString()));
jobMapper.insert(dbJob);
// 测试 handlerName 不匹配
jobMapper.insert(cloneJob);
// 准备参数
ArrayList<Long> ids = new ArrayList<>();
ids.add(dbJob.getId());
ids.add(cloneJob.getId());
// 调用
List<JobDO> list = jobService.getJobList(ids);
// 断言
assertEquals(2, list.size());
assertPojoEquals(dbJob, list.get(0));
}
@Test
public void testGetJobPage_success() {
// mock 数据
JobDO dbJob = randomPojo(JobDO.class, o -> {
o.setName("定时任务测试");
o.setHandlerName("handlerName 单元测试");
o.setStatus(JobStatusEnum.INIT.getStatus());
});
jobMapper.insert(dbJob);
// 测试 name 不匹配
jobMapper.insert(ObjectUtils.cloneIgnoreId(dbJob, o -> o.setName("土豆")));
// 测试 status 不匹配
jobMapper.insert(ObjectUtils.cloneIgnoreId(dbJob, o -> o.setStatus(JobStatusEnum.NORMAL.getStatus())));
// 测试 handlerName 不匹配
jobMapper.insert(ObjectUtils.cloneIgnoreId(dbJob, o -> o.setHandlerName(randomString())));
// 准备参数
JobPageReqVO reqVo = new JobPageReqVO();
reqVo.setName("定时");
reqVo.setStatus(JobStatusEnum.INIT.getStatus());
reqVo.setHandlerName("单元");
// 调用
PageResult<JobDO> pageResult = jobService.getJobPage(reqVo);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbJob, pageResult.getList().get(0));
}
@Test
public void testGetJobListForExport_success() {
// mock 数据
JobDO dbJob = randomPojo(JobDO.class, o -> {
o.setName("定时任务测试");
o.setHandlerName("handlerName 单元测试");
o.setStatus(JobStatusEnum.INIT.getStatus());
});
jobMapper.insert(dbJob);
// 测试 name 不匹配
jobMapper.insert(ObjectUtils.cloneIgnoreId(dbJob, o -> o.setName("土豆")));
// 测试 status 不匹配
jobMapper.insert(ObjectUtils.cloneIgnoreId(dbJob, o -> o.setStatus(JobStatusEnum.NORMAL.getStatus())));
// 测试 handlerName 不匹配
jobMapper.insert(ObjectUtils.cloneIgnoreId(dbJob, o -> o.setHandlerName(randomString())));
// 准备参数
JobExportReqVO reqVo = new JobExportReqVO();
reqVo.setName("定时");
reqVo.setStatus(JobStatusEnum.INIT.getStatus());
reqVo.setHandlerName("单元");
// 调用
List<JobDO> list = jobService.getJobList(reqVo);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbJob, list.get(0));
}
private static void fillJobMonitorTimeoutEmpty(JobDO job) {
if (job.getMonitorTimeout() == null) {
job.setMonitorTimeout(0);
}
}
}

View File

@ -1,6 +1,3 @@
server:
port: 48081
--- #################### 数据库相关配置 ####################
spring:

View File

@ -4,3 +4,6 @@ spring:
profiles:
active: local
server:
port: 48081