1. 程式人生 > >[外包]!採用asp.net core 快速構建小型創業公司後臺管理系統(四.quartz 簡單配置使用)

[外包]!採用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">&times;</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開發的青春歲月,晚安!