上一章介绍了log4net的简单配置,这一章介绍一下quartz的简单使用,下一章介绍一下我得权限使用,然后就结束
本章主要介绍:
- quartz在asp.net core中的使用
这个项目虽小,但是及其容易扩展,后面的业务直接能丢进来,使其更加丰富
废话不说开始介绍
一.基础类配置
在domain里定义IJobCenter接口
代码如下:
public interface IJobCenter
{
/// <summary>
/// 添加定时任务
/// </summary>
/// <param name="m"></param>
/// <returns></returns>
Task<Result> AddScheduleJobAsync(TaskScheduleModel m);
/// <summary>
/// 暂停定时任务
/// </summary>
/// <param name="jobGroup"></param>
/// <param name="jobName"></param>
/// <returns></returns>
Task<Result> StopScheduleJobAsync(string jobGroup, string jobName);
/// <summary>
/// 恢复定时任务
/// </summary>
/// <param name="jobGroup"></param>
/// <param name="jobName"></param>
/// <returns></returns>
Task<Result> RunScheduleJobAsync(TaskScheduleModel m);
}
infrastructure里实现IJobCenter
记得引入quartz的nuget
代码如下:
/// <summary>
/// 任务调度中心
/// </summary>
public class JobCenter:IJobCenter
{
public static IScheduler scheduler = null;
public static async Task<IScheduler> GetSchedulerAsync()
{
if (scheduler != null)
{
return scheduler;
}
else
{
ISchedulerFactory schedf = new StdSchedulerFactory();
IScheduler sched = await schedf.GetScheduler();
return sched;
}
}
/// <summary>
/// 添加任务计划//或者进程终止后的开启
/// </summary>
/// <returns></returns>
public async Task<Result> AddScheduleJobAsync(TaskScheduleModel m)
{
Result result = new Result();
try
{
if (m != null)
{
DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(m.StarRunTime, 1);
DateTimeOffset endRunTime = DateBuilder.NextGivenSecondDate(m.EndRunTime, 1);
scheduler = await GetSchedulerAsync();
IJobDetail job = JobBuilder.Create<HttpJob>()
.WithIdentity(m.JobName, m.JobGroup)
.Build();
ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create()
.StartAt(starRunTime)
.EndAt(endRunTime)
.WithIdentity(m.JobName, m.JobGroup)
.WithCronSchedule(m.CronExpress)
.Build();
await scheduler.ScheduleJob(job, trigger);
await scheduler.Start();
result.Data = m;
return result;
}
return result.SetError("传入实体为空");
}
catch (Exception ex)
{
Log4Net.Error($"[JobCenter_AddScheduleJobAsync]_{ex}");
return result.SetError(ex.Message);
}
}
/// <summary>
/// 暂停指定任务计划
/// </summary>
/// <returns></returns>
public async Task<Result> StopScheduleJobAsync(string jobGroup, string jobName)
{
Result result = new Result();
try
{
scheduler = await GetSchedulerAsync();
//使任务暂停
await scheduler.PauseJob(new JobKey(jobName, jobGroup));
var status = new StatusViewModel()
{
Status = 0,
Msg = "暂停任务计划成功",
};
result.Data = status.GetJson();
return result;
}
catch (Exception ex)
{
Log4Net.Error($"[JobCenter_StopScheduleJobAsync]_{ex}");
var status = new StatusViewModel()
{
Status = -1,
Msg = "暂停任务计划失败",
};
result.Data = status.GetJson();
return result;
}
}
/// <summary>
/// 恢复指定的任务计划**恢复的是暂停后的任务计划,如果是程序奔溃后 或者是进程杀死后的恢复,此方法无效
/// </summary>
/// <returns></returns>
public async Task<Result> RunScheduleJobAsync(TaskScheduleModel sm)
{
Result result = new Result();
try
{
#region 开任务
//scheduler = await GetSchedulerAsync();
//DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(sm.StarRunTime, 1);
//DateTimeOffset endRunTime = DateBuilder.NextGivenSecondDate(sm.EndRunTime, 1);
//IJobDetail job = JobBuilder.Create<HttpJob>()
// .WithIdentity(sm.JobName, sm.JobGroup)
// .Build();
//ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create()
// .StartAt(starRunTime)
// .EndAt(endRunTime)
// .WithIdentity(sm.JobName, sm.JobGroup)
// .WithCronSchedule(sm.CronExpress)
// .Build();
//await scheduler.ScheduleJob(job, trigger);
//await scheduler.Start();
#endregion
scheduler = await GetSchedulerAsync();
//resumejob 恢复
await scheduler.ResumeJob(new JobKey(sm.JobName, sm.JobGroup));
var status = new StatusViewModel()
{
Status = 0,
Msg = "恢复任务计划成功",
};
result.Data = status.GetJson();
return result;
}
catch (Exception ex)
{
Log4Net.Error($"[JobCenter_RunScheduleJobAsync]_{ex}");
var status = new StatusViewModel()
{
Status = -1,
Msg = "恢复任务计划失败",
};
result.Data = status.GetJson();
return result;
}
}
}
写一个JobServiceExtensions拓展,其中里面有一个AddJobSerivce,用于在startup里添加启动服务
这个拓展的主要作用是:程序启动,自动查询我的任务调度管理表,查询里面的状态是开启中的任务,并将其添加到定时任务中
表结构大致如下:
这里我们主要注意任务组和任务名,这两个何在一起,能顶一个唯一的任务,后面我们会在httpjob里根据不同的的任务组和任务名
区分他们要执行的不同的逻辑
代码很简单:如下
public class HttpJob : IJob
{
/// <summary>
/// 通过group和name判断是要执行哪个任务 具体(任务执行逻辑)业务逻辑写后面
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Execute(IJobExecutionContext context)
{
await Task.Run(() =>
{
var name = context.JobDetail.Key.Name;
var group = context.JobDetail.Key.Group;
if (group=="xx1"&&name=="xx2")
{
//do something
}
if (group == "xx2" && name == "xx3")
{
//do something also
}
Log4Net.Info($"执行任务_Name:{name}_Grop:{group}");
});
}
}
在domain里新增ITaskService接口,如下
代码如下:
public interface ITaskService
{
//获取任务列表
Result GetTaskList();
//添加任务
Task<Result> AddTaskAsync(TaskScheduleModel model);
//修改任务
Task<Result> ModifyTaskAsync(TaskScheduleModel model);
//删除任务
Task<Result> DelTaskAsync(TaskScheduleModel model);
//暂停任务
Result PauseTask();
//开启任务
Result StartTask();
}
在application 里实现这个接口
代码如下:
public class TaskService : BaseService, ITaskService
{
private IJobCenter jobCenter = ServiceCollectionExtension.Get<IJobCenter>();
private MvcIdentity identity = (ServiceCollectionExtension.HttpContext.User.Identity as MvcIdentity);
public async Task<Result> AddTaskAsync(TaskScheduleModel model)
{
Result result = new Result();
if (model == null)
{
return result.SetError("Model 不能为空!");
}
if (string.IsNullOrEmpty(model.JobName))
{
return result.SetError("JobName 不能为空!");
}
if (string.IsNullOrEmpty(model.JobGroup))
{
return result.SetError("JobGroup 不能为空!");
}
if (model.StarRunTime == null)
{
model.StarRunTime = DateTime.Now;
}
if (model.EndRunTime == null)
{
model.EndRunTime = DateTime.MaxValue.AddDays(-1);
}
var info = await jobCenter.AddScheduleJobAsync(model);
if (info.Status != 200)
{
return result.SetError(info.Message);
}
base.Add(new TaskSchedule
{
Id = Guid.NewGuid().ToString("N"),
CreateAuthr = identity.Name,
CronExpress = model.CronExpress,
EndRunTime = model.EndRunTime,
StarRunTime = model.StarRunTime,
JobGroup = model.JobGroup,
JobName = model.JobName,
RunStatus = model.RunStatus
}, true);
return result;
}
public async Task<Result> DelTaskAsync(TaskScheduleModel model)
{
Result result = new Result();
if (model == null)
{
return result.SetError("Model 不能为空!");
}
if (string.IsNullOrEmpty(model.JobName))
{
return result.SetError("JobName 不能为空!");
}
if (string.IsNullOrEmpty(model.JobGroup))
{
return result.SetError("JobGroup 不能为空!");
}
//search
var taskInfo = base.Single<TaskSchedule>(t => t.Id.Equals(model.Id));
var info = await jobCenter.StopScheduleJobAsync(model.JobGroup, model.JobName);
if (!info.Status.Equals(200))
{
return result.SetError(info.Message);
}
//del
base.Delete(taskInfo, true);
return result;
}
public Result GetTaskList()
{
Result result = new Result();
var query = base.Where<TaskSchedule>(t => !t.RunStatus.Equals(TaskJobStatus.JobHasDel)).Select(t => new
{
t.Id,
t.JobGroup,
t.JobName,
t.RunStatus,
t.StarRunTime,
t.UpdateTime,
t.CronExpress,
t.EndRunTime,
t.CreateTime
}).ToList();
List<TaskScheduleModel> _list = new List<TaskScheduleModel>();
query.ForEach(t =>
{
_list.Add(new TaskScheduleModel()
{
Id = t.Id,
JobGroup = t.JobGroup,
JobName = t.JobName,
_RunStatus = ((TaskJobStatus)t.RunStatus).GetString(),
StarRunTime = t.StarRunTime,
UpdateTime = t.UpdateTime,
CronExpress = t.CronExpress,
EndRunTime = t.EndRunTime,
CreateTime = t.CreateTime
});
});
result.Data = _list;
return result;
}
public async Task<Result> ModifyTaskAsync(TaskScheduleModel model)
{
Result result = new Result();
if (model == null)
{
return result.SetError("Model 不能为空!");
}
if (string.IsNullOrEmpty(model.JobName))
{
return result.SetError("JobName 不能为空!");
}
if (string.IsNullOrEmpty(model.JobGroup))
{
return result.SetError("JobGroup 不能为空!");
}
if (model.StarRunTime == null)
{
model.StarRunTime = DateTime.Now;
}
if (model.EndRunTime == null)
{
model.EndRunTime = DateTime.MaxValue.AddDays(-1);
}
//modify
var taskInfo = base.Single<TaskSchedule>(t => t.Id.Equals(model.Id));
if (taskInfo == null)
{
return result.SetError("无此任务!");
}
//cron
if (!taskInfo.CronExpress.Equals(model.CronExpress))
{
taskInfo.CronExpress = model.CronExpress;
var stopInfo = await jobCenter.StopScheduleJobAsync(model.JobGroup, model.JobName);
if (!stopInfo.Status.Equals(200))
{
return result.SetError(stopInfo.Message);
}
var info = await jobCenter.AddScheduleJobAsync(model);
if (!stopInfo.Status.Equals(200))
{
return result.SetError(info.Message);
}
}
//状态
if (!taskInfo.RunStatus.Equals(model.RunStatus))
{
taskInfo.RunStatus = model.RunStatus;
if (model.RunStatus != (int)TaskJobStatus.PauseJob)
{
var stopInfo = await jobCenter.StopScheduleJobAsync(model.JobGroup, model.JobName);
if (!stopInfo.Status.Equals(200))
{
return result.SetError(stopInfo.Message);
}
var info = await jobCenter.AddScheduleJobAsync(model);
if (!stopInfo.Status.Equals(200))
{
return result.SetError(info.Message);
}
}
else
{
var stopInfo = await jobCenter.StopScheduleJobAsync(model.JobGroup, model.JobName);
if (!stopInfo.Status.Equals(200))
{
return result.SetError(stopInfo.Message);
}
}
}
//
taskInfo.CreateAuthr = identity.Name;
taskInfo.UpdateTime = DateTime.Now;
taskInfo.EndRunTime = model.EndRunTime;
taskInfo.StarRunTime = model.StarRunTime;
taskInfo.JobGroup = model.JobGroup;
taskInfo.JobName = model.JobName;
base.Update(taskInfo, true);
return result;
}
public Result PauseTask()
{
throw new NotImplementedException();
}
public Result StartTask()
{
throw new NotImplementedException();
}
}
接着我们构建web页面
在website里创建taskinfo控制器
代码很简单只是做了一个跳转
创建视图
代码如下:
@{
ViewData["title"] = "定时任务管理";
}
<div class="row search">
<div class="form-inline">
<input type="text" class="form-control" placeholder="任务名称" ng-model="search.username" />
</div>
<button class="btn btn-primary" type="button" ng-click="pagechanged(true)">搜索</button>
<button class="btn btn-primary" type="button" ng-click="addTask()">添加任务</button>
<button class="btn btn-primary" type="button" csv-header="getHeader()" ng-csv="_getCsv()" filename="taskList.csv" add-bom="true">导出</button>
</div>
@*弹出框*@
<div class="modal fade in" id="addmodel" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="mymodallabel">
<div class="modal-dialog modal-lg" role="document" style="height:100%">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">close</span></button>
<h4>计划任务管理</h4>
</div>
<div class="modal-body" ng-form="mymodel">
<div class="row">
<div class="form-group col-sm-6">
<label class="control-label col-sm-4">任务组:</label>
<div class="col-sm-8">
<input type="text" class="form-control" ng-model="task.jobGroup" required placeholder="任务组" />
</div>
</div>
<div class="form-group col-sm-6">
<label class="control-label col-sm-4">任务名称:</label>
<div class="col-sm-8">
<input type="text" class="form-control" ng-model="task.jobName" required placeholder="任务名称" />
</div>
</div>
<div class="form-group col-sm-6">
<label class="control-label col-sm-4">CronExpress:</label>
<div class="col-sm-8">
<input type="text" class="form-control" ng-model="task.cronExpress" required placeholder="CronExpress" />
</div>
</div>
<div class="form-group col-sm-6">
<label class="control-label col-sm-4">运行状态:</label>
<div class="col-sm-8">
<select class="form-control" required ng-model="task.runStatus">
@*<option value="1">全部</option>*@
<option value="1" selected>执行任务中</option>
<option value="2">暂停任务中</option>
<option value="4">任务关闭</option>
</select>
</div>
</div>
<div class="form-group col-sm-6">
<label class="control-label col-sm-4">开始时间:</label>
<div class="col-sm-8">
<input type="text" class="form-control mydatetimepicker" ng-model="task.starRunTime" ng-max-date="task.starRunTime||defaultDate" placeholder="开始日期" />
</div>
</div>
<div class="form-group col-sm-6">
<label class="control-label col-sm-4">结束时间:</label>
<div class="col-sm-8">
<input type="text" class="form-control mydatetimepicker" ng-model="task.endRunTime" ng-min-date="task.starRunTime" ng-max-date="defaultDate" placeholder="结束日期" />
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" ng-disabled="mymodel.$invalid" ng-click="saveTask()">保 存</button>
</div>
</div>
</div>
</div>
<table class="table table-hover table-condensed table-bordered" style="width:auto;">
<thead>
<tr style="height: 35px;">
<th>任务组</th>
<th>任务</th>
<th>CronExpress</th>
<th>开始时间</th>
<th>结束时间</th>
<th>运行状态</th>
<th>创建时间</th>
<th>修改时间</th>
<th>操作</th>
</tr>
</thead>
<tbody class="wait-loaded">
<tr ng-repeat="m in list track by m.id">
<td>{{m.jobGroup}}</td>
<td>{{m.jobName}}</td>
<td>{{m.cronExpress}}</td>
<td>{{m.starRunTime}}</td>
<td>{{m.endRunTime}}</td>
<td>{{m._RunStatus}}</td>
<td>{{m.createTime}}</td>
<td>{{m.updateTime}}</td>
<td>
<button class="btn btn-success" ng-click="edit(m)">编辑</button>
<button class="btn btn-danger" ng-click="delete(m)">删除</button>
</td>
</tr>
</tbody>
</table>
<div class="mypager wait-loaded">
<ul uib-pagination total-items="search.recordcount" items-per-page="search.pagesize" ng-change="pagechanged()" ng-model="search.pageindex" max-size="7"
class="pagination-sm" boundary-links="true" num-pages="search.numpages" boundary-link-numbers="true" first-text="首页" last-text="末页" previous-text="上一页" next-text="下一页"></ul>
<div class="recordcount">共 {{search.recordcount}} 条</div>
</div>
@section scripts{
<script type="text/javascript">
$angular.add(function ($scope, $query, $timeout, $sce) {
$scope.getHeader = function () { return ["任务组", "任务", "CronExpress", "开始时间", "结束时间", "运行状态", "创建时间", "修改时间"] };
$scope._getCsv = function () {
var model = _.cloneDeep($scope.search);
model.pageSize = 10000000;
var promise = $query.post("/webapi/GetTaskList", model, function (response) {
var objList = angular.fromJson(response.data);
var getArray = [];
_.forEach(objList, function (item) {
getArray.push({
"jobGroup": item.jobGroup,
"jobName": item.jobName,
"cronExpress": item.cronExpress,
"starRunTime": item.starRunTime,
"endRunTime": item.endRunTime,
"_RunStatus": item._RunStatus,
"createTime": item.createTime,
"updateTime": item.updateTime
});
});
return getArray;
});
return promise;
};
$scope.search = { pageindex: 1, pagesize: 10, gender: "-1", order: 1, status: "-1" };
$scope.setorder = function (index) {
if (index == math.abs($scope.search.order)) {
index = -$scope.search.order;
}
$scope.search.order = index;
$scope.pagechanged(true);
};
//查询
$scope.pagechanged = function (reindex) {
if (reindex) {
$scope.search.pageindex = 1;
}
$query.post("/webapi/GetTaskList", $scope.search, function (response) {
if (response.status === 200) {
$scope.list = response.data;
$scope.search.recordcount = response.recordcount;
} else {
$alert(response.message);
}
});
};
$scope.pagechanged();
//添加任务
$scope.addTask = function () {
$("#addmodel").modal("show");
$scope.task = { id: -1 };
};
//保存
$scope.saveTask = function () {
$query.post("/webapi/AddTaskAsync", $scope.task, function (response) {
$("#addmodel").modal("hide");
$scope.pagechanged();
});
};
$scope.edit = function (m) {
$("#addmodel").modal("show");
m.id = m.id;
$scope.task = m;
}
//删除类型
$scope.delete = function (m) {
m.id = m.id;
$scope.task = m;
$Confirm("你确认删除吗?该操作不可恢复!", function () {
$query.post("/webapi/DelTaskAsync", $scope.task, function (response) {
$scope.pagechanged();
});
});
}
});
</script>
}
之后效果就出来了
这个table会调用一个webapi接口获取数据
这个webapi里我们直接调用我们改才ITaskService里分装的方法
这里代码如下:
#region 计划任务模块
[HttpPost]
public Result GetTaskList()
{
return _taskService.GetTaskList();
}
[HttpPost]
public async Task<Result> AddTaskAsync([FromBody]TaskScheduleModel model)
{
if (model != null && !string.IsNullOrEmpty(model.Id)&&!model.Id.Equals("-1"))
{
return await _taskService.ModifyTaskAsync(model);
}
return await _taskService.AddTaskAsync(model);
}
public async Task<Result> DelTaskAsync([FromBody]TaskScheduleModel model)
{
return await _taskService.DelTaskAsync(model);
}
#endregion
二.运行测试
换得添加服务哦
算了,这个运行测试截图不好说明,明天录一个视频说明一下,今天就到这里
下一章主要说一下权限这块的东西,本来想着今天就写完的,但是有点困了,
此文章献给我做一年半asp.net core开发的青春岁月,晚安!
来源:oschina
链接:https://my.oschina.net/u/4386188/blog/4274441