[外包]!採用asp.net core 快速構建小型創業公司後臺管理系統(四.quartz 簡單配置使用)
上一章介紹了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(); } }View Code
接著我們構建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> }View Code
之後效果就出來了
這個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開發的青春歲月,晚安!