using System.Reflection; using JNPF.Common.Configuration; using JNPF.Common.Const; using JNPF.Common.Core.Manager; using JNPF.Common.Enums; using JNPF.Common.Extension; using JNPF.Common.Filter; using JNPF.Common.Manager; using JNPF.Common.Security; using JNPF.DataEncryption; using JNPF.DependencyInjection; using JNPF.DynamicApiController; using JNPF.FriendlyException; using JNPF.LinqBuilder; using JNPF.Schedule; using JNPF.Systems.Interfaces.System; using JNPF.TaskScheduler.Entitys; using JNPF.TaskScheduler.Entitys.Dto.TaskScheduler; using JNPF.TaskScheduler.Entitys.Model; using JNPF.TaskScheduler.Interfaces.TaskScheduler; using JNPF.TimeCrontab; using Mapster; using Microsoft.AspNetCore.Mvc; using SqlSugar; namespace JNPF.TaskScheduler; /// /// 定时任务 /// 版 本:V3.4.1 /// 版 权:拓通智联科技有限公司(http://www.tuotong-tech.com) /// 日 期:2021-06-01. /// [ApiDescriptionSettings(Tag = "TaskScheduler", Name = "scheduletask", Order = 220)] [Route("api/[controller]")] public class TimeTaskService : ITimeTaskService, IDynamicApiController, ITransient { private readonly ISqlSugarRepository _repository; private readonly IDataInterfaceService _dataInterfaceService; private readonly IUserManager _userManager; private readonly ICacheManager _cacheManager; //private readonly ISchedulerFactory _schedulerFactory; private readonly IDataBaseManager _dataBaseManager; /// /// 初始化一个类型的新实例. /// public TimeTaskService( ISqlSugarRepository repository, IUserManager userManager, IDataInterfaceService dataInterfaceService, ICacheManager cacheManager, //ISchedulerFactory schedulerFactory, IDataBaseManager dataBaseManager) { _repository = repository; _userManager = userManager; _dataInterfaceService = dataInterfaceService; _cacheManager = cacheManager; _dataBaseManager = dataBaseManager; //_schedulerFactory = schedulerFactory; } #region Get /// /// 列表. /// /// 请求参数 /// [HttpGet("")] public async Task GetList([FromQuery] PageInputBase input) { var queryWhere = LinqExpression.And().And(x => x.DeleteMark == null); if (!string.IsNullOrEmpty(input.keyword)) queryWhere = queryWhere.And(m => m.FullName.Contains(input.keyword) || m.EnCode.Contains(input.keyword)); var list = await _repository.AsQueryable().Where(queryWhere).OrderBy(x => x.CreatorTime, OrderByType.Desc) .OrderByIF(!string.IsNullOrEmpty(input.keyword), t => t.LastModifyTime, OrderByType.Desc).ToPagedListAsync(input.currentPage, input.pageSize); var pageList = new SqlSugarPagedList() { list = list.list.Adapt>(), pagination = list.pagination }; return PageResult.SqlSugarPageResult(pageList); } /// /// 列表(执行记录). /// /// 请求参数. /// 任务Id. /// [HttpGet("{id}/TaskLog")] public async Task GetTaskLogList([FromQuery] TaskLogInput input, string id) { var whereLambda = LinqExpression.And().And(x => x.TaskId == id); if (input.runResult.IsNotEmptyOrNull()) whereLambda = whereLambda.And(x => x.RunResult == input.runResult); if (input.endTime != null && input.startTime != null) { var start = Convert.ToDateTime(string.Format("{0:yyyy-MM-dd 00:00:00}", input.startTime?.TimeStampToDateTime())); var end = Convert.ToDateTime(string.Format("{0:yyyy-MM-dd 23:59:59}", input.endTime?.TimeStampToDateTime())); whereLambda = whereLambda.And(x => SqlFunc.Between(x.RunTime, start, end)); } var list = await _repository.AsSugarClient().Queryable().Where(whereLambda).OrderBy(x => x.RunTime, OrderByType.Desc).ToPagedListAsync(input.currentPage, input.pageSize); var pageList = new SqlSugarPagedList() { list = list.list.Adapt>(), pagination = list.pagination }; return PageResult.SqlSugarPageResult(pageList); } /// /// 信息. /// /// 主键值. /// [HttpGet("Info/{id}")] public async Task GetInfo_Api(string id) { return (await GetInfo(id)).Adapt(); } /// /// 本地方法. /// /// [HttpGet("TaskMethods")] public async Task GetTaskMethodSelector() { return await GetTaskMethods(); } #endregion #region Post /// /// 新建. /// /// 实体对象. /// [HttpPost("")] public async Task Create([FromBody] TimeTaskCrInput input) { if (await _repository.IsAnyAsync(x => (x.EnCode == input.enCode || x.FullName == input.fullName) && x.DeleteMark == null)) throw Oops.Oh(ErrorCode.COM1004); var comtentModel = input.executeContent.ToObject(); comtentModel.TenantId = _userManager.TenantId; comtentModel.TenantDbName = _userManager.TenantDbName; comtentModel.ConnectionConfig = _userManager.ConnectionConfig; comtentModel.Token = _userManager.ToKen; var entity = input.Adapt(); entity.ExecuteContent = comtentModel.ToJsonString(); entity.ExecuteCycleJson = comtentModel.cron; var result = await _repository.AsInsertable(entity).IgnoreColumns(ignoreNullColumn: true).CallEntityMethod(m => m.Create()).ExecuteReturnEntityAsync(); // var job = _repository.AsTenant().GetConnection("JNPF-Job"); // var jobDetail = new JobDetail() { }; // job.Insertable() _ = result ?? throw Oops.Oh(ErrorCode.COM1000); // 添加到任务调度里 AddTimerJob(result); //await AddJob(result); } /// /// 更新. /// /// 主键值 /// 实体对象 /// [HttpPut("{id}")] public async Task Update(string id, [FromBody] TimeTaskUpInput input) { if (await _repository.IsAnyAsync(x => x.Id != id && (x.EnCode == input.enCode || x.FullName == input.fullName) && x.DeleteMark == null)) throw Oops.Oh(ErrorCode.COM1004); var entityOld = await GetInfo(id); // 先从调度器里取消 SpareTime.Cancel(id); var entityNew = input.Adapt(); entityNew.RunCount = entityOld.RunCount; var comtentModel = input.executeContent.ToObject(); comtentModel.TenantId = _userManager.TenantId; comtentModel.TenantDbName = _userManager.TenantDbName; comtentModel.ConnectionConfig = _userManager.ConnectionConfig; comtentModel.Token = _userManager.ToKen; entityNew.ExecuteContent = comtentModel.ToJsonString(); entityNew.ExecuteCycleJson = comtentModel.cron; var isOk = await _repository.AsUpdateable(entityNew).IgnoreColumns(ignoreAllNullColumns: true).CallEntityMethod(m => m.LastModify()).ExecuteCommandHasChangeAsync(); if (!isOk) throw Oops.Oh(ErrorCode.COM1001); // 再添加到任务调度里 if (entityNew.EnabledMark == 1) { AddTimerJob(entityNew); } } /// /// 删除. /// /// 主键值. /// [HttpDelete("{id}")] public async Task Delete(string id) { var entity = await GetInfo(id); if (entity == null) throw Oops.Oh(ErrorCode.COM1005); var isOk = await _repository.AsUpdateable(entity).CallEntityMethod(m => m.Delete()).UpdateColumns(it => new { it.DeleteMark, it.DeleteTime, it.DeleteUserId }).ExecuteCommandHasChangeAsync(); if (!isOk) throw Oops.Oh(ErrorCode.COM1002); // 从调度器里取消 SpareTime.Cancel(entity.Id); } /// /// 停止. /// /// 主键值. /// [HttpPut("{id}/Actions/Stop")] public async Task Stop(string id) { var isOk = await _repository.AsUpdateable().SetColumns(it => new TimeTaskEntity() { EnabledMark = SqlFunc.IIF(it.EnabledMark == 1, 0, 1), LastModifyUserId = _userManager.UserId, LastModifyTime = SqlFunc.GetDate() }).Where(it => it.Id.Equals(id)).ExecuteCommandHasChangeAsync(); if (!isOk) throw Oops.Oh(ErrorCode.COM1003); SpareTime.Stop(id); } /// /// 启动. /// /// 主键值. /// [HttpPut("{id}/Actions/Enable")] public async Task Enable(string id) { var entity = await GetInfo(id); var isOk = await _repository.AsUpdateable().SetColumns(it => new TimeTaskEntity() { EnabledMark = SqlFunc.IIF(it.EnabledMark == 1, 0, 1), LastModifyUserId = _userManager.UserId, LastModifyTime = SqlFunc.GetDate() }).Where(it => it.Id.Equals(id)).ExecuteCommandHasChangeAsync(); if (!isOk) throw Oops.Oh(ErrorCode.COM1003); var comtentModel = entity.ExecuteContent.ToObject(); comtentModel.Token = _userManager.ToKen; entity.ExecuteContent = comtentModel.ToJsonString(); var timer = SpareTime.GetWorkers().ToList().Find(u => u.WorkerName == id); if (timer == null) { AddTimerJob(entity); } else { // 如果 StartNow 为 flase , 执行 AddTimerJob 并不会启动任务 SpareTime.Start(entity.Id); } } #endregion #region PublicMethod /// /// 启动自启动任务. /// [NonAction] public void StartTimerJob() { // 非多租户模式启动自启任务 if (!KeyVariable.MultiTenancy) { //_repository.AsQueryable().Where(x => x.DeleteMark == null && x.EnabledMark == 1).ToList().ForEach(AddTimerJob); } } /// /// 根据类型执行任务. /// /// 任务实体. /// [NonAction] public async Task PerformJob(TimeTaskEntity entity) { try { var model = entity.ExecuteContent.ToObject(); if (!string.IsNullOrEmpty(model.Token)) { var claims = JWTEncryption.ReadJwtToken(model.Token.Replace("Bearer ", string.Empty).Replace("bearer ", string.Empty))?.Claims; var connectionConfig = claims.FirstOrDefault(e => e.Type == ClaimConst.CONNECTIONCONFIG)?.Value.ToObject(); var parameters = model.parameter.ToDictionary(key => key.field, value => value.value.IsNotEmptyOrNull() ? value.value : value.defaultValue); await _dataInterfaceService.GetResponseByType(model.interfaceId, 3, model.TenantId, null, parameters); } return string.Empty; } catch (Exception ex) { return ex.Message; } } #endregion #region PrivateMethod /// /// 详情. /// /// 主键. /// private async Task GetInfo(string id) { return await _repository.GetFirstAsync(x => x.Id == id && x.DeleteMark == null); } /// /// 新增定时任务. /// /// private async void AddTimerJob(TimeTaskEntity input) { Action? action = null; ContentModel? comtentModel = input.ExecuteContent.ToObject(); input.ExecuteCycleJson = comtentModel.cron; switch (input.ExecuteType) { case "3": // 查询符合条件的任务方法 TaskMethodInfo? taskMethod = GetTaskMethods()?.Result.FirstOrDefault(m => m.id == comtentModel.localHostTaskId); if (taskMethod == null) break; // 创建任务对象 object? typeInstance = Activator.CreateInstance(taskMethod.DeclaringType); // 创建委托 action = (Action)Delegate.CreateDelegate(typeof(Action), typeInstance, taskMethod.MethodName); break; default: action = async (timer, count) => { var msg = await PerformJob(input); }; break; } if (action == null) return; // SpareTime.Do(comtentModel.cron, action, input.Id, comtentModel.ConnectionConfig.ToJsonString(), true, executeType: SpareTimeExecuteTypes.Parallel); var starTime = comtentModel.startTime?.TimeStampToDateTime(); var endTime = comtentModel.endTime?.TimeStampToDateTime(); var interval = 1; if (DateTime.Now < starTime) { interval = (starTime.ParseToDateTime() - DateTime.Now).TotalMilliseconds.ParseToInt(); } SpareTime.DoOnce(interval, action, "Once_" + input.Id); SpareTime.Do( () => { var isRun = comtentModel.endTime.IsNullOrEmpty() ? DateTime.Now >= starTime : DateTime.Now >= starTime && DateTime.Now < endTime; if (isRun) { return SpareTime.GetCronNextOccurrence(comtentModel.cron); } else { return null; } }, action, input.Id, comtentModel.ConnectionConfig.ToJsonString(), true, executeType: SpareTimeExecuteTypes.Parallel, cancelInNoneNextTime: false); } private async Task AddJob(TimeTaskEntity input) { //ContentModel? comtentModel = input.ExecuteContent.ToObject(); //var starTime = comtentModel.startTime?.TimeStampToDateTime(); //var endTime = comtentModel.endTime?.TimeStampToDateTime(); //var jobBuilder = JobBuilder.Create(async (context, stoppingToken) => //{ // Console.WriteLine(string.Format("{0}在执行任务,执行时间{1},触发时间:{1}", context.JobId, context.ExecutingTime, context.OccurrenceTime)); // if (!input.ExecuteType.Equals("3")) // { // var msg = await PerformJob(input); // } //}); //jobBuilder.SetJobId(input.Id); //jobBuilder.SetGroupName(input.FullName); //var triggerBuilder = TriggerBuilder.Create(input.ExecuteCycleJson, CronStringFormat.WithSeconds); //triggerBuilder.SetStartTime(starTime); //triggerBuilder.SetEndTime(endTime); ////triggerBuilder.SetRunOnStart(true); //_schedulerFactory.AddJob(jobBuilder, triggerBuilder); } /// /// 获取所有本地任务. /// /// private async Task> GetTaskMethods() { //var taskMethods = await _cacheManager.GetAsync>(CommonConst.CACHEKEYTIMERJOB); //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)) // .Select(m => // { // // 默认获取第一条任务特性 // var spareTimeAttribute = m.GetCustomAttribute(); // return new TaskMethodInfo // { // id = $"{m.DeclaringType.Name}/{m.Name}", // fullName = 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.BuiltIn, // Remark = spareTimeAttribute.Description, // TimerType = string.IsNullOrEmpty(spareTimeAttribute.CronExpression) ? SpareTimeTypes.Interval : SpareTimeTypes.Cron, // MethodName = m.Name, // DeclaringType = m.DeclaringType // }; // })).ToList(); //await _cacheManager.SetAsync(CommonConst.CACHEKEYTIMERJOB, taskMethods); //return taskMethods; return new List(); } #endregion }