SysTimerService.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. using Furion;
  2. using Furion.DependencyInjection;
  3. using Furion.DynamicApiController;
  4. using Furion.FriendlyException;
  5. using Furion.JsonSerialization;
  6. using Furion.RemoteRequest.Extensions;
  7. using Furion.TaskScheduler;
  8. using Mapster;
  9. using Microsoft.AspNetCore.Mvc;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Linq;
  13. using System.Reflection;
  14. using System.Threading.Tasks;
  15. namespace Admin.NET.Core.Service
  16. {
  17. /// <summary>
  18. /// 定时任务服务
  19. /// </summary>
  20. [ApiDescriptionSettings(Name = "定时任务", Order = 188)]
  21. public class SysTimerService : IDynamicApiController, ITransient
  22. {
  23. private readonly SqlSugarRepository<SysTimer> _sysTimerRep;
  24. private readonly ISysCacheService _sysCacheService;
  25. public SysTimerService(SqlSugarRepository<SysTimer> sysTimerRep,
  26. ISysCacheService sysCacheService)
  27. {
  28. _sysTimerRep = sysTimerRep;
  29. _sysCacheService = sysCacheService;
  30. }
  31. /// <summary>
  32. /// 获取任务分页列表
  33. /// </summary>
  34. /// <param name="input"></param>
  35. /// <returns></returns>
  36. [HttpGet("/sysTimer/pageList")]
  37. public async Task<SqlSugarPagedList<TimerOutput>> GetTimerPageList([FromQuery] PageTimerInput input)
  38. {
  39. var workers = SpareTime.GetWorkers().ToList();
  40. var timers = await _sysTimerRep.AsQueryable()
  41. .WhereIF(!string.IsNullOrWhiteSpace(input.TimerName), u => u.TimerName.Contains(input.TimerName))
  42. .Select<TimerOutput>()
  43. .ToPagedListAsync(input.Page, input.PageSize);
  44. timers.Items.ToList().ForEach(u =>
  45. {
  46. var timer = workers.FirstOrDefault(m => m.WorkerName == u.TimerName);
  47. if (timer != null)
  48. {
  49. u.Status = timer.Status;
  50. u.Tally = timer.Tally;
  51. u.Exception = JSON.Serialize(timer.Exception);
  52. }
  53. });
  54. return timers;
  55. }
  56. /// <summary>
  57. /// 增加任务
  58. /// </summary>
  59. /// <param name="input"></param>
  60. /// <returns></returns>
  61. [HttpPost("/sysTimer/add")]
  62. public async Task AddTimer(AddTimerInput input)
  63. {
  64. var isExist = await _sysTimerRep.IsAnyAsync(u => u.TimerName == input.TimerName);
  65. if (isExist) throw Oops.Oh(ErrorCodeEnum.D1100);
  66. var timer = input.Adapt<SysTimer>();
  67. await _sysTimerRep.InsertAsync(timer);
  68. CreateTimer(timer); // 添加到任务调度里
  69. }
  70. /// <summary>
  71. /// 删除任务
  72. /// </summary>
  73. /// <param name="input"></param>
  74. /// <returns></returns>
  75. [HttpPost("/sysTimer/delete")]
  76. public async Task DeleteTimer(DeleteTimerInput input)
  77. {
  78. var timer = await _sysTimerRep.GetFirstAsync(u => u.Id == input.Id);
  79. if (timer == null) throw Oops.Oh(ErrorCodeEnum.D1101);
  80. await _sysTimerRep.DeleteAsync(timer);
  81. SpareTime.Cancel(timer.TimerName); // 从调度器里取消
  82. }
  83. /// <summary>
  84. /// 更新任务
  85. /// </summary>
  86. /// <param name="input"></param>
  87. /// <returns></returns>
  88. [HttpPost("/sysTimer/update")]
  89. public async Task UpdateTimber(UpdateTimerInput input)
  90. {
  91. var isExist = await _sysTimerRep.IsAnyAsync(u => u.TimerName == input.TimerName && u.Id != input.Id);
  92. if (isExist) throw Oops.Oh(ErrorCodeEnum.D1100);
  93. // 先从调度器里取消
  94. var oldTimer = await _sysTimerRep.GetFirstAsync(u => u.Id == input.Id);
  95. SpareTime.Cancel(oldTimer.TimerName);
  96. var timer = input.Adapt<SysTimer>();
  97. await _sysTimerRep.AsUpdateable(timer).IgnoreColumns(true).ExecuteCommandAsync();
  98. CreateTimer(timer); // 再添加到任务调度里
  99. }
  100. /// <summary>
  101. /// 设置任务状态
  102. /// </summary>
  103. /// <param name="input"></param>
  104. /// <returns></returns>
  105. [HttpPost("/sysTimer/setStatus")]
  106. public async void SetStatusTimer(SetTimerStatusInput input)
  107. {
  108. if (input.Status == SpareTimeStatus.Stopped)
  109. SpareTime.Stop(input.TimerName);
  110. else if (input.Status == SpareTimeStatus.Running)
  111. {
  112. var spareTime = SpareTime.GetWorkers().ToList().Find(u => u.WorkerName == input.TimerName);
  113. if (spareTime == null)
  114. {
  115. var timer = await _sysTimerRep.GetFirstAsync(u => u.TimerName == input.TimerName);
  116. CreateTimer(timer);
  117. }
  118. SpareTime.Start(input.TimerName); // 若StartNow=flase则不会启动任务
  119. }
  120. }
  121. /// <summary>
  122. /// 创建定时任务
  123. /// </summary>
  124. /// <param name="input"></param>
  125. private void CreateTimer(SysTimer input)
  126. {
  127. Action<SpareTimer, long> action = null;
  128. switch (input.RequestType)
  129. {
  130. case RequestTypeEnum.Run: // 创建本地方法委托
  131. {
  132. var taskMethod = GetTimerMethodList()?.Result.FirstOrDefault(m => m.RequestUrl == input.RequestUrl);
  133. if (taskMethod == null) break;
  134. var typeInstance = Activator.CreateInstance(taskMethod.DeclaringType);
  135. action = (Action<SpareTimer, long>)Delegate.CreateDelegate(typeof(Action<SpareTimer, long>), typeInstance, taskMethod.MethodName);
  136. break;
  137. }
  138. default: // 创建网络任务委托
  139. {
  140. action = async (_, _) =>
  141. {
  142. var requestUrl = input.RequestUrl.Trim();
  143. requestUrl = requestUrl?.IndexOf("http") == 0 ? requestUrl : "http://" + requestUrl; // 默认http协议
  144. var requestParameters = input.RequestPara;
  145. var headersString = input.Headers;
  146. var headers = string.IsNullOrEmpty(headersString)
  147. ? null : JSON.Deserialize<Dictionary<string, string>>(headersString);
  148. switch (input.RequestType)
  149. {
  150. case RequestTypeEnum.Get:
  151. await requestUrl.SetHeaders(headers).GetAsync();
  152. break;
  153. case RequestTypeEnum.Post:
  154. await requestUrl.SetHeaders(headers).SetQueries(requestParameters).PostAsync();
  155. break;
  156. case RequestTypeEnum.Put:
  157. await requestUrl.SetHeaders(headers).SetQueries(requestParameters).PutAsync();
  158. break;
  159. case RequestTypeEnum.Delete:
  160. await requestUrl.SetHeaders(headers).DeleteAsync();
  161. break;
  162. }
  163. };
  164. break;
  165. }
  166. }
  167. if (action == null) return;
  168. // 缓存任务配置参数供任务运行时读取
  169. if (input.RequestType == RequestTypeEnum.Run)
  170. {
  171. var timerParaName = $"{input.TimerName}_para";
  172. var timerPara = _sysCacheService.Exists(timerParaName);
  173. var requestPara = string.IsNullOrEmpty(input.RequestPara);
  174. // 若没有任务配置但存在缓存则删除
  175. if (requestPara && timerPara)
  176. _sysCacheService.RemoveAsync(timerParaName);
  177. else if (!requestPara)
  178. _sysCacheService.SetAsync(timerParaName, JSON.Deserialize<Dictionary<string, string>>(input.RequestPara));
  179. }
  180. // 创建定时任务
  181. switch (input.TimerType)
  182. {
  183. case SpareTimeTypes.Interval:
  184. if (input.DoOnce)
  185. SpareTime.DoOnce((int)input.Interval * 1000, action, input.TimerName, input.Remark, input.StartNow, executeType: input.ExecuteType);
  186. else
  187. SpareTime.Do((int)input.Interval * 1000, action, input.TimerName, input.Remark, input.StartNow, executeType: input.ExecuteType);
  188. break;
  189. case SpareTimeTypes.Cron:
  190. SpareTime.Do(input.Cron, action, input.TimerName, input.Remark, input.StartNow, executeType: input.ExecuteType);
  191. break;
  192. }
  193. }
  194. /// <summary>
  195. /// 获取所有定时任务方法列表(贴spareTime特性)
  196. /// </summary>
  197. /// <returns></returns>
  198. private async Task<IEnumerable<TimerMethod>> GetTimerMethodList()
  199. {
  200. // 有缓存就返回缓存
  201. var timerMethodList = await _sysCacheService.GetAsync<IEnumerable<TimerMethod>>(CacheConst.KeyTimer);
  202. if (timerMethodList != null) return timerMethodList;
  203. timerMethodList = App.EffectiveTypes
  204. .Where(u => u.IsClass && !u.IsInterface && !u.IsAbstract && typeof(ISpareTimeWorker).IsAssignableFrom(u))
  205. .SelectMany(u => u.GetMethods(BindingFlags.Public | BindingFlags.Instance)
  206. .Where(m => m.IsDefined(typeof(SpareTimeAttribute), false) &&
  207. m.GetParameters().Length == 2 &&
  208. m.GetParameters()[0].ParameterType == typeof(SpareTimer) &&
  209. m.GetParameters()[1].ParameterType == typeof(long) && m.ReturnType == typeof(void))
  210. .Select(m =>
  211. {
  212. // 默认获取第一条任务特性
  213. var spareTimeAttribute = m.GetCustomAttribute<SpareTimeAttribute>();
  214. return new TimerMethod
  215. {
  216. TimerName = spareTimeAttribute.WorkerName,
  217. RequestUrl = $"{m.DeclaringType.Name}/{m.Name}",
  218. Cron = spareTimeAttribute.CronExpression,
  219. DoOnce = spareTimeAttribute.DoOnce,
  220. ExecuteType = spareTimeAttribute.ExecuteType,
  221. Interval = (int)spareTimeAttribute.Interval / 1000,
  222. StartNow = spareTimeAttribute.StartNow,
  223. RequestType = RequestTypeEnum.Run,
  224. Remark = spareTimeAttribute.Description,
  225. TimerType = string.IsNullOrEmpty(spareTimeAttribute.CronExpression) ? SpareTimeTypes.Interval : SpareTimeTypes.Cron,
  226. MethodName = m.Name,
  227. DeclaringType = m.DeclaringType
  228. };
  229. }));
  230. await _sysCacheService.SetAsync(CacheConst.KeyTimer, timerMethodList);
  231. return timerMethodList;
  232. }
  233. /// <summary>
  234. /// 启动自启动任务
  235. /// </summary>
  236. [NonAction]
  237. public async void StartTimer()
  238. {
  239. var timerList = await _sysTimerRep.GetListAsync(t => t.StartNow);
  240. timerList.ForEach(CreateTimer);
  241. }
  242. }
  243. }