From e12b22590df5c6194ca7e2927a7cff6213ac1567 Mon Sep 17 00:00:00 2001
From: ningshuxia <ningshuxia0927@outlook.com>
Date: 星期五, 19 八月 2022 13:50:04 +0800
Subject: [PATCH] Merge branch 'master' of http://47.103.154.90:83/r/IStation/Service.V4.1

---
 Server/IStation.Server.Job/helpers/ConfigHelper.cs                             |   29 +
 Server/IStation.Server.Job/custom_cron/service/CustomCronServiceJob.cs         |  115 ++++
 Server/IStation.Server.Job/helpers/JobHelper.cs                                |   37 +
 TopShelf/IStation.TopShelf.Job/Program.cs                                      |   16 
 Server/IStation.Server.Job/custom_real/repeat/CustomRealRepeatJob.cs           |  112 ++++
 Settings/IStation.Settings/paras_settings.json                                 |    4 
 Server/IStation.Server.Job/custom_cron/repeat/CustomCronRepeatJobHelper.cs     |   78 +++
 TopShelf/IStation.TopShelf.Job/Service.cs                                      |   40 +
 Server/IStation.Server.Job/custom_cron/service/CustomCronServiceJobHelper.cs   |   70 ++
 IStation.Server.Job.sln                                                        |   13 
 Server/IStation.Server.Job/interface/IJobHelper.cs                             |   27 +
 TopShelf/IStation.TopShelf.Job/IStation.TopShelf.Job.csproj                    |   15 
 Server/IStation.Server.Job/custom_cron/repeat/CustomCronRepeatJobNameHelper.cs |   48 ++
 Server/IStation.Server.Job/custom_real/service/CustomRealServiceJob.cs         |  115 ++++
 Server/IStation.Server.Job/Program.cs                                          |    6 
 Settings/IStation.Settings/models/job/Paras_Job.cs                             |    5 
 Server/IStation.Server.Job/custom_cron/repeat/CustomCronRepeatJob.cs           |  112 ++++
 Server/IStation.Server.Job/custom_real/job/CustomRealJobNameHelper.cs          |   46 +
 Server/IStation.Server.Job/custom_real/repeat/CustomRealRepeatJobHelper.cs     |   78 +++
 Server/IStation.Server.Job/custom_real/repeat/CustomRealRepeatJobNameHelper.cs |   48 ++
 Server/IStation.Server.Job/custom_real/job/CustomRealJob.cs                    |   50 ++
 /dev/null                                                                      |   12 
 Server/IStation.Server.Job/custom_real/service/CustomRealServiceJobHelper.cs   |   70 ++
 Server/IStation.Server.Job/IStation.Server.Job.csproj                          |    6 
 Settings/IStation.Settings/models/job/Paras_Job_Execution.cs                   |   26 +
 Server/IStation.Server.Job/custom_real/job/CustomRealJobHelper.cs              |   75 +++
 Server/IStation.Server.Job/custom_cron/cron/CustomCronJobHelper.cs             |   74 +++
 Server/IStation.Server.Job/custom_cron/cron/CustomCronJobNameHelper.cs         |   46 +
 Server/IStation.Server.Job/custom_cron/cron/CustomCronJob.cs                   |   50 ++
 29 files changed, 1,408 insertions(+), 15 deletions(-)

diff --git a/IStation.Server.Job.sln b/IStation.Server.Job.sln
index ed286b8..363e19a 100644
--- a/IStation.Server.Job.sln
+++ b/IStation.Server.Job.sln
@@ -65,6 +65,10 @@
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IStation.MemoryCache", "Component\IStation.MemoryCache\IStation.MemoryCache.csproj", "{57FE2AAD-6C18-4E86-A103-1865AF97F04A}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IStation.TopShelf.Job", "TopShelf\IStation.TopShelf.Job\IStation.TopShelf.Job.csproj", "{417AD452-1359-4C86-B81A-E958BABC7879}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IStation.TopShelf", "Component\IStation.TopShelf\IStation.TopShelf.csproj", "{C2EC86B3-85F5-4CC6-800B-D7215F05372C}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -159,6 +163,14 @@
 		{57FE2AAD-6C18-4E86-A103-1865AF97F04A}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{57FE2AAD-6C18-4E86-A103-1865AF97F04A}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{57FE2AAD-6C18-4E86-A103-1865AF97F04A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{417AD452-1359-4C86-B81A-E958BABC7879}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{417AD452-1359-4C86-B81A-E958BABC7879}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{417AD452-1359-4C86-B81A-E958BABC7879}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{417AD452-1359-4C86-B81A-E958BABC7879}.Release|Any CPU.Build.0 = Release|Any CPU
+		{C2EC86B3-85F5-4CC6-800B-D7215F05372C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{C2EC86B3-85F5-4CC6-800B-D7215F05372C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C2EC86B3-85F5-4CC6-800B-D7215F05372C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{C2EC86B3-85F5-4CC6-800B-D7215F05372C}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -185,6 +197,7 @@
 		{521CC846-751A-4F9A-896B-E20011DEFF63} = {B2A37802-88E8-4029-8256-343C5AF13D18}
 		{BA870208-76FA-471A-861A-328ED902069C} = {2235DB47-DB5E-4072-B147-0F2ECBD1CEC0}
 		{57FE2AAD-6C18-4E86-A103-1865AF97F04A} = {CEA4D501-B221-4F3C-9277-A40D4524FFE3}
+		{C2EC86B3-85F5-4CC6-800B-D7215F05372C} = {CEA4D501-B221-4F3C-9277-A40D4524FFE3}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {E02989A8-9B6F-43E5-AACA-790328215089}
diff --git a/Server/IStation.Server.Job/IStation.Server.Job.csproj b/Server/IStation.Server.Job/IStation.Server.Job.csproj
index d49c1ff..2f06a93 100644
--- a/Server/IStation.Server.Job/IStation.Server.Job.csproj
+++ b/Server/IStation.Server.Job/IStation.Server.Job.csproj
@@ -9,4 +9,10 @@
     <RootNamespace>IStation.Server</RootNamespace>
   </PropertyGroup>
 
+  <ItemGroup>
+    <ProjectReference Include="..\..\Component\IStation.Quartz\IStation.Quartz.csproj" />
+    <ProjectReference Include="..\..\Job\IStation.JobFactory\IStation.JobFactory.csproj" />
+    <ProjectReference Include="..\..\Service\IStation.Service.Job\IStation.Service.Job.csproj" />
+  </ItemGroup>
+
 </Project>
diff --git a/Server/IStation.Server.Job/Program.cs b/Server/IStation.Server.Job/Program.cs
index 64e3d97..2af805b 100644
--- a/Server/IStation.Server.Job/Program.cs
+++ b/Server/IStation.Server.Job/Program.cs
@@ -1,14 +1,14 @@
 锘�// See https://aka.ms/new-console-template for more information
-using IStation.Server;
 using IStation;
+using IStation.Server;
 
 LogHelper.Info("鏈嶅姟姝e湪鍚姩...");
 var jobHelper = new JobHelper();
-jobHelper.Start();
+jobHelper.StartJob();
 AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
 {
     LogHelper.Info("鏈嶅姟姝e湪鍏抽棴...");
-    jobHelper.Cancel();
+    jobHelper.CancelJob();
     LogHelper.Info("鏈嶅姟鍏抽棴锛�");
 };
 LogHelper.Info("鏈嶅姟鍚姩鎴愬姛锛�");
diff --git a/Server/IStation.Server.Job/custom_cron/cron/CustomCronJob.cs b/Server/IStation.Server.Job/custom_cron/cron/CustomCronJob.cs
new file mode 100644
index 0000000..f0379fa
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_cron/cron/CustomCronJob.cs
@@ -0,0 +1,50 @@
+锘縰sing Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夎鍒掍换鍔�
+    /// </summary>
+    public class CustomCronJob : Quartz.IJob
+    {
+        internal const string Instance = "Instance";
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public Task Execute(IJobExecutionContext context)
+        {
+            return Task.Run(() =>
+            {
+                try
+                {
+                    var dataMap = context.MergedJobDataMap;
+                    var jobModel = (Model.CustomCronJob)dataMap[Instance];
+                    if (jobModel == null)
+                        return;
+                    var jobExecuter = JobFactory.CreateJob(jobModel.Execution);
+                    if (jobExecuter == null)
+                    {
+                        LogHelper.Error($"鑷畾涔夎鍒掍换鍔�,ID:{jobModel.ID},NO:{jobModel.NO},Name:{jobModel.Name},Execution:{jobModel.Execution},鍒涘缓浠诲姟鎵ц瀵硅薄澶辫触锛�");
+                        return;
+                    }
+                    jobExecuter.Execute();
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.Error("鑷畾涔夎鍒掍换鍔★紝鎵ц鍑洪敊", ex);
+                    var e = new JobExecutionException(ex);
+                    throw e;
+                }
+
+            });
+
+
+        }
+    }
+}
diff --git a/Server/IStation.Server.Job/custom_cron/cron/CustomCronJobHelper.cs b/Server/IStation.Server.Job/custom_cron/cron/CustomCronJobHelper.cs
new file mode 100644
index 0000000..c353e4a
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_cron/cron/CustomCronJobHelper.cs
@@ -0,0 +1,74 @@
+锘縰sing Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夎鍒掍换鍔¤緟鍔╃被
+    /// </summary>
+    public class CustomCronJobHelper
+    {
+        private IScheduler _sched;//璋冨害鍣�
+
+        /// <summary>
+        /// 瀹炰緥
+        /// </summary>
+        public Model.CustomCronJob Instance { get; private set; }
+
+        /// <summary>
+        /// 寮�濮嬩换鍔�
+        /// </summary>
+        public async Task StartJob(Model.CustomCronJob instance)
+        {
+            if (_sched != null)
+                return;
+            this.Instance = instance ?? throw new ArgumentNullException();
+            var jobName = CustomCronJobNameHelper.GetJobName(instance);
+            var jobGroupName = CustomCronJobNameHelper.GetJobGroupName(instance);
+            var triggerName = CustomCronJobNameHelper.GetTriggerName(instance);
+
+            // 1.鍒涘缓scheduler鐨勫紩鐢�
+            var fac = new Quartz.Impl.StdSchedulerFactory();
+            _sched = await fac.GetScheduler();
+
+            //2.鍚姩 scheduler
+            await _sched.Start();
+
+            //3.鍒涘缓浠诲姟
+            var job = JobBuilder.Create<CustomCronJob>()
+             .WithIdentity(jobName, jobGroupName)
+             .Build();
+            job.JobDataMap.Put(CustomRealJob.Instance, instance);
+
+            //4.鍒涘缓Trigger
+            var trigger = TriggerBuilder.Create()
+             .WithIdentity(triggerName, jobGroupName)
+             .WithCronSchedule(instance.Expression)
+             .Build();
+
+            //5.鍔犲叆璋冨害绠$悊鍣�
+            await _sched.ScheduleJob(job, trigger);
+        }
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        public async Task CancelJob()
+        {
+            if (_sched == null)
+                return;
+            var jobGroupName = CustomCronJobNameHelper.GetJobGroupName(this.Instance);
+            var triggerName = CustomCronJobNameHelper.GetTriggerName(this.Instance);
+            var triggerKey = new TriggerKey(triggerName, jobGroupName);
+            if (await _sched.CheckExists(triggerKey))
+            {
+                await _sched.UnscheduleJob(triggerKey);
+            }
+        }
+
+    }
+}
diff --git a/Server/IStation.Server.Job/custom_cron/cron/CustomCronJobNameHelper.cs b/Server/IStation.Server.Job/custom_cron/cron/CustomCronJobNameHelper.cs
new file mode 100644
index 0000000..eeb232b
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_cron/cron/CustomCronJobNameHelper.cs
@@ -0,0 +1,46 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夎鍒掍换鍔″悕绉拌緟鍔╃被
+    /// </summary>
+    public class CustomCronJobNameHelper
+    {
+        private const string _jobNameHeader = "CustomCronJob";
+        private const string _jobGroupHeader = "CustomCronJobGroup";
+        private const string _triggerNameHeader = "CustomCronJobTrigger";
+
+        /// <summary>
+        /// 鑾峰彇浠诲姟鍚嶇О
+        /// </summary>
+        public static string GetJobName(Model.CustomCronJob rhs)
+        {
+            var name = string.Format("{0}_{1}", _jobNameHeader, rhs.ID);
+            return name;
+        }
+
+        /// <summary>
+        /// 鑾峰彇浠诲姟缁勫悕绉�
+        /// </summary>
+        public static string GetJobGroupName(Model.CustomCronJob rhs)
+        {
+            var name = string.Format("{0}_{1}", _jobGroupHeader, rhs.ID);
+            return name;
+        }
+
+        /// <summary>
+        /// 鑾峰彇瑙﹀彂鍣ㄥ悕绉�
+        /// </summary>
+        public static string GetTriggerName(Model.CustomCronJob rhs)
+        {
+            var name = string.Format("{0}_{1}", _triggerNameHeader, rhs.ID);
+            return name;
+        }
+
+    }
+}
diff --git a/Server/IStation.Server.Job/custom_cron/repeat/CustomCronRepeatJob.cs b/Server/IStation.Server.Job/custom_cron/repeat/CustomCronRepeatJob.cs
new file mode 100644
index 0000000..ebd87f9
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_cron/repeat/CustomCronRepeatJob.cs
@@ -0,0 +1,112 @@
+锘縰sing Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夎鍒掗噸澶嶄换鍔�
+    /// </summary>
+    public class CustomCronRepeatJob : Quartz.IJob
+    {
+        //鑷畾涔夎鍒掍换鍔″瓧鍏�
+        private static Dictionary<long,CustomCronJobHelper> _dict=new Dictionary<long, CustomCronJobHelper>();
+
+        /// <summary>
+        /// 浠诲姟id 
+        /// </summary>
+        public long JobID { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public Task Execute(IJobExecutionContext context)
+        {
+            return Task.Run(async () =>
+            {
+                try
+                {
+                    //鑾峰彇浠诲姟
+                    var model = new Service.CustomCronJob().GetByID(JobID);
+
+                    //鑾峰彇浠诲姟澶辫触鍚庯紝鍋滄浠诲姟骞剁Щ闄や换鍔¤緟鍔╃被
+                    if (model == null)
+                    {
+                        if (_dict.ContainsKey(JobID))
+                        {
+                            await _dict[JobID].CancelJob();
+                            _dict.Remove(JobID);
+                        }
+                        return;
+                    }
+
+                    //灏氭湭鍒涘缓璁″垝浠诲姟鏃讹紝鍒涘缓璁″垝浠诲姟杈呭姪绫�
+                    if (!_dict.ContainsKey(JobID))
+                    {
+                        var jobHelper = new CustomCronJobHelper();
+                        await jobHelper.StartJob(model);
+                        _dict.Add(JobID, jobHelper);
+                        return;
+                    }
+
+                    //浠诲姟宸插瓨鍦紝妫�鏌ヤ换鍔℃墽琛岄棿闅旀槸鍚﹀彂鐢熷彉鍖栵紝鑻ュ彂鐢熷彉鍖栵紝閲嶇疆浠诲姟
+                    if (_dict[JobID].Instance.Expression != model.Expression)
+                    {
+                        await _dict[JobID].CancelJob();
+                        var jobHelper = new CustomCronJobHelper();
+                        await jobHelper.StartJob(model);
+                        _dict[JobID] = jobHelper;
+                    }
+
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.Error("鑷畾涔夎鍒掗噸澶嶄换鍔★紝鎵ц鍑洪敊", ex);
+                    var e = new JobExecutionException(ex);
+                    throw e;
+                }
+            });
+
+
+        }
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        /// <returns></returns>
+        public async Task CancelJob()
+        {
+            await CancelJob(JobID);
+        }
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        /// <param name="jobId">浠诲姟id</param>
+        public static async Task CancelJob(long jobId)
+        {
+            if (_dict.ContainsKey(jobId))
+            {
+                await _dict[jobId].CancelJob();
+                _dict.Remove(jobId);
+            }
+
+        }
+
+        /// <summary>
+        /// 鍙栦笅浠诲姟
+        /// </summary>
+        public static async Task CancelJobs()
+        {
+            foreach (var key in _dict.Keys)
+            {
+                await CancelJob(key);
+            }
+        }
+
+    }
+
+}
diff --git a/Server/IStation.Server.Job/custom_cron/repeat/CustomCronRepeatJobHelper.cs b/Server/IStation.Server.Job/custom_cron/repeat/CustomCronRepeatJobHelper.cs
new file mode 100644
index 0000000..049d60d
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_cron/repeat/CustomCronRepeatJobHelper.cs
@@ -0,0 +1,78 @@
+锘縰sing Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夊疄鏃堕噸澶嶄换鍔¤緟鍔╃被
+    /// </summary>
+    public class CustomCronRepeatJobHelper
+    {
+        private IScheduler _sched;//璋冨害鍣�
+
+        /// <summary>
+        /// 瀹炰緥
+        /// </summary>
+        public Model.CustomCronJob Instance { get; private set; }
+
+        /// <summary>
+        /// 寮�濮嬩换鍔�
+        /// </summary>
+        public async Task StartJob(Model.CustomCronJob instance)
+        {
+            if (_sched != null)
+                return;
+            this.Instance = instance ?? throw new ArgumentNullException();
+            var jobName = CustomCronRepeatJobNameHelper.GetJobName(instance);
+            var jobGroupName = CustomCronRepeatJobNameHelper.GetJobGroupName(instance);
+            var triggerName = CustomCronRepeatJobNameHelper.GetTriggerName(instance);
+
+            // 1.鍒涘缓scheduler鐨勫紩鐢�
+            var fac = new Quartz.Impl.StdSchedulerFactory();
+            _sched = await fac.GetScheduler();
+
+            //2.鍚姩 scheduler
+            await _sched.Start();
+
+            //3.鍒涘缓浠诲姟
+            var job = JobBuilder.Create<CustomCronRepeatJob>()
+             .WithIdentity(jobName, jobGroupName)
+             .UsingJobData("JobID", instance.ID)
+             .Build();
+
+            //4.鍒涘缓Trigger
+            var trigger = TriggerBuilder.Create()
+            .WithIdentity(triggerName, jobGroupName)
+            .WithSimpleSchedule(x => x.WithIntervalInMinutes(instance.Repeat)
+            .RepeatForever().WithMisfireHandlingInstructionNextWithRemainingCount())
+            .Build();
+
+            //5.鍔犲叆璋冨害绠$悊鍣�
+            await _sched.ScheduleJob(job, trigger);
+        }
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        public async Task CancelJob()
+        {
+            if (_sched == null)
+                return;
+            var jobGroupName = CustomCronRepeatJobNameHelper.GetJobGroupName(this.Instance);
+            var triggerName = CustomCronRepeatJobNameHelper.GetTriggerName(this.Instance);
+            var triggerKey = new TriggerKey(triggerName, jobGroupName);
+            if (await _sched.CheckExists(triggerKey))
+            {
+                await _sched.UnscheduleJob(triggerKey);
+            }
+            await CustomCronRepeatJob.CancelJob(this.Instance.ID);
+        }
+
+
+    }
+
+}
diff --git a/Server/IStation.Server.Job/custom_cron/repeat/CustomCronRepeatJobNameHelper.cs b/Server/IStation.Server.Job/custom_cron/repeat/CustomCronRepeatJobNameHelper.cs
new file mode 100644
index 0000000..a88e141
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_cron/repeat/CustomCronRepeatJobNameHelper.cs
@@ -0,0 +1,48 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夎鍒掗噸澶嶄换鍔″悕绉拌緟鍔╃被
+    /// </summary>
+    public class CustomCronRepeatJobNameHelper
+    {
+        private const string _jobNameHeader = "CustomCronRepeatJob";
+        private const string _jobGroupHeader = "CustomCronRepeatJobGroup";
+        private const string _triggerNameHeader = "CustomCronRepeatJobTrigger";
+
+        /// <summary>
+        /// 鑾峰彇浠诲姟鍚嶇О
+        /// </summary>
+        public static string GetJobName(Model.CustomCronJob rhs)
+        {
+            var name = string.Format("{0}_{1}", _jobNameHeader, rhs.ID);
+            return name;
+        }
+
+        /// <summary>
+        /// 鑾峰彇浠诲姟缁勫悕绉�
+        /// </summary>
+        public static string GetJobGroupName(Model.CustomCronJob rhs)
+        {
+            var name = string.Format("{0}_{1}", _jobGroupHeader, rhs.ID);
+            return name;
+        }
+
+        /// <summary>
+        /// 鑾峰彇瑙﹀彂鍣ㄥ悕绉�
+        /// </summary>
+        public static string GetTriggerName(Model.CustomCronJob rhs)
+        {
+            var name = string.Format("{0}_{1}", _triggerNameHeader, rhs.ID);
+            return name;
+        }
+
+
+
+    }
+}
diff --git a/Server/IStation.Server.Job/custom_cron/service/CustomCronServiceJob.cs b/Server/IStation.Server.Job/custom_cron/service/CustomCronServiceJob.cs
new file mode 100644
index 0000000..efd1000
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_cron/service/CustomCronServiceJob.cs
@@ -0,0 +1,115 @@
+锘縰sing Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夎鍒掓湇鍔′换鍔�
+    /// </summary>
+    public class CustomCronServiceJob : Quartz.IJob
+    {
+        private static List<CustomCronRepeatJobHelper> _jobHelpers = new List<CustomCronRepeatJobHelper>();
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public Task Execute(IJobExecutionContext context)
+        {
+            return Task.Run(async () =>
+            {
+                try
+                {
+                    #region 鍔犺浇鎵�鏈変换鍔�
+
+                    var jobList = new Service.CustomCronJob().GetAll();
+                    if (jobList == null || jobList.Count < 1)
+                    {
+                        LogHelper.Info("鑷畾涔夎鍒掓湇鍔′换鍔′腑锛屾湭妫�绱㈠埌鑷畾涔夎鍒掍换鍔′俊鎭�");
+                        CancelJobs();
+                        return;
+                    }
+                    jobList = jobList.Where(x => x.UseStatus == Model.eUseStatus.Enable).ToList();
+                    if (jobList.Count < 1)
+                    {
+                        LogHelper.Info("鑷畾涔夎鍒掓湇鍔′换鍔′腑锛屾湭妫�绱㈠埌鏈夋晥鑷畾涔夎鍒掍换鍔′俊鎭�");
+                        CancelJobs();
+                        return;
+                    }
+
+                    #endregion
+
+                    #region 寮�鍚换鍔�
+
+                    foreach (var job in jobList)
+                    {
+                        var jobHelper = _jobHelpers.Find(t => t.Instance.ID == job.ID);
+                        if (jobHelper == null)
+                        {
+                            jobHelper = new CustomCronRepeatJobHelper();
+                            await jobHelper.StartJob(job);
+                            _jobHelpers.Add(jobHelper);
+                            LogHelper.Info($"鑷畾涔夎鍒掓湇鍔′换鍔′腑锛欼D:{job.ID},NO:{job.NO},Name:{job.Name} 鐨勪换鍔″紑鍚紒");
+                        }
+                    }
+
+                    #endregion
+
+                    #region 鍏抽棴浠诲姟
+
+                    foreach (var jobHelper in _jobHelpers.ToList())
+                    {
+                        var job = jobList.Find(t => t.ID == jobHelper.Instance.ID);
+                        if (job == null)
+                        {
+                            await jobHelper.CancelJob();
+                            _jobHelpers.Remove(jobHelper);
+                            LogHelper.Info($"鑷畾涔夎鍒掓湇鍔′换鍔′腑锛欼D:{job.ID},NO:{job.NO},Name:{job.Name} 鐨勪换鍔″叧闂紒");
+                            continue;
+                        }
+
+                        if (job.Repeat != jobHelper.Instance.Repeat)
+                        {
+                            await jobHelper.CancelJob();
+                            _jobHelpers.Remove(jobHelper);
+                            var rJobHelper = new CustomCronRepeatJobHelper();
+                            await rJobHelper.StartJob(job);
+                            _jobHelpers.Add(rJobHelper);
+                        }
+                    }
+
+                    #endregion
+
+                    LogHelper.Info($"鑷畾涔夎鍒掓湇鍔′换鍔′腑锛屽紑鍚换鍔℃暟閲忎负{_jobHelpers.Count}!");
+
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.Error("鑷畾涔夎鍒掓湇鍔′换鍔′腑锛屾墽琛屽嚭閿�", ex);
+                    var e = new JobExecutionException(ex);
+                    throw e;
+                }
+            });
+
+
+        }
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        public static void CancelJobs()
+        {
+            if (_jobHelpers != null && _jobHelpers.Count > 0)
+            {
+                _jobHelpers.ForEach(async x => await x.CancelJob());
+                _jobHelpers.Clear();
+            }
+        }
+
+
+
+    }
+}
diff --git a/Server/IStation.Server.Job/custom_cron/service/CustomCronServiceJobHelper.cs b/Server/IStation.Server.Job/custom_cron/service/CustomCronServiceJobHelper.cs
new file mode 100644
index 0000000..d169c62
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_cron/service/CustomCronServiceJobHelper.cs
@@ -0,0 +1,70 @@
+锘縰sing Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夎鍒掓湇鍔′换鍔¤緟鍔╃被
+    /// </summary>
+    public class CustomCronServiceJobHelper : IJobHelper
+    {
+        private const string _jobName = "CustomCronServiceJobName";
+        private const string _jobGroup = "CustomCronServiceJobGroup";
+        private const string _triggerName = "CustomCronServiceJobTrigger";
+        private static IScheduler _sched;//璋冨害鍣�
+
+        /// <summary>
+        /// 寮�濮嬩换鍔�
+        /// </summary>
+        public async Task StartJob()
+        {
+            if (_sched != null)
+                return;
+
+            // 1.鍒涘缓scheduler鐨勫紩鐢�
+            var fac = new Quartz.Impl.StdSchedulerFactory();
+            _sched = await fac.GetScheduler();
+
+            //2.鍚姩 scheduler
+            await _sched.Start();
+
+            //3.鍒涘缓浠诲姟
+            var job = JobBuilder.Create<CustomCronServiceJob>()
+             .WithIdentity(_jobName, _jobGroup)
+             .Build();
+
+            //4.鍒涘缓Trigger
+            var trigger = TriggerBuilder.Create()
+            .WithIdentity(_triggerName, _jobGroup)
+            .WithSimpleSchedule(x => x.WithIntervalInMinutes(ConfigHelper.CustomCronResetThreshold)
+            .RepeatForever()
+            .WithMisfireHandlingInstructionNextWithRemainingCount())
+            .Build();
+
+            //5.鍔犲叆璋冨害绠$悊鍣�
+            await _sched.ScheduleJob(job, trigger);
+        }
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        public async Task CancelJob()
+        {
+            if (_sched == null)
+                return;
+
+            var triggerKey = new TriggerKey(_triggerName, _jobGroup);
+            if (await _sched.CheckExists(triggerKey))
+            {
+                await _sched.UnscheduleJob(triggerKey);
+            }
+            CustomCronServiceJob.CancelJobs();
+        }
+
+
+    }
+}
diff --git a/Server/IStation.Server.Job/custom_real/CustomRealJob.cs b/Server/IStation.Server.Job/custom_real/CustomRealJob.cs
deleted file mode 100644
index 7e74a54..0000000
--- a/Server/IStation.Server.Job/custom_real/CustomRealJob.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-锘縰sing System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace IStation.Server.real
-{
-    internal class Class1
-    {
-    }
-}
diff --git a/Server/IStation.Server.Job/custom_real/CustomRealJobHelper.cs b/Server/IStation.Server.Job/custom_real/CustomRealJobHelper.cs
deleted file mode 100644
index 17101d6..0000000
--- a/Server/IStation.Server.Job/custom_real/CustomRealJobHelper.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-锘縰sing System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace IStation.Server.real
-{
-    internal class CustomRealJobHelper
-    {
-    }
-}
diff --git a/Server/IStation.Server.Job/custom_real/CustomRealServiceJob.cs b/Server/IStation.Server.Job/custom_real/CustomRealServiceJob.cs
deleted file mode 100644
index 6809b5c..0000000
--- a/Server/IStation.Server.Job/custom_real/CustomRealServiceJob.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-锘縰sing System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace IStation.Server.real
-{
-    internal class CustomRealServiceJob
-    {
-    }
-}
diff --git a/Server/IStation.Server.Job/custom_real/CustomRealServiceJobHelper.cs b/Server/IStation.Server.Job/custom_real/CustomRealServiceJobHelper.cs
deleted file mode 100644
index fc004bd..0000000
--- a/Server/IStation.Server.Job/custom_real/CustomRealServiceJobHelper.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-锘縰sing System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace IStation.Server.real
-{
-    internal class CustomRealServiceJobHelper
-    {
-    }
-}
diff --git a/Server/IStation.Server.Job/custom_real/job/CustomRealJob.cs b/Server/IStation.Server.Job/custom_real/job/CustomRealJob.cs
new file mode 100644
index 0000000..3c3f7f2
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_real/job/CustomRealJob.cs
@@ -0,0 +1,50 @@
+锘縰sing Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夊疄鏃朵换鍔�
+    /// </summary>
+    public class CustomRealJob : Quartz.IJob
+    {
+        internal const string Instance = "Instance";
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public Task Execute(IJobExecutionContext context)
+        {
+            return Task.Run(() =>
+            {
+                try
+                {
+                    var dataMap = context.MergedJobDataMap;
+                    var jobModel = (Model.CustomRealJob)dataMap[Instance];
+                    if (jobModel == null)
+                        return;
+                    var jobExecuter = JobFactory.CreateJob(jobModel.Execution);
+                    if (jobExecuter == null)
+                    {
+                        LogHelper.Error($"鑷畾涔夊疄鏃朵换鍔�,ID:{jobModel.ID},NO:{jobModel.NO},Name:{jobModel.Name},Execution:{jobModel.Execution},鍒涘缓浠诲姟瀵硅薄澶辫触锛�");
+                        return;
+                    }
+                    jobExecuter.Execute();
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.Error("鑷畾涔夊疄鏃朵换鍔★紝鎵ц鍑洪敊", ex);
+                    var e = new JobExecutionException(ex);
+                    throw e;
+                }
+
+            });
+        }
+
+
+    }
+}
diff --git a/Server/IStation.Server.Job/custom_real/job/CustomRealJobHelper.cs b/Server/IStation.Server.Job/custom_real/job/CustomRealJobHelper.cs
new file mode 100644
index 0000000..088133d
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_real/job/CustomRealJobHelper.cs
@@ -0,0 +1,75 @@
+锘縰sing Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夊疄鏃朵换鍔¤緟鍔╃被
+    /// </summary>
+    public class CustomRealJobHelper
+    {
+        private IScheduler _sched;//璋冨害鍣�
+
+        /// <summary>
+        /// 瀹炰緥
+        /// </summary>
+        public Model.CustomRealJob Instance { get; private set; }
+
+        /// <summary>
+        /// 寮�濮嬩换鍔�
+        /// </summary>
+        public async Task StartJob(Model.CustomRealJob instance)
+        {
+            if (_sched != null)
+                return;
+            this.Instance = instance ?? throw new ArgumentNullException();
+            var jobName = CustomRealJobNameHelper.GetJobName(instance);
+            var jobGroupName = CustomRealJobNameHelper.GetJobGroupName(instance);
+            var triggerName = CustomRealJobNameHelper.GetTriggerName(instance);
+
+            // 1.鍒涘缓scheduler鐨勫紩鐢�
+            var fac = new Quartz.Impl.StdSchedulerFactory();
+            _sched = await fac.GetScheduler();
+
+            //2.鍚姩 scheduler
+            await _sched.Start();
+
+            //3.鍒涘缓浠诲姟
+            var job = JobBuilder.Create<CustomRealJob>()
+             .WithIdentity(jobName, jobGroupName)
+             .Build();
+            job.JobDataMap.Put(CustomRealJob.Instance, instance);
+
+            //4.鍒涘缓Trigger
+            var trigger = TriggerBuilder.Create()
+            .WithIdentity(triggerName, jobGroupName)
+            .WithSimpleSchedule(x => x.WithIntervalInSeconds(instance.Interval)
+            .RepeatForever().WithMisfireHandlingInstructionNextWithRemainingCount())
+            .Build();
+
+            //5.鍔犲叆璋冨害绠$悊鍣�
+            await _sched.ScheduleJob(job, trigger);
+        }
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        public async Task CancelJob()
+        {
+            if (_sched == null)
+                return;
+            var jobGroupName = CustomRealJobNameHelper.GetJobGroupName(this.Instance);
+            var triggerName = CustomRealJobNameHelper.GetTriggerName(this.Instance);
+            var triggerKey = new TriggerKey(triggerName, jobGroupName);
+            if (await _sched.CheckExists(triggerKey))
+            {
+                await _sched.UnscheduleJob(triggerKey);
+            }
+        }
+
+    }
+}
diff --git a/Server/IStation.Server.Job/custom_real/job/CustomRealJobNameHelper.cs b/Server/IStation.Server.Job/custom_real/job/CustomRealJobNameHelper.cs
new file mode 100644
index 0000000..9b98244
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_real/job/CustomRealJobNameHelper.cs
@@ -0,0 +1,46 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夊疄鏃朵换鍔″悕绉拌緟鍔╃被
+    /// </summary>
+    public class CustomRealJobNameHelper
+    {
+        private const string _jobNameHeader = "CustomRealJob";
+        private const string _jobGroupHeader = "CustomRealJobGroup";
+        private const string _triggerNameHeader = "CustomRealJobTrigger";
+
+        /// <summary>
+        /// 鑾峰彇浠诲姟鍚嶇О
+        /// </summary>
+        public static string GetJobName(Model.CustomRealJob rhs)
+        {
+            var name = string.Format("{0}_{1}", _jobNameHeader, rhs.ID);
+            return name;
+        }
+
+        /// <summary>
+        /// 鑾峰彇浠诲姟缁勫悕绉�
+        /// </summary>
+        public static string GetJobGroupName(Model.CustomRealJob rhs)
+        {
+            var name = string.Format("{0}_{1}", _jobGroupHeader, rhs.ID);
+            return name;
+        }
+
+        /// <summary>
+        /// 鑾峰彇瑙﹀彂鍣ㄥ悕绉�
+        /// </summary>
+        public static string GetTriggerName(Model.CustomRealJob rhs)
+        {
+            var name = string.Format("{0}_{1}", _triggerNameHeader, rhs.ID);
+            return name;
+        }
+
+    }
+}
diff --git a/Server/IStation.Server.Job/custom_real/repeat/CustomRealRepeatJob.cs b/Server/IStation.Server.Job/custom_real/repeat/CustomRealRepeatJob.cs
new file mode 100644
index 0000000..4585257
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_real/repeat/CustomRealRepeatJob.cs
@@ -0,0 +1,112 @@
+锘縰sing Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夊疄鏃堕噸澶嶄换鍔�
+    /// </summary>
+    public class CustomRealRepeatJob : Quartz.IJob
+    {
+        //鑷畾涔夊疄鏃朵换鍔″瓧鍏�
+        private static Dictionary<long,CustomRealJobHelper> _dict=new Dictionary<long, CustomRealJobHelper>();
+
+        /// <summary>
+        /// 浠诲姟id 
+        /// </summary>
+        public long JobID { get; set; }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public Task Execute(IJobExecutionContext context)
+        {
+            return Task.Run(async () =>
+            {
+                try
+                {
+                    //鑾峰彇浠诲姟
+                    var model = new Service.CustomRealJob().GetByID(JobID);
+
+                    //鑾峰彇浠诲姟澶辫触鍚庯紝鍋滄浠诲姟骞剁Щ闄や换鍔¤緟鍔╃被
+                    if (model == null)
+                    {
+                        if (_dict.ContainsKey(JobID))
+                        {
+                            await _dict[JobID].CancelJob();
+                            _dict.Remove(JobID);
+                        }
+                        return;
+                    }
+
+                    //灏氭湭鍒涘缓瀹炴椂浠诲姟鏃讹紝鍒涘缓瀹炴椂浠诲姟杈呭姪绫�
+                    if (!_dict.ContainsKey(JobID))
+                    {
+                        var jobHelper = new CustomRealJobHelper();
+                        await jobHelper.StartJob(model);
+                        _dict.Add(JobID, jobHelper);
+                        return;
+                    }
+
+                    //浠诲姟宸插瓨鍦紝妫�鏌ヤ换鍔℃墽琛岄棿闅旀槸鍚﹀彂鐢熷彉鍖栵紝鑻ュ彂鐢熷彉鍖栵紝閲嶇疆浠诲姟
+                    if (_dict[JobID].Instance.Interval != model.Interval)
+                    {
+                        await _dict[JobID].CancelJob();
+                        var jobHelper = new CustomRealJobHelper();
+                        await jobHelper.StartJob(model);
+                        _dict[JobID] = jobHelper;
+                    }
+
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.Error("鑷畾涔夊疄鏃堕噸澶嶄换鍔★紝鎵ц鍑洪敊", ex);
+                    var e = new JobExecutionException(ex);
+                    throw e;
+                }
+            });
+
+
+        }
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        /// <returns></returns>
+        public async Task CancelJob()
+        {
+            await CancelJob(JobID);
+        }
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        /// <param name="jobId">浠诲姟id</param>
+        public static async Task CancelJob(long jobId)
+        {
+            if (_dict.ContainsKey(jobId))
+            {
+                await _dict[jobId].CancelJob();
+                _dict.Remove(jobId);
+            }
+
+        }
+
+        /// <summary>
+        /// 鍙栦笅浠诲姟
+        /// </summary>
+        public static async Task CancelJobs()
+        {
+            foreach (var key in _dict.Keys)
+            {
+                await CancelJob(key);
+            }
+        }
+
+    }
+
+}
diff --git a/Server/IStation.Server.Job/custom_real/repeat/CustomRealRepeatJobHelper.cs b/Server/IStation.Server.Job/custom_real/repeat/CustomRealRepeatJobHelper.cs
new file mode 100644
index 0000000..2354ebd
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_real/repeat/CustomRealRepeatJobHelper.cs
@@ -0,0 +1,78 @@
+锘縰sing Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夊疄鏃堕噸澶嶄换鍔¤緟鍔╃被
+    /// </summary>
+    public class CustomRealRepeatJobHelper
+    {
+        private IScheduler _sched;//璋冨害鍣�
+
+        /// <summary>
+        /// 瀹炰緥
+        /// </summary>
+        public Model.CustomRealJob Instance { get; private set; }
+
+        /// <summary>
+        /// 寮�濮嬩换鍔�
+        /// </summary>
+        public async Task StartJob(Model.CustomRealJob instance)
+        {
+            if (_sched != null)
+                return;
+            this.Instance = instance ?? throw new ArgumentNullException();
+            var jobName = CustomRealRepeatJobNameHelper.GetJobName(instance);
+            var jobGroupName = CustomRealRepeatJobNameHelper.GetJobGroupName(instance);
+            var triggerName = CustomRealRepeatJobNameHelper.GetTriggerName(instance);
+
+            // 1.鍒涘缓scheduler鐨勫紩鐢�
+            var fac = new Quartz.Impl.StdSchedulerFactory();
+            _sched = await fac.GetScheduler();
+
+            //2.鍚姩 scheduler
+            await _sched.Start();
+
+            //3.鍒涘缓浠诲姟
+            var job = JobBuilder.Create<CustomRealRepeatJob>()
+             .WithIdentity(jobName, jobGroupName)
+             .UsingJobData("JobID", instance.ID)
+             .Build();
+
+            //4.鍒涘缓Trigger
+            var trigger = TriggerBuilder.Create()
+            .WithIdentity(triggerName, jobGroupName)
+            .WithSimpleSchedule(x => x.WithIntervalInMinutes(instance.Repeat)
+            .RepeatForever().WithMisfireHandlingInstructionNextWithRemainingCount())
+            .Build();
+
+            //5.鍔犲叆璋冨害绠$悊鍣�
+            await _sched.ScheduleJob(job, trigger);
+        }
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        public async Task CancelJob()
+        {
+            if (_sched == null)
+                return;
+            var jobGroupName = CustomRealRepeatJobNameHelper.GetJobGroupName(this.Instance);
+            var triggerName = CustomRealRepeatJobNameHelper.GetTriggerName(this.Instance);
+            var triggerKey = new TriggerKey(triggerName, jobGroupName);
+            if (await _sched.CheckExists(triggerKey))
+            {
+                await _sched.UnscheduleJob(triggerKey);
+            }
+            await CustomRealRepeatJob.CancelJob(this.Instance.ID);
+        }
+
+
+    }
+
+}
diff --git a/Server/IStation.Server.Job/custom_real/repeat/CustomRealRepeatJobNameHelper.cs b/Server/IStation.Server.Job/custom_real/repeat/CustomRealRepeatJobNameHelper.cs
new file mode 100644
index 0000000..cd1b585
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_real/repeat/CustomRealRepeatJobNameHelper.cs
@@ -0,0 +1,48 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夊疄鏃堕噸澶嶄换鍔″悕绉拌緟鍔╃被
+    /// </summary>
+    public class CustomRealRepeatJobNameHelper
+    {
+        private const string _jobNameHeader = "CustomRealRepeatJob";
+        private const string _jobGroupHeader = "CustomRealRepeatJobGroup";
+        private const string _triggerNameHeader = "CustomRealRepeatJobTrigger";
+
+        /// <summary>
+        /// 鑾峰彇浠诲姟鍚嶇О
+        /// </summary>
+        public static string GetJobName(Model.CustomRealJob rhs)
+        {
+            var name = string.Format("{0}_{1}", _jobNameHeader, rhs.ID);
+            return name;
+        }
+
+        /// <summary>
+        /// 鑾峰彇浠诲姟缁勫悕绉�
+        /// </summary>
+        public static string GetJobGroupName(Model.CustomRealJob rhs)
+        {
+            var name = string.Format("{0}_{1}", _jobGroupHeader, rhs.ID);
+            return name;
+        }
+
+        /// <summary>
+        /// 鑾峰彇瑙﹀彂鍣ㄥ悕绉�
+        /// </summary>
+        public static string GetTriggerName(Model.CustomRealJob rhs)
+        {
+            var name = string.Format("{0}_{1}", _triggerNameHeader, rhs.ID);
+            return name;
+        }
+
+
+
+    }
+}
diff --git a/Server/IStation.Server.Job/custom_real/service/CustomRealServiceJob.cs b/Server/IStation.Server.Job/custom_real/service/CustomRealServiceJob.cs
new file mode 100644
index 0000000..b94c87a
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_real/service/CustomRealServiceJob.cs
@@ -0,0 +1,115 @@
+锘縰sing Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夊疄鏃舵湇鍔′换鍔�
+    /// </summary>
+    public class CustomRealServiceJob:Quartz.IJob
+    {
+        private static List<CustomRealRepeatJobHelper> _jobHelpers = new List<CustomRealRepeatJobHelper>();
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public Task Execute(IJobExecutionContext context)
+        {
+            return Task.Run(async () =>
+            {
+                try
+                {
+                    #region 鍔犺浇鎵�鏈変换鍔�
+
+                    var jobList = new Service.CustomRealJob().GetAll();
+                    if (jobList == null || jobList.Count < 1)
+                    {
+                        LogHelper.Info("鑷畾涔夊疄鏃舵湇鍔′换鍔′腑锛屾湭妫�绱㈠埌鑷畾涔夊疄鏃朵换鍔′俊鎭�");
+                        CancelJobs();
+                        return;
+                    }
+                    jobList = jobList.Where(x => x.UseStatus == Model.eUseStatus.Enable).ToList();
+                    if (jobList.Count < 1)
+                    {
+                        LogHelper.Info("鑷畾涔夊疄鏃舵湇鍔′换鍔′腑锛屾湭妫�绱㈠埌鏈夋晥鑷畾涔夊疄鏃朵换鍔′俊鎭�");
+                        CancelJobs();
+                        return;
+                    }
+
+                    #endregion
+
+                    #region 寮�鍚换鍔�
+
+                    foreach (var job in jobList)
+                    {
+                        var jobHelper = _jobHelpers.Find(t => t.Instance.ID == job.ID);
+                        if (jobHelper == null)
+                        {
+                            jobHelper = new CustomRealRepeatJobHelper();
+                            await jobHelper.StartJob(job);
+                            _jobHelpers.Add(jobHelper);
+                            LogHelper.Info($"鑷畾涔夊疄鏃舵湇鍔′换鍔′腑锛欼D:{job.ID},NO:{job.NO},Name:{job.Name} 鐨勪换鍔″紑鍚紒");
+                        }
+                    }
+
+                    #endregion
+
+                    #region 鍏抽棴浠诲姟
+
+                    foreach (var jobHelper in _jobHelpers.ToList())
+                    {
+                        var job = jobList.Find(t=>t.ID==jobHelper.Instance.ID);
+                        if (job == null)
+                        {
+                            await jobHelper.CancelJob();
+                            _jobHelpers.Remove(jobHelper);
+                            LogHelper.Info($"鑷畾涔夊疄鏃舵湇鍔′换鍔′腑锛欼D:{job.ID},NO:{job.NO},Name:{job.Name} 鐨勪换鍔″叧闂紒");
+                            continue;
+                        }
+
+                        if (job.Repeat != jobHelper.Instance.Repeat)
+                        {
+                            await jobHelper.CancelJob();
+                            _jobHelpers.Remove(jobHelper);
+                            var rJobHelper = new CustomRealRepeatJobHelper();
+                            await rJobHelper.StartJob(job);
+                            _jobHelpers.Add(rJobHelper);
+                        }
+                    }
+
+                    #endregion
+
+                    LogHelper.Info($"鑷畾涔夊疄鏃舵湇鍔′换鍔′腑锛屽紑鍚换鍔℃暟閲忎负{_jobHelpers.Count}!");
+
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.Error("鑷畾涔夊疄鏃舵湇鍔′换鍔′腑锛屾墽琛屽嚭閿�", ex);
+                    var e = new JobExecutionException(ex);
+                    throw e;
+                }
+            });
+
+          
+        }
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        public static void CancelJobs()
+        {
+            if (_jobHelpers != null && _jobHelpers.Count > 0)
+            {
+                _jobHelpers.ForEach(async x => await x.CancelJob());
+                _jobHelpers.Clear();
+            }
+        }
+
+
+
+    }
+}
diff --git a/Server/IStation.Server.Job/custom_real/service/CustomRealServiceJobHelper.cs b/Server/IStation.Server.Job/custom_real/service/CustomRealServiceJobHelper.cs
new file mode 100644
index 0000000..1d7c19c
--- /dev/null
+++ b/Server/IStation.Server.Job/custom_real/service/CustomRealServiceJobHelper.cs
@@ -0,0 +1,70 @@
+锘縰sing Quartz;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 鑷畾涔夊疄鏃舵湇鍔′换鍔¤緟鍔╃被
+    /// </summary>
+    public class CustomRealServiceJobHelper : IJobHelper
+    {
+        private const string _jobName = "CustomRealServiceJobName";
+        private const string _jobGroup = "CustomRealServiceJobGroup";
+        private const string _triggerName = "CustomRealServiceJobTrigger";
+        private static IScheduler _sched;//璋冨害鍣�
+
+        /// <summary>
+        /// 寮�濮嬩换鍔�
+        /// </summary>
+        public async Task StartJob()
+        {
+            if (_sched != null)
+                return;
+
+            // 1.鍒涘缓scheduler鐨勫紩鐢�
+            var fac = new Quartz.Impl.StdSchedulerFactory();
+            _sched = await fac.GetScheduler();
+
+            //2.鍚姩 scheduler
+            await _sched.Start();
+
+            //3.鍒涘缓浠诲姟
+            var job = JobBuilder.Create<CustomRealServiceJob>()
+             .WithIdentity(_jobName, _jobGroup)
+             .Build();
+
+            //4.鍒涘缓Trigger
+            var trigger = TriggerBuilder.Create()
+            .WithIdentity(_triggerName, _jobGroup)
+            .WithSimpleSchedule(x => x.WithIntervalInMinutes(ConfigHelper.CustomRealResetThreshold)
+            .RepeatForever()
+            .WithMisfireHandlingInstructionNextWithRemainingCount())
+            .Build();
+
+            //5.鍔犲叆璋冨害绠$悊鍣�
+            await _sched.ScheduleJob(job, trigger);
+        }
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        public async Task CancelJob()
+        {
+            if (_sched == null)
+                return;
+
+            var triggerKey = new TriggerKey(_triggerName, _jobGroup);
+            if (await _sched.CheckExists(triggerKey))
+            {
+                await _sched.UnscheduleJob(triggerKey);
+            }
+            CustomRealServiceJob.CancelJobs();
+        }
+
+
+    }
+}
diff --git a/Server/IStation.Server.Job/helpers/ConfigHelper.cs b/Server/IStation.Server.Job/helpers/ConfigHelper.cs
new file mode 100644
index 0000000..9c9121f
--- /dev/null
+++ b/Server/IStation.Server.Job/helpers/ConfigHelper.cs
@@ -0,0 +1,29 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    internal class ConfigHelper
+    {
+        /// <summary>
+        /// 鑷畾涔夊疄鏃堕噸缃榾鍊�
+        /// </summary>
+        public static int CustomRealResetThreshold
+        {
+            get { return Settings.Job.Execution.CustomRealResetThreshold; }
+        }
+
+        /// <summary>
+        /// 鑷畾涔夎鍒掗噸缃榾鍊�
+        /// </summary>
+        public static int CustomCronResetThreshold
+        {
+            get { return Settings.Job.Execution.CustomCronResetThreshold; }
+        }
+
+
+    }
+}
diff --git a/Server/IStation.Server.Job/helpers/JobHelper.cs b/Server/IStation.Server.Job/helpers/JobHelper.cs
new file mode 100644
index 0000000..757ccc0
--- /dev/null
+++ b/Server/IStation.Server.Job/helpers/JobHelper.cs
@@ -0,0 +1,37 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 浠诲姟杈呭姪绫�
+    /// </summary>
+    public class JobHelper
+    {
+        private List<IJobHelper> _jobHelpers = new List<IJobHelper>()
+        {
+            new CustomCronServiceJobHelper(),
+            new CustomRealServiceJobHelper()
+        };
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public void StartJob()
+        {
+            _jobHelpers.ForEach(async x => await x.StartJob());
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public void CancelJob()
+        {
+            _jobHelpers.ForEach(async x => await x.CancelJob());
+        }
+
+    }
+}
diff --git a/Server/IStation.Server.Job/interface/IJobHelper.cs b/Server/IStation.Server.Job/interface/IJobHelper.cs
new file mode 100644
index 0000000..6aaa888
--- /dev/null
+++ b/Server/IStation.Server.Job/interface/IJobHelper.cs
@@ -0,0 +1,27 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation.Server
+{
+    /// <summary>
+    /// 浠诲姟杈呭姪绫绘帴鍙�
+    /// </summary>
+    public interface IJobHelper
+    {
+        /// <summary>
+        /// 寮�濮嬩换鍔�
+        /// </summary>
+        Task StartJob();
+
+        /// <summary>
+        /// 鍙栨秷浠诲姟
+        /// </summary>
+        Task CancelJob();
+
+    }
+
+
+}
diff --git a/Settings/IStation.Settings/models/job/Paras_Job.cs b/Settings/IStation.Settings/models/job/Paras_Job.cs
index 3842856..5f5e704 100644
--- a/Settings/IStation.Settings/models/job/Paras_Job.cs
+++ b/Settings/IStation.Settings/models/job/Paras_Job.cs
@@ -15,5 +15,10 @@
         /// 鏁版嵁搴�
         /// </summary>
         public Paras_Job_DataBase DataBase { get; set; }
+
+        /// <summary>
+        /// 鎵ц鍙傛暟
+        /// </summary>
+        public Paras_Job_Execution Execution { get; set; }
     }
 }
diff --git a/Settings/IStation.Settings/models/job/Paras_Job_Execution.cs b/Settings/IStation.Settings/models/job/Paras_Job_Execution.cs
new file mode 100644
index 0000000..6ff3200
--- /dev/null
+++ b/Settings/IStation.Settings/models/job/Paras_Job_Execution.cs
@@ -0,0 +1,26 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace IStation
+{
+    /// <summary>
+    /// 浠诲姟鎵ц鍙傛暟
+    /// </summary>
+    public class Paras_Job_Execution
+    {
+        /// <summary>
+        /// 鑷畾涔夊疄鏃堕噸缃榾鍊硷紙鍒嗛挓锛�
+        /// </summary>
+        public int CustomRealResetThreshold { get; set; }
+
+        /// <summary>
+        /// 鑷畾涔夎鍒掗噸缃榾鍊硷紙鍒嗛挓锛�
+        /// </summary>
+        public int CustomCronResetThreshold { get; set; }
+
+    }
+
+}
diff --git a/Settings/IStation.Settings/paras_settings.json b/Settings/IStation.Settings/paras_settings.json
index 3ed60ae..789080a 100644
--- a/Settings/IStation.Settings/paras_settings.json
+++ b/Settings/IStation.Settings/paras_settings.json
@@ -163,6 +163,10 @@
   "Job": {
     "DataBase": {
       "ConnectString": "PORT=5432;DATABASE=istation_job_test;HOST=101.133.223.111;PASSWORD=Eventech2010;USER ID=postgres;"
+    },
+    "Execution": {
+      "CustomRealResetThreshold": 60,
+      "CustomCronResetThreshold": 1440
     }
   },
 
diff --git a/TopShelf/IStation.TopShelf.Job/IStation.TopShelf.Job.csproj b/TopShelf/IStation.TopShelf.Job/IStation.TopShelf.Job.csproj
new file mode 100644
index 0000000..c1f99ff
--- /dev/null
+++ b/TopShelf/IStation.TopShelf.Job/IStation.TopShelf.Job.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\Component\IStation.TopShelf\IStation.TopShelf.csproj" />
+    <ProjectReference Include="..\..\Server\IStation.Server.Job\IStation.Server.Job.csproj" />
+  </ItemGroup>
+
+</Project>
diff --git a/TopShelf/IStation.TopShelf.Job/Program.cs b/TopShelf/IStation.TopShelf.Job/Program.cs
new file mode 100644
index 0000000..e79ec33
--- /dev/null
+++ b/TopShelf/IStation.TopShelf.Job/Program.cs
@@ -0,0 +1,16 @@
+锘�// See https://aka.ms/new-console-template for more information
+using IStation.TopShelf;
+using Topshelf;
+
+HostFactory.Run(x =>
+{
+    x.Service<Service>();
+    x.RunAsLocalSystem();
+    x.SetDescription("鏅烘収娉电珯Core鐗堜换鍔$▼搴�");
+    x.SetDisplayName("IStaion.Core.Server.Job");
+    x.SetServiceName("IStaion.Core.Server.Job");
+    x.EnableServiceRecovery(r => r.RestartService(TimeSpan.FromSeconds(120)));
+    x.StartAutomatically();
+
+
+});
\ No newline at end of file
diff --git a/TopShelf/IStation.TopShelf.Job/Service.cs b/TopShelf/IStation.TopShelf.Job/Service.cs
new file mode 100644
index 0000000..ed38665
--- /dev/null
+++ b/TopShelf/IStation.TopShelf.Job/Service.cs
@@ -0,0 +1,40 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Topshelf;
+using IStation.Server;
+
+namespace IStation.TopShelf
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public class Service : ServiceControl
+    {
+        private readonly JobHelper _jobHelper = new JobHelper();
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="hostControl"></param>
+        /// <returns></returns>
+        public bool Start(HostControl hostControl)
+        {
+            _jobHelper.StartJob();
+            return true;
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="hostControl"></param>
+        /// <returns></returns>
+        public bool Stop(HostControl hostControl)
+        {
+            _jobHelper.CancelJob();
+            return true;
+        }
+    }
+}

--
Gitblit v1.9.3