Создание собственного планировщика

В статье приведен пример создания системного ежедневного планировщика, который каждый день в 0:30 проверяет, у каких проектов срок сдачи будет на следующий день. После выполнения задачи планировщика менеджерам проектов будут отправлены сообщения от имени системы о том, что завтра срок проекта заканчивается.

Пример отображения данных

Рис. 1. Планировщик «Проверка завершения проекта»

Рис. 2. Сообщение в ленте сообщений у менеджера проекта, по которому завершается срок сдачи

Методы расширения (интерфейса) ISchedulerJobRepository

Точка расширения (интерфейс) ISchedulerJobRepository имеет следующие методы:

/// <summary>
/// Получить список запланированных задач
/// </summary>
/// <returns></returns>
ICollection<ISchedulerJob> GetSchedulerJobs();

Для реализации точки расширения планировщика, необходимо реализовать интерфейс запланированной работы планировщика

Методы расширения (интерфейса) ISchedulerJob

Интерфейс ISchedulerJob имеет следующие методы:

/// <summary>
/// Уникальный идентификатор владельца
/// Если нужно, чтобы планировщик не был в блоке "Системные" необходимо реализовать <seealso cref="ISchedulerTaskOwner"/> и указать его Uid
/// </summary>
Guid? OwnerUid { get; }

/// <summary>
/// Триггер
/// </summary>
[NotNull]
ITrigger Trigger { get; }

/// <summary>
/// Список выполняемых работ
/// </summary>
[NotNull]
ICollection<IJob> Jobs  { get; }

Методы расширения (интерфейса) ISchedulerTaskOwner

Точка расширения (интерфейс) ISchedulerTaskOwner имеет следующие методы:

/// <summary>
/// Уникальный идентификатор владельца
/// </summary>
Guid Uid { get; }

/// <summary>
/// Имя владельца
/// </summary>
string Name { get; }

Пример класса точки расширения ISchedulerTaskOwner, ISchedulerJobRepository и ISchedulerJob

[Component]
public class Owner : ISchedulerTaskOwner
{
 public static readonly Guid Trigguid = new Guid("E2460CB3-5571-4130-A1E2-883882B1D164");

 public Guid Uid
 {
  get { return Trigguid; }
 }

 public string Name
 {
  get { return "Планировщик по проверке завершения проекта"; }
 }
}

[Component]
public class Scheduler : ISchedulerJobRepository
{
 /// <summary>
 /// Уникальный идентификатор триггера
 /// </summary>
 public static readonly Guid TriggerGuid = new Guid("5D9991B5-47E0-4f54-8B4E-0CA72A232A12");

 public ICollection<ISchedulerJob> GetSchedulerJobs()
 {
  return new ISchedulerJob[] { new MessageSchedulerJob() };
 }

 private class MessageSchedulerJob : ISchedulerJob
 {
  private readonly ITrigger _trigger;
  private readonly ICollection<IJob> _jobs;
   

  public MessageSchedulerJob()
  {
   _trigger = new NthIncludedDayTrigger(
    new NthIncludedDaySettings
    {
     ScheduleType = ScheduleType.Daily, //Тип планировщика (Ежедневный, ежемесячный, еженедельный, одиночный)
     DailySettings = new DailySettings { EveryDay = 1, OnlyWorkDays = false }, //В зависимости от типа задаются настройки планировщика: EveryDay - каждый N-ый день, OnlyWorkDays - только по рабочим дням
     StartDate = DateTime.Today.AddMinutes(30), //Дата начала (во сколько планировщик будет запущен, в данном случае в 00:30)
     OvertimeToRun = TimeSpan.FromDays(1) //Время, через которое еще можно выполнить задачу, если в нужное время выполнить не получилось по какой-то причине (например не был запущен сервер)
     //Можно добавить RepeatSettings - настройки повтора планировщика, чтобы, например, автоматически выполнялся каждую минуту 
     //Пример: RepeatSettings = new RepeatSettings {Enabled = true, RepeatEvery = TimeSpan.FromMinutes(1), RepeatTo = TimeSpan.FromHours(24)},
    },
    Locator.GetService<IProductionCalendarService>())
   {
    Name = SR.T("Триггер проверки завершения проекта"), //Название блока с вашими напоминаниями
    Id = TriggerGuid //Идентификатор планировщика
   };
   _jobs = new List<IJob>
   {
    new MessageScheduler()
   };
  }

  public ITrigger Trigger
  {
   get { return _trigger; }
  }

  public ICollection<IJob> Jobs
  {
   get { return _jobs; }
  }
   
  public virtual Guid? OwnerUid
  {
   get { return Owner.Trigguid; } //Вернуть null, если нужно, чтобы планировщик был в блоке "Системные"
  }

  private class MessageScheduler : IJob
  {
   public Guid Id
   {
    get { return new Guid("94A8F714-01D8-4472-85F2-417167075CAE"); }
   }

   public string Name
   {
    get { return SR.T("Проверка завершения проекта"); }
   }

   public Image Icon
   {
    get { return null; }
   }

   public JobResult Do(DateTime dateToRun)
   {
    var user = UserManager.Instance.LoadOrNull(SecurityConstants.SystemUserUid);
    Locator.GetServiceNotNull<ISecurityService>().RunByUser(user, () =>
    { 
     var filter = InterfaceActivator.Create<IProjectFilter>();
     filter.FinishDate = new RelativeDateTime(DateTime.Today.AddDays(1), DateTime.Today.AddDays(1));
     filter.Status = ProjectStatus.Active;
     filter.DisableSecurity = true;
     var projectsendtoday = ProjectManager.Instance.Find(filter, null);
     if (projectsendtoday.Count != 0)
     {
      foreach (var project in projectsendtoday)
      {
       var message = ChannelMessageManager.Instance.Create();
       message.CreationDate = dateToRun;
       message.CreationAuthor = user;
       message.Subject = string.Format("У следующего проекта заканчивается срок сдачи: {0}", project.FinishDate);
       message.FullMessage = string.Format("Наименование проекта: {0}", project.Name);
       message.Recipients.Add(project.Manager);
       message.Save();
      }
     }
    });

    return new JobResult
    {
     Status = JobStatus.Success,
     Information = SR.T("Проверка завершения проекта")
    };
   }
  }
 }
}

Как можно заметить, в методе public JobResult Do(DateTime dateToRun) работа планировщика выполняется от имени системы (по умолчанию при выполнении работы планировщика текущий пользователь не задан), затем фильтруется список активных проектов по дате завершения и отбираются только те, чья дата завершения будет завтра, затем создается сообщение в ленту сообщений для каждого менеджера по каждому проекту, автором сообщения является система.

Ссылки на элементы API

ISchedulerJobRepository
ISchedulerJob
ISchedulerTaskOwner

Прикрепленные файлы