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;
}
}
}