using iWare.Wms.Core; using Furion; using Furion.DatabaseAccessor; using Furion.DatabaseAccessor.Extensions; using Furion.DependencyInjection; using Furion.DynamicApiController; using Furion.FriendlyException; using Furion.JsonSerialization; using Furion.RemoteRequest.Extensions; using Furion.TaskScheduler; using Mapster; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Reflection; namespace iWare.Wms.Application { /// /// 任务调度服务 /// [ApiDescriptionSettings(Name = "Timer", Order = 100)] [Route("api")] public class SysTimerService : ISysTimerService, IDynamicApiController, IScoped { private readonly IRepository _sysTimerRep; // 任务表仓储 private readonly ISysCacheService _cache; public SysTimerService(IRepository sysTimerRep, ISysCacheService cache) { _sysTimerRep = sysTimerRep; _cache = cache; } /// /// 分页获取任务列表 /// /// /// [HttpGet("sysTimers/page")] public async Task> GetTimerPageList([FromQuery] JobPageInput input) { var workers = SpareTime.GetWorkers().ToList(); var timers = await _sysTimerRep.DetachedEntities .Where(!string.IsNullOrEmpty(input.JobName?.Trim()), u => EF.Functions.Like(u.JobName, $"%{input.JobName.Trim()}%")) .ProjectToType() .ToADPagedListAsync(input.PageNo, input.PageSize); timers.Rows.ToList().ForEach(u => { var timer = workers.FirstOrDefault(m => m.WorkerName == u.JobName); if (timer != null) { u.TimerStatus = timer.Status; u.RunNumber = timer.Tally; u.Exception = JSON.Serialize(timer.Exception); } }); return timers; } /// /// 获取所有本地任务 /// /// [HttpGet("sysTimers/localJobList")] public async Task> GetLocalJobList() { // 获取本地所有任务方法 return await GetTaskMethods(); } /// /// 增加任务 /// /// /// [HttpPost("sysTimers/add")] public async Task AddTimer(AddJobInput input) { var isExist = await _sysTimerRep.AnyAsync(u => u.JobName == input.JobName, false); if (isExist) throw Oops.Oh(ErrorCode.D1100); var timer = input.Adapt(); await _sysTimerRep.InsertAsync(timer); // 添加到任务调度里 AddTimerJob(input); } /// /// 删除任务 /// /// /// [HttpPost("sysTimers/delete")] public async Task DeleteTimer(DeleteJobInput input) { var timer = await _sysTimerRep.FirstOrDefaultAsync(u => u.Id == input.Id); if (timer == null) throw Oops.Oh(ErrorCode.D1101); await timer.DeleteAsync(); // 从调度器里取消 SpareTime.Cancel(timer.JobName); } /// /// 修改任务 /// /// /// [HttpPost("sysTimers/edit")] public async Task UpdateTimber(UpdateJobInput input) { // 排除自己并且判断与其他是否相同 var isExist = await _sysTimerRep.AnyAsync(u => u.JobName == input.JobName && u.Id != input.Id, false); if (isExist) throw Oops.Oh(ErrorCode.D1100); // 先从调度器里取消 var oldTimer = await _sysTimerRep.FirstOrDefaultAsync(u => u.Id == input.Id, false); SpareTime.Cancel(oldTimer.JobName); var timer = input.Adapt(); await timer.UpdateAsync(ignoreNullValues: true); var addJobInput = input.Adapt(); // 再添加到任务调度里 AddTimerJob(addJobInput); } /// /// 查看任务 /// /// /// [HttpGet("sysTimers/detail")] public async Task GetTimer([FromQuery] QueryJobInput input) { return await _sysTimerRep.DetachedEntities.FirstOrDefaultAsync(u => u.Id == input.Id); } /// /// 停止任务 /// /// /// [HttpPost("sysTimers/stop")] public void StopTimerJob(StopJobInput input) { SpareTime.Stop(input.JobName); } /// /// 启动任务 /// /// /// [HttpPost("sysTimers/start")] public void StartTimerJob(AddJobInput input) { var timer = SpareTime.GetWorkers().ToList().Find(u => u.WorkerName == input.JobName); if (timer == null) AddTimerJob(input); // 如果 StartNow 为 flase , 执行 AddTimerJob 并不会启动任务 SpareTime.Start(input.JobName); } /// /// 新增定时任务 /// /// [NonAction] public void AddTimerJob(AddJobInput input) { Func action = null; switch (input.RequestType) { // 创建本地方法委托 case RequestTypeEnum.Run: { // 查询符合条件的任务方法 var taskMethod = GetTaskMethods()?.Result.FirstOrDefault(m => m.RequestUrl == input.RequestUrl); if (taskMethod == null) break; // 创建任务对象 var typeInstance = Activator.CreateInstance(taskMethod.DeclaringType); // 创建委托 action = (Func)Delegate.CreateDelegate(typeof(Func), typeInstance, taskMethod.MethodName); break; } // 创建网络任务委托 default: { action = async (_, _) => { var requestUrl = input.RequestUrl.Trim(); requestUrl = requestUrl?.IndexOf("http") == 0 ? requestUrl : "http://" + requestUrl; var requestParameters = input.RequestParameters; var headersString = input.Headers; var headers = string.IsNullOrEmpty(headersString) ? null : JSON.Deserialize>(headersString); switch (input.RequestType) { case RequestTypeEnum.Get: await requestUrl.SetHeaders(headers).GetAsync(); break; case RequestTypeEnum.Post: await requestUrl.SetHeaders(headers).SetQueries(requestParameters).PostAsync(); break; case RequestTypeEnum.Put: await requestUrl.SetHeaders(headers).SetQueries(requestParameters).PutAsync(); break; case RequestTypeEnum.Delete: await requestUrl.SetHeaders(headers).DeleteAsync(); break; } }; break; } } if (action == null) return; // 缓存任务配置参数,以供任务运行时读取 if (input.RequestType == RequestTypeEnum.Run) { var jobParametersName = $"{input.JobName}_Parameters"; var jobParameters = _cache.Exists(jobParametersName); var requestParametersIsNull = string.IsNullOrEmpty(input.RequestParameters); // 如果没有任务配置却又存在缓存,则删除缓存 if (requestParametersIsNull && jobParameters) _cache.RemoveAsync(jobParametersName); else if (!requestParametersIsNull) _cache.SetAsync(jobParametersName, JSON.Deserialize>(input.RequestParameters)); } // 创建定时任务 switch (input.TimerType) { case SpareTimeTypes.Interval: if (input.DoOnce) SpareTime.DoOnce((int)input.Interval * 1000, action, input.JobName, input.Remark, input.StartNow, executeType: input.ExecuteType); else SpareTime.Do((int)input.Interval * 1000, action, input.JobName, input.Remark, input.StartNow, executeType: input.ExecuteType); break; case SpareTimeTypes.Cron: SpareTime.Do(input.Cron, action, input.JobName, input.Remark, input.StartNow, executeType: input.ExecuteType); break; } } /// /// 启动自启动任务 /// [NonAction] public void StartTimerJob() { var sysTimerList = _sysTimerRep.DetachedEntities.Where(t => t.StartNow).ProjectToType().ToList(); sysTimerList.ForEach(AddTimerJob); } /// /// 获取所有本地任务 /// /// [NonAction] public async Task> GetTaskMethods() { // 有缓存就返回缓存 var taskMethods = await _cache.GetAsync>(CommonConst.CACHE_KEY_TIMER_JOB); if (taskMethods != null) return taskMethods; // 获取所有本地任务方法,必须有spareTimeAttribute特性 taskMethods = App.EffectiveTypes .Where(u => u.IsClass && !u.IsInterface && !u.IsAbstract && typeof(ISpareTimeWorker).IsAssignableFrom(u)) .SelectMany(u => u.GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(m => m.IsDefined(typeof(SpareTimeAttribute), false) && m.GetParameters().Length == 2 && m.GetParameters()[0].ParameterType == typeof(SpareTimer) && m.GetParameters()[1].ParameterType == typeof(long) && (m.ReturnType == typeof(void) || m.ReturnType == typeof(Task))) .Select(m => { // 默认获取第一条任务特性 var spareTimeAttribute = m.GetCustomAttribute(); return new TaskMethodInfo { JobName = spareTimeAttribute.WorkerName, RequestUrl = $"{m.DeclaringType.Name}/{m.Name}", Cron = spareTimeAttribute.CronExpression, DoOnce = spareTimeAttribute.DoOnce, ExecuteType = spareTimeAttribute.ExecuteType, Interval = (int)spareTimeAttribute.Interval / 1000, StartNow = spareTimeAttribute.StartNow, RequestType = RequestTypeEnum.Run, Remark = spareTimeAttribute.Description, TimerType = string.IsNullOrEmpty(spareTimeAttribute.CronExpression) ? SpareTimeTypes.Interval : SpareTimeTypes.Cron, MethodName = m.Name, DeclaringType = m.DeclaringType }; })); await _cache.SetAsync(CommonConst.CACHE_KEY_TIMER_JOB, taskMethods); return taskMethods; } } }