[ELMA3] Создание собственного планировщика
В статье приведен пример создания системного ежедневного планировщика, который каждый день в 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) работа планировщика выполняется от имени системы (по умолчанию при выполнении работы планировщика текущий пользователь не задан), затем фильтруется список активных проектов по дате завершения и отбираются только те, чья дата завершения будет завтра, затем создается сообщение в ленту сообщений для каждого менеджера по каждому проекту, автором сообщения является система.