فهرست منبع

升级furion v4.8.0,定时任务替换成Sundial(待完善)

zuohuaijun 3 سال پیش
والد
کامیت
cdfa67e247

+ 4 - 4
Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj

@@ -14,9 +14,9 @@
   <ItemGroup>
     <PackageReference Include="AngleSharp" Version="0.17.1" />
     <PackageReference Include="AspNetCoreRateLimit" Version="4.0.2" />
-    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.7.9" />
-    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.7.9" />
-    <PackageReference Include="Furion.Pure" Version="4.7.9" />
+    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.8.0" />
+    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.8.0" />
+    <PackageReference Include="Furion.Pure" Version="4.8.0" />
     <PackageReference Include="Lazy.Captcha.Core" Version="1.1.6" />
     <PackageReference Include="Magicodes.IE.Excel" Version="2.7.0" />
     <PackageReference Include="Magicodes.IE.Pdf" Version="2.7.0" />
@@ -31,7 +31,7 @@
     <PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.23" />
     <PackageReference Include="UAParser" Version="3.1.47" />
     <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
-    <PackageReference Include="Masuit.Tools.Core" Version="2.5.7.7" />
+    <PackageReference Include="Masuit.Tools.Core" Version="2.5.8" />
   </ItemGroup>
 
   <ItemGroup>

+ 290 - 186
Admin.NET/Admin.NET.Core/Admin.NET.Core.xml

@@ -307,6 +307,16 @@
             租户Id
             </summary>
         </member>
+        <member name="T:Admin.NET.Core.BaseId">
+            <summary>
+            框架实体基类自增Id
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.BaseId.Id">
+            <summary>
+            Id
+            </summary>
+        </member>
         <member name="T:Admin.NET.Core.SysCodeGen">
             <summary>
             代码生成表
@@ -637,6 +647,186 @@
             外链地址-OSS上传后生成外链地址方便前端预览
             </summary>
         </member>
+        <member name="T:Admin.NET.Core.SysJobCluster">
+            <summary>
+            系统作业集群表
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobCluster.ClusterId">
+            <summary>
+            作业集群Id
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobCluster.Description">
+            <summary>
+            描述信息
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobCluster.Status">
+            <summary>
+            状态
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobCluster.UpdatedTime">
+            <summary>
+            更新时间
+            </summary>
+        </member>
+        <member name="T:Admin.NET.Core.SysJobDetail">
+            <summary>
+            系统作业信息表
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobDetail.JobId">
+            <summary>
+            作业Id
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobDetail.GroupName">
+            <summary>
+            组名称
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobDetail.JobType">
+            <summary>
+            作业类型FullName
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobDetail.AssemblyName">
+            <summary>
+            程序集Name
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobDetail.Description">
+            <summary>
+            描述信息
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobDetail.Concurrent">
+            <summary>
+            是否并行执行
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobDetail.IncludeAnnotations">
+            <summary>
+            是否扫描特性触发器
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobDetail.Properties">
+            <summary>
+            额外数据
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobDetail.UpdatedTime">
+            <summary>
+            更新时间
+            </summary>
+        </member>
+        <member name="T:Admin.NET.Core.SysJobTrigger">
+            <summary>
+            系统作业触发器表
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.TriggerId">
+            <summary>
+            触发器Id
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.JobId">
+            <summary>
+            作业Id
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.TriggerType">
+            <summary>
+            触发器类型FullName
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.AssemblyName">
+            <summary>
+            程序集Name
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.Args">
+            <summary>
+            参数
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.Description">
+            <summary>
+            描述信息
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.Status">
+            <summary>
+            状态
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.StartTime">
+            <summary>
+            起始时间
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.EndTime">
+            <summary>
+            结束时间
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.LastRunTime">
+            <summary>
+            最近运行时间
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.NextRunTime">
+            <summary>
+            下一次运行时间
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.NumberOfRuns">
+            <summary>
+            触发次数
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.MaxNumberOfRuns">
+            <summary>
+            最大触发次数(0:不限制,n:N次)
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.NumberOfErrors">
+            <summary>
+            出错次数
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.MaxNumberOfErrors">
+            <summary>
+            最大出错次数(0:不限制,n:N次)
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.NumRetries">
+            <summary>
+            重试次数
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.RetryTimeout">
+            <summary>
+            重试间隔时间(ms)
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.StartNow">
+            <summary>
+            是否立即启动
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.RunOnStart">
+            <summary>
+            是否启动时执行一次
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysJobTrigger.UpdatedTime">
+            <summary>
+            更新时间
+            </summary>
+        </member>
         <member name="T:Admin.NET.Core.SysLogAudit">
             <summary>
             系统审计日志表
@@ -1362,77 +1552,6 @@
             状态
             </summary>
         </member>
-        <member name="T:Admin.NET.Core.SysTimer">
-            <summary>
-            定时任务
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.TimerName">
-            <summary>
-            任务名称
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.DoOnce">
-            <summary>
-            是否只执行一次
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.StartNow">
-            <summary>
-            是否立即执行
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.ExecuteType">
-            <summary>
-            执行类型(串行并行)
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.Interval">
-            <summary>
-            执行间隔(单位秒)
-            </summary>
-            <example>5</example>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.Cron">
-            <summary>
-            Cron表达式
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.TimerType">
-            <summary>
-            定时器类型
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.RequestUrl">
-            <summary>
-            请求url
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.RequestType">
-            <summary>
-            请求类型
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.RequestPara">
-            <summary>
-            请求参数
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.Headers">
-            <summary>
-            Headers参数 比如{"Authorization":"userpassword"}
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.Order">
-            <summary>
-            排序
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.SysTimer.Remark">
-            <summary>
-            备注
-            </summary>
-        </member>
         <member name="T:Admin.NET.Core.SysUser">
             <summary>
             系统用户表
@@ -3250,24 +3369,7 @@
         </member>
         <member name="T:Admin.NET.Core.LogJob">
             <summary>
-            任务调度
-            </summary>
-        </member>
-        <member name="M:Admin.NET.Core.LogJob.ClearLogJob(Furion.TaskScheduler.SpareTimer,System.Int64)">
-            <summary>
-            日志删除定时器
-            </summary>
-            <param name="timer"></param>
-            <param name="count"></param>
-        </member>
-        <member name="T:Admin.NET.Core.Job.OnlineUserJob">
-            <summary>
-            在线用户任务调度
-            </summary>
-        </member>
-        <member name="M:Admin.NET.Core.Job.OnlineUserJob.ClearOnlineUser(Furion.TaskScheduler.SpareTimer,System.Int64)">
-            <summary>
-            服务重启清空在线用户(防止僵尸用户,掉线用户会自动重连)
+            清理日志作业任务
             </summary>
         </member>
         <member name="T:Admin.NET.Core.DatabaseLoggingWriter">
@@ -4956,6 +5058,100 @@
             <param name="file"></param>
             <returns></returns>
         </member>
+        <member name="T:Admin.NET.Core.Service.DbJobPersistence">
+            <summary>
+            作业持久化(数据库)
+            </summary>
+        </member>
+        <member name="M:Admin.NET.Core.Service.DbJobPersistence.OnChanged(Furion.Schedule.PersistenceContext)">
+            <summary>
+            作业计划Scheduler的JobDetail变化时
+            </summary>
+            <param name="context"></param>
+        </member>
+        <member name="M:Admin.NET.Core.Service.DbJobPersistence.OnTriggerChanged(Furion.Schedule.PersistenceTriggerContext)">
+            <summary>
+            作业计划Scheduler的触发器Trigger变化时
+            </summary>
+            <param name="context"></param>
+        </member>
+        <member name="M:Admin.NET.Core.Service.DbJobPersistence.Preload(System.String,Furion.Schedule.SchedulerBuilder)">
+            <summary>
+            作业调度器服务启动时(通常用来同步持久化数据到内存中)
+            </summary>
+            <param name="jobId"></param>
+            <param name="builder"></param>
+            <returns></returns>
+        </member>
+        <member name="P:Admin.NET.Core.Service.JobInput.Name">
+            <summary>
+            作业任务名称
+            </summary>
+        </member>
+        <member name="T:Admin.NET.Core.Service.JobClusterServer">
+            <summary>
+            作业集群控制
+            </summary>
+        </member>
+        <member name="M:Admin.NET.Core.Service.JobClusterServer.Start(Furion.Schedule.JobClusterContext)">
+            <summary>
+            当前作业调度器启动通知
+            </summary>
+            <param name="context">作业集群服务上下文</param>
+        </member>
+        <member name="M:Admin.NET.Core.Service.JobClusterServer.WaitingForAsync(Furion.Schedule.JobClusterContext)">
+            <summary>
+            等待被唤醒
+            </summary>
+            <param name="context">作业集群服务上下文</param>
+            <returns><see cref="T:System.Threading.Tasks.Task"/></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.JobClusterServer.Stop(Furion.Schedule.JobClusterContext)">
+            <summary>
+            当前作业调度器停止通知
+            </summary>
+            <param name="context">作业集群服务上下文</param>
+        </member>
+        <member name="M:Admin.NET.Core.Service.JobClusterServer.Crash(Furion.Schedule.JobClusterContext)">
+            <summary>
+            当前作业调度器宕机
+            </summary>
+            <param name="context">作业集群服务上下文</param>
+        </member>
+        <member name="M:Admin.NET.Core.Service.JobClusterServer.WorkNowAsync(System.String)">
+            <summary>
+            指示集群可以工作
+            </summary>
+            <param name="clusterId">集群 Id</param>
+            <returns></returns>
+        </member>
+        <member name="T:Admin.NET.Core.Service.JobExecutor">
+            <summary>
+            作业执行器
+            </summary>
+        </member>
+        <member name="T:Admin.NET.Core.Service.SysJobService">
+            <summary>
+            系统作业任务服务
+            </summary>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysJobService.GetJobList">
+            <summary>
+            获取所有作业任务列表
+            </summary>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysJobService.AddJob">
+            <summary>
+            增加作业任务
+            </summary>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysJobService.DeleteJob(Admin.NET.Core.Service.JobInput)">
+            <summary>
+            删除作业任务
+            </summary>
+            <returns></returns>
+        </member>
         <member name="P:Admin.NET.Core.Service.PageLogInput.StartTime">
             <summary>
             开始时间
@@ -6014,103 +6210,6 @@
             <param name="input"></param>
             <returns></returns>
         </member>
-        <member name="P:Admin.NET.Core.Service.PageTimerInput.TimerName">
-            <summary>
-            任务名称
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.Service.AddTimerInput.TimerName">
-            <summary>
-            任务名称
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.Service.SetTimerStatusInput.TimerName">
-            <summary>
-            任务名称
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.Service.SetTimerStatusInput.Status">
-            <summary>
-            任务状态
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.Service.TimerMethod.MethodName">
-            <summary>
-            方法名
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.Service.TimerMethod.DeclaringType">
-            <summary>
-            所属类
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.Service.TimerOutput.Tally">
-            <summary>
-            执行次数
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.Service.TimerOutput.Status">
-            <summary>
-            任务状态
-            </summary>
-        </member>
-        <member name="P:Admin.NET.Core.Service.TimerOutput.Exception">
-            <summary>
-            异常信息
-            </summary>
-        </member>
-        <member name="T:Admin.NET.Core.Service.SysTimerService">
-            <summary>
-            系统定时任务服务
-            </summary>
-        </member>
-        <member name="M:Admin.NET.Core.Service.SysTimerService.GetTimerPage(Admin.NET.Core.Service.PageTimerInput)">
-            <summary>
-            获取任务分页列表
-            </summary>
-            <param name="input"></param>
-            <returns></returns>
-        </member>
-        <member name="M:Admin.NET.Core.Service.SysTimerService.AddTimer(Admin.NET.Core.Service.AddTimerInput)">
-            <summary>
-            增加任务
-            </summary>
-            <param name="input"></param>
-            <returns></returns>
-        </member>
-        <member name="M:Admin.NET.Core.Service.SysTimerService.DeleteTimer(Admin.NET.Core.Service.DeleteTimerInput)">
-            <summary>
-            删除任务
-            </summary>
-            <param name="input"></param>
-            <returns></returns>
-        </member>
-        <member name="M:Admin.NET.Core.Service.SysTimerService.UpdateTimber(Admin.NET.Core.Service.UpdateTimerInput)">
-            <summary>
-            更新任务
-            </summary>
-            <param name="input"></param>
-            <returns></returns>
-        </member>
-        <member name="M:Admin.NET.Core.Service.SysTimerService.SetStatusTimer(Admin.NET.Core.Service.SetTimerStatusInput)">
-            <summary>
-            设置任务状态
-            </summary>
-            <param name="input"></param>
-            <returns></returns>
-        </member>
-        <member name="M:Admin.NET.Core.Service.SysTimerService.CreateTimer(Admin.NET.Core.SysTimer)">
-            <summary>
-            创建定时任务
-            </summary>
-            <param name="input"></param>
-        </member>
-        <member name="M:Admin.NET.Core.Service.SysTimerService.GetTimerMethodList">
-            <summary>
-            获取所有定时任务方法列表(贴spareTime特性)
-            </summary>
-            <returns></returns>
-        </member>
         <member name="P:Admin.NET.Core.Service.UserExtOrgInput.OrgId">
             <summary>
             机构Id
@@ -7272,5 +7371,10 @@
             SM工具类
             </summary>
         </member>
+        <member name="T:OnlineUserJob">
+            <summary>
+            清理在线用户作业任务
+            </summary>
+        </member>
     </members>
 </doc>

+ 12 - 0
Admin.NET/Admin.NET.Core/Entity/EntityBase.cs

@@ -82,4 +82,16 @@ public abstract class EntityTenantId : EntityBaseId
     /// </summary>
     [SugarColumn(ColumnDescription = "租户Id")]
     public virtual long? TenantId { get; set; }
+}
+
+/// <summary>
+/// 框架实体基类自增Id
+/// </summary>
+public abstract class BaseId
+{
+    /// <summary>
+    /// Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true, IsIdentity = true)]
+    public virtual int Id { get; set; }
 }

+ 34 - 0
Admin.NET/Admin.NET.Core/Entity/SysJobCluster.cs

@@ -0,0 +1,34 @@
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 系统作业集群表
+/// </summary>
+[SugarTable("sys_job_cluster", "系统作业集群表")]
+public class SysJobCluster : BaseId
+{
+    /// <summary>
+    /// 作业集群Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "作业集群Id", Length = 64)]
+    [MaxLength(64)]
+    public virtual string ClusterId { get; set; }
+
+    /// <summary>
+    /// 描述信息
+    /// </summary>
+    [SugarColumn(ColumnDescription = "描述信息", Length = 128)]
+    [MaxLength(128)]
+    public string Description { get; set; }
+
+    /// <summary>
+    /// 状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "状态")]
+    public ClusterStatus Status { get; set; }
+
+    /// <summary>
+    /// 更新时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "更新时间")]
+    public DateTime? UpdatedTime { get; set; }
+}

+ 67 - 0
Admin.NET/Admin.NET.Core/Entity/SysJobDetail.cs

@@ -0,0 +1,67 @@
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 系统作业信息表
+/// </summary>
+[SugarTable("sys_job_detail", "系统作业信息表")]
+public class SysJobDetail : BaseId
+{
+    /// <summary>
+    /// 作业Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "作业Id", Length = 64)]
+    [MaxLength(64)]
+    public virtual string JobId { get; set; }
+
+    /// <summary>
+    /// 组名称
+    /// </summary>
+    [SugarColumn(ColumnDescription = "组名称", Length = 128)]
+    [MaxLength(128)]
+    public string GroupName { get; set; } = "default";
+
+    /// <summary>
+    /// 作业类型FullName
+    /// </summary>
+    [SugarColumn(ColumnDescription = "作业类型", Length = 128)]
+    [MaxLength(128)]
+    public string JobType { get; set; }
+
+    /// <summary>
+    /// 程序集Name
+    /// </summary>
+    [SugarColumn(ColumnDescription = "程序集", Length = 128)]
+    [MaxLength(128)]
+    public string AssemblyName { get; set; }
+
+    /// <summary>
+    /// 描述信息
+    /// </summary>
+    [SugarColumn(ColumnDescription = "描述信息", Length = 128)]
+    [MaxLength(128)]
+    public string Description { get; set; }
+
+    /// <summary>
+    /// 是否并行执行
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否并行执行")]
+    public bool Concurrent { get; set; } = true;
+
+    /// <summary>
+    /// 是否扫描特性触发器
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否扫描特性触发器")]
+    public bool IncludeAnnotations { get; set; } = false;
+
+    /// <summary>
+    /// 额外数据
+    /// </summary>
+    [SugarColumn(ColumnDescription = "额外数据", ColumnDataType = "longtext,text,clob")]
+    public string Properties { get; set; } = "{}";
+
+    /// <summary>
+    /// 更新时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "更新时间")]
+    public DateTime? UpdatedTime { get; set; }
+}

+ 134 - 0
Admin.NET/Admin.NET.Core/Entity/SysJobTrigger.cs

@@ -0,0 +1,134 @@
+namespace Admin.NET.Core;
+
+/// <summary>
+/// 系统作业触发器表
+/// </summary>
+[SugarTable("sys_job_trigger", "系统作业触发器表")]
+public class SysJobTrigger : BaseId
+{
+    /// <summary>
+    /// 触发器Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "触发器Id", Length = 64)]
+    [MaxLength(64)]
+    public virtual string TriggerId { get; set; }
+
+    /// <summary>
+    /// 作业Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "作业Id", Length = 64)]
+    [MaxLength(64)]
+    public virtual string JobId { get; set; }
+
+    /// <summary>
+    /// 触发器类型FullName
+    /// </summary>
+    [SugarColumn(ColumnDescription = "触发器类型", Length = 128)]
+    [MaxLength(128)]
+    public string TriggerType { get; set; }
+
+    /// <summary>
+    /// 程序集Name
+    /// </summary>
+    [SugarColumn(ColumnDescription = "程序集", Length = 128)]
+    [MaxLength(128)]
+    public string AssemblyName { get; set; }
+
+    /// <summary>
+    /// 参数
+    /// </summary>
+    [SugarColumn(ColumnDescription = "参数", Length = 128)]
+    [MaxLength(128)]
+    public string Args { get; set; }
+
+    /// <summary>
+    /// 描述信息
+    /// </summary>
+    [SugarColumn(ColumnDescription = "描述信息", Length = 128)]
+    [MaxLength(128)]
+    public string Description { get; set; }
+
+    /// <summary>
+    /// 状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "状态")]
+    public TriggerStatus Status { get; set; } = TriggerStatus.Ready;
+
+    /// <summary>
+    /// 起始时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "起始时间")]
+    public DateTime? StartTime { get; set; }
+
+    /// <summary>
+    /// 结束时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "结束时间")]
+    public DateTime? EndTime { get; set; }
+
+    /// <summary>
+    /// 最近运行时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "最近运行时间")]
+    public DateTime? LastRunTime { get; set; }
+
+    /// <summary>
+    /// 下一次运行时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "下一次运行时间")]
+    public DateTime? NextRunTime { get; set; }
+
+    /// <summary>
+    /// 触发次数
+    /// </summary>
+    [SugarColumn(ColumnDescription = "触发次数")]
+    public long NumberOfRuns { get; set; }
+
+    /// <summary>
+    /// 最大触发次数(0:不限制,n:N次)
+    /// </summary>
+    [SugarColumn(ColumnDescription = "最大触发次数")]
+    public long MaxNumberOfRuns { get; set; }
+
+    /// <summary>
+    /// 出错次数
+    /// </summary>
+    [SugarColumn(ColumnDescription = "出错次数")]
+    public long NumberOfErrors { get; set; }
+
+    /// <summary>
+    /// 最大出错次数(0:不限制,n:N次)
+    /// </summary>
+    [SugarColumn(ColumnDescription = "最大出错次数")]
+    public long MaxNumberOfErrors { get; set; }
+
+    /// <summary>
+    /// 重试次数
+    /// </summary>
+    [SugarColumn(ColumnDescription = "重试次数")]
+    public int NumRetries { get; set; }
+
+    /// <summary>
+    /// 重试间隔时间(ms)
+    /// </summary>
+    [SugarColumn(ColumnDescription = "重试间隔时间(ms)")]
+    public int RetryTimeout { get; set; } = 1000;
+
+    /// <summary>
+    /// 是否立即启动
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否立即启动")]
+    public bool StartNow { get; set; } = true;
+
+    /// <summary>
+    /// 是否启动时执行一次
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否启动时执行一次")]
+    public bool RunOnStart { get; set; } = false;
+
+    /// <summary>
+    /// 更新时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "更新时间")]
+    public DateTime? UpdatedTime { get; set; }
+}

+ 78 - 78
Admin.NET/Admin.NET.Core/Entity/SysTimer.cs

@@ -1,91 +1,91 @@
-namespace Admin.NET.Core;
+//namespace Admin.NET.Core;
 
-/// <summary>
-/// 定时任务
-/// </summary>
-[SugarTable("sys_timer", "定时任务表")]
-public class SysTimer : EntityBase
-{
-    /// <summary>
-    /// 任务名称
-    /// </summary>
-    [SugarColumn(ColumnDescription = "任务名称", Length = 64)]
-    [Required, MaxLength(64)]
-    public virtual string TimerName { get; set; }
+///// <summary>
+///// 定时任务
+///// </summary>
+//[SugarTable("sys_timer", "定时任务表")]
+//public class SysTimer : EntityBase
+//{
+//    /// <summary>
+//    /// 任务名称
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "任务名称", Length = 64)]
+//    [Required, MaxLength(64)]
+//    public virtual string TimerName { get; set; }
 
-    /// <summary>
-    /// 是否只执行一次
-    /// </summary>
-    [SugarColumn(ColumnDescription = "是否只执行一次")]
-    public bool DoOnce { get; set; } = false;
+//    /// <summary>
+//    /// 是否只执行一次
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "是否只执行一次")]
+//    public bool DoOnce { get; set; } = false;
 
-    /// <summary>
-    /// 是否立即执行
-    /// </summary>
-    [SugarColumn(ColumnDescription = "是否立即执行")]
-    public bool StartNow { get; set; } = false;
+//    /// <summary>
+//    /// 是否立即执行
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "是否立即执行")]
+//    public bool StartNow { get; set; } = false;
 
-    /// <summary>
-    /// 执行类型(串行并行)
-    /// </summary>
-    [SugarColumn(ColumnDescription = "执行类型")]
-    public SpareTimeExecuteTypes ExecuteType { get; set; } = SpareTimeExecuteTypes.Parallel;
+//    /// <summary>
+//    /// 执行类型(串行并行)
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "执行类型")]
+//    public SpareTimeExecuteTypes ExecuteType { get; set; } = SpareTimeExecuteTypes.Parallel;
 
-    /// <summary>
-    /// 执行间隔(单位秒)
-    /// </summary>
-    /// <example>5</example>
-    [SugarColumn(ColumnDescription = "执行间隔")]
-    public int? Interval { get; set; } = 5;
+//    /// <summary>
+//    /// 执行间隔(单位秒)
+//    /// </summary>
+//    /// <example>5</example>
+//    [SugarColumn(ColumnDescription = "执行间隔")]
+//    public int? Interval { get; set; } = 5;
 
-    /// <summary>
-    /// Cron表达式
-    /// </summary>
-    [SugarColumn(ColumnDescription = "Cron表达式", Length = 128)]
-    [MaxLength(128)]
-    public string Cron { get; set; }
+//    /// <summary>
+//    /// Cron表达式
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "Cron表达式", Length = 128)]
+//    [MaxLength(128)]
+//    public string Cron { get; set; }
 
-    /// <summary>
-    /// 定时器类型
-    /// </summary>
-    [SugarColumn(ColumnDescription = "定时器类型")]
-    public SpareTimeTypes TimerType { get; set; } = SpareTimeTypes.Interval;
+//    /// <summary>
+//    /// 定时器类型
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "定时器类型")]
+//    public SpareTimeTypes TimerType { get; set; } = SpareTimeTypes.Interval;
 
-    /// <summary>
-    /// 请求url
-    /// </summary>
-    [SugarColumn(ColumnDescription = "请求url", Length = 256)]
-    [MaxLength(256)]
-    public string RequestUrl { get; set; }
+//    /// <summary>
+//    /// 请求url
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "请求url", Length = 256)]
+//    [MaxLength(256)]
+//    public string RequestUrl { get; set; }
 
-    /// <summary>
-    /// 请求类型
-    /// </summary>
-    [SugarColumn(ColumnDescription = "请求类型")]
-    public RequestTypeEnum RequestType { get; set; } = RequestTypeEnum.Post;
+//    /// <summary>
+//    /// 请求类型
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "请求类型")]
+//    public RequestTypeEnum RequestType { get; set; } = RequestTypeEnum.Post;
 
-    /// <summary>
-    /// 请求参数
-    /// </summary>
-    [SugarColumn(ColumnDescription = "请求参数")]
-    public string RequestPara { get; set; }
+//    /// <summary>
+//    /// 请求参数
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "请求参数")]
+//    public string RequestPara { get; set; }
 
-    /// <summary>
-    /// Headers参数 比如{"Authorization":"userpassword"}
-    /// </summary>
-    [SugarColumn(ColumnDescription = "Headers参数")]
-    public string Headers { get; set; }
+//    /// <summary>
+//    /// Headers参数 比如{"Authorization":"userpassword"}
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "Headers参数")]
+//    public string Headers { get; set; }
 
-    /// <summary>
-    /// 排序
-    /// </summary>
-    [SugarColumn(ColumnDescription = "排序")]
-    public int Order { get; set; } = 100;
+//    /// <summary>
+//    /// 排序
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "排序")]
+//    public int Order { get; set; } = 100;
 
-    /// <summary>
-    /// 备注
-    /// </summary>
-    [SugarColumn(ColumnDescription = "备注", Length = 128)]
-    [MaxLength(128)]
-    public string Remark { get; set; }
-}
+//    /// <summary>
+//    /// 备注
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "备注", Length = 128)]
+//    [MaxLength(128)]
+//    public string Remark { get; set; }
+//}

+ 1 - 1
Admin.NET/Admin.NET.Core/Entity/SysUser.cs

@@ -205,7 +205,7 @@ public class SysUser : EntityTenant
     /// 职位
     /// </summary>
     [SugarColumn(IsIgnore = true)]
-    [Navigate(NavigateType.OneToMany, nameof(PosId))]
+    [Navigate(NavigateType.OneToOne, nameof(PosId))]
     public SysPos SysPos { get; set; }
 
     /// <summary>

+ 1 - 2
Admin.NET/Admin.NET.Core/GlobalUsings.cs

@@ -12,12 +12,11 @@ global using Furion.FriendlyException;
 global using Furion.JsonSerialization;
 global using Furion.Logging;
 global using Furion.RemoteRequest.Extensions;
+global using Furion.Schedule;
 global using Furion.SensitiveDetection;
-global using Furion.TaskScheduler;
 global using Furion.UnifyResult;
 global using Furion.ViewEngine;
 global using Mapster;
-global using MapsterMapper;
 global using Microsoft.AspNetCore.Authorization;
 global using Microsoft.AspNetCore.Http;
 global using Microsoft.AspNetCore.Mvc;

+ 18 - 19
Admin.NET/Admin.NET.Core/Job/LogJob.cs

@@ -1,28 +1,27 @@
 namespace Admin.NET.Core;
 
 /// <summary>
-/// 任务调度
+/// 清理日志作业任务
 /// </summary>
-public class LogJob : ISpareTimeWorker
+[Daily(TriggerId = "tId_log", Description = "清理操作日志")]
+public class LogJob : IJob
 {
-    /// <summary>
-    /// 日志删除定时器
-    /// </summary>
-    /// <param name="timer"></param>
-    /// <param name="count"></param>
-    [SpareTime("@midnight", "清理日志", Description = "每天午夜运行一次", DoOnce = false, StartNow = true, ExecuteType = SpareTimeExecuteTypes.Serial)]
-    public void ClearLogJob(SpareTimer timer, long count)
+    private readonly IServiceProvider _serviceProvider;
+
+    public LogJob(IServiceProvider serviceProvider)
+    {
+        _serviceProvider = serviceProvider;
+    }
+
+    public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
     {
-        Scoped.CreateAsync(async (_, scope) =>
-        {
-            var services = scope.ServiceProvider;
-            var db = services.GetService<ISqlSugarClient>();
+        using var serviceScope = _serviceProvider.CreateScope();
+        var db = serviceScope.ServiceProvider.GetService<ISqlSugarClient>();
 
-            var daysAgo = 30; // 删除30天以前
-            await db.Deleteable<SysLogVis>().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(); // 删除访问日志
-            await db.Deleteable<SysLogOp>().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(); // 删除操作日志
-            await db.Deleteable<SysLogEx>().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(); // 删除异常日志
-            await db.Deleteable<SysLogDiff>().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(); // 删除差异日志
-        });
+        var daysAgo = 30; // 删除30天以前
+        await db.Deleteable<SysLogVis>().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(); // 删除访问日志
+        await db.Deleteable<SysLogOp>().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(); // 删除操作日志
+        await db.Deleteable<SysLogEx>().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(); // 删除异常日志
+        await db.Deleteable<SysLogDiff>().Where(u => (DateTime)u.CreateTime < DateTime.Now.AddDays(-daysAgo)).ExecuteCommandAsync(); // 删除差异日志
     }
 }

+ 20 - 18
Admin.NET/Admin.NET.Core/Job/OnlineUserJob.cs

@@ -1,27 +1,29 @@
-namespace Admin.NET.Core.Job;
+using Admin.NET.Core;
 
 /// <summary>
-/// 在线用户任务调度
+/// 清理在线用户作业任务
 /// </summary>
-public class OnlineUserJob : ISpareTimeWorker
+[PeriodSeconds(1, TriggerId = "tId_onlineUser", Description = "清理在线用户", MaxNumberOfRuns = 1, StartNow = true, RunOnStart = true)]
+public class OnlineUserJob : IJob
 {
-    /// <summary>
-    /// 服务重启清空在线用户(防止僵尸用户,掉线用户会自动重连)
-    /// </summary>
-    [SpareTime(1000, "服务重启清空在线用户", Description = "服务重启清空在线用户", DoOnce = true, StartNow = true, ExecuteType = SpareTimeExecuteTypes.Serial)]
-    public void ClearOnlineUser(SpareTimer timer, long count)
+    private readonly IServiceProvider _serviceProvider;
+
+    public OnlineUserJob(IServiceProvider serviceProvider)
     {
-        Scoped.CreateAsync(async (_, scope) =>
-        {
-            var services = scope.ServiceProvider;
-            var rep = services.GetService<SqlSugarRepository<SysOnlineUser>>();
-            await rep.AsDeleteable().ExecuteCommandAsync();
+        _serviceProvider = serviceProvider;
+    }
+
+    public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
+    {
+        using var serviceScope = _serviceProvider.CreateScope();
+
+        var rep = serviceScope.ServiceProvider.GetService<SqlSugarRepository<SysOnlineUser>>();
+        await rep.AsDeleteable().ExecuteCommandAsync();
 
-            Console.ForegroundColor = ConsoleColor.Red;
-            Console.WriteLine("【" + DateTime.Now + "】服务重启清空在线用户");
+        Console.ForegroundColor = ConsoleColor.Red;
+        Console.WriteLine("【" + DateTime.Now + "】服务重启清空在线用户");
 
-            // 缓存多租户
-            await services.GetService<SysTenantService>().UpdateTenantCache();
-        });
+        // 缓存多租户
+        await serviceScope.ServiceProvider.GetService<SysTenantService>().UpdateTenantCache();
     }
 }

+ 52 - 0
Admin.NET/Admin.NET.Core/Service/Job/DbJobPersistence.cs

@@ -0,0 +1,52 @@
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 作业持久化(数据库)
+/// </summary>
+public class DbJobPersistence : IJobPersistence
+{
+    private readonly IServiceProvider _serviceProvider;
+
+    public DbJobPersistence(IServiceProvider serviceProvider)
+    {
+        _serviceProvider = serviceProvider;
+    }
+
+    /// <summary>
+    /// 作业计划Scheduler的JobDetail变化时
+    /// </summary>
+    /// <param name="context"></param>
+    public void OnChanged(PersistenceContext context)
+    {
+        using var serviceScope = _serviceProvider.CreateScope();
+        var db = serviceScope.ServiceProvider.GetService<ISqlSugarClient>();
+        var sql = context.ConvertToSQL(db.EntityMaintenance.GetEntityInfo<SysJobDetail>().DbTableName, NamingConventions.Pascal);
+        db.Ado.ExecuteCommand(sql);
+    }
+
+    /// <summary>
+    /// 作业计划Scheduler的触发器Trigger变化时
+    /// </summary>
+    /// <param name="context"></param>
+    public void OnTriggerChanged(PersistenceTriggerContext context)
+    {
+        using var serviceScope = _serviceProvider.CreateScope();
+        var db = serviceScope.ServiceProvider.GetService<ISqlSugarClient>();
+        var sql = context.ConvertToSQL(db.EntityMaintenance.GetEntityInfo<SysJobTrigger>().DbTableName, NamingConventions.Pascal);
+        db.Ado.ExecuteCommand(sql);
+    }
+
+    /// <summary>
+    /// 作业调度器服务启动时(通常用来同步持久化数据到内存中)
+    /// </summary>
+    /// <param name="jobId"></param>
+    /// <param name="builder"></param>
+    /// <returns></returns>
+    public SchedulerBuilder Preload(string jobId, SchedulerBuilder builder)
+    {
+        // 如果是更新操作,则 return builder.Updated();
+        // 如果是新增操作,则 return builder.Appended();
+        // 如果是删除操作,则 return builder.Removed();
+        return builder.Updated();
+    }
+}

+ 10 - 0
Admin.NET/Admin.NET.Core/Service/Job/Dto/JobInput.cs

@@ -0,0 +1,10 @@
+namespace Admin.NET.Core.Service;
+
+public class JobInput
+{
+    /// <summary>
+    /// 作业任务名称
+    /// </summary>
+    [Required(ErrorMessage = "作业任务名称不能为空")]
+    public string Name { get; set; }
+}

+ 74 - 0
Admin.NET/Admin.NET.Core/Service/Job/JobClusterServer.cs

@@ -0,0 +1,74 @@
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 作业集群控制
+/// </summary>
+public class JobClusterServer : IJobClusterServer
+{
+    /// <summary>
+    /// 当前作业调度器启动通知
+    /// </summary>
+    /// <param name="context">作业集群服务上下文</param>
+    public void Start(JobClusterContext context)
+    {
+        // 在作业集群表中,如果 clusterId 不存在,则新增一条(否则更新一条),并设置 status 为 ClusterStatus.Waiting
+    }
+
+    /// <summary>
+    /// 等待被唤醒
+    /// </summary>
+    /// <param name="context">作业集群服务上下文</param>
+    /// <returns><see cref="Task"/></returns>
+    public async Task WaitingForAsync(JobClusterContext context)
+    {
+        var clusterId = context.ClusterId;
+
+        while (true)
+        {
+            try
+            {
+                // 在这里查询数据库,根据以下两种情况处理
+                // 1) 如果作业集群表已有 status 为 ClusterStatus.Working 则继续循环
+                // 2) 如果作业集群表中还没有其他服务或只有自己,则插入一条集群服务或调用 await WorkNowAsync(clusterId); 之后 return;
+                // 3) 如果作业集群表中没有 status 为 ClusterStatus.Working 的,调用 await WorkNowAsync(clusterId); 之后 return;
+
+                await WorkNowAsync(clusterId);
+                return;
+            }
+            catch { }
+
+            // 控制集群心跳频率
+            await Task.Delay(3000);
+        }
+    }
+
+    /// <summary>
+    /// 当前作业调度器停止通知
+    /// </summary>
+    /// <param name="context">作业集群服务上下文</param>
+    public void Stop(JobClusterContext context)
+    {
+        // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed
+    }
+
+    /// <summary>
+    /// 当前作业调度器宕机
+    /// </summary>
+    /// <param name="context">作业集群服务上下文</param>
+    public void Crash(JobClusterContext context)
+    {
+        // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Crashed
+    }
+
+    /// <summary>
+    /// 指示集群可以工作
+    /// </summary>
+    /// <param name="clusterId">集群 Id</param>
+    /// <returns></returns>
+    private Task WorkNowAsync(string clusterId)
+    {
+        // 在作业集群表中,更新 clusterId 的 status 为 ClusterStatus.Working
+
+        return Task.CompletedTask;
+    }
+}

+ 16 - 0
Admin.NET/Admin.NET.Core/Service/Job/JobExecutor.cs

@@ -0,0 +1,16 @@
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 作业执行器
+/// </summary>
+public class JobExecutor : IJobExecutor
+{
+    public async Task ExecuteAsync(JobExecutingContext context, IJob jobHandler, CancellationToken stoppingToken)
+    {
+        // 实现失败重试策略,如失败重试3次
+        await Retry.InvokeAsync(async () =>
+        {
+            await jobHandler.ExecuteAsync(context, stoppingToken);
+        }, 3, 1000);
+    }
+}

+ 60 - 0
Admin.NET/Admin.NET.Core/Service/Job/SysJobService.cs

@@ -0,0 +1,60 @@
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 系统作业任务服务
+/// </summary>
+[ApiDescriptionSettings(Order = 188)]
+[AllowAnonymous]
+public class SysJobService : IDynamicApiController, ITransient
+{
+    private readonly ISchedulerFactory _schedulerFactory;
+
+    public SysJobService(ISchedulerFactory schedulerFactory)
+    {
+        _schedulerFactory = schedulerFactory;
+    }
+
+    /// <summary>
+    /// 获取所有作业任务列表
+    /// </summary>
+    [HttpGet("/sysJob/list")]
+    public async Task<List<SchedulerModel>> GetJobList()
+    {
+        var JobDetails = _schedulerFactory.GetJobsOfModels();
+        return await Task.FromResult(JobDetails.ToList());
+    }
+
+    /// <summary>
+    /// 增加作业任务
+    /// </summary>
+    /// <returns></returns>
+    [HttpPost("/sysJob/add")]
+    public async Task<ScheduleResult> AddJob()
+    {
+        var jobBuilder = JobBuilder.From(@"
+        {
+            ""jobId"": ""job1"",
+            ""groupName"": null,
+            ""jobType"": ""Admin.NET.Application"",
+            ""assemblyName"": ""Admin.NET.Application"",
+            ""description"": null,
+            ""concurrent"": true,
+            ""includeAnnotations"": false,
+            ""properties"": ""{}"",
+            ""updatedTime"": ""2022-11-22 18:00:00""
+        }");
+        var schedulerResult = _schedulerFactory.TryAddJob(jobBuilder, new[] { Triggers.PeriodSeconds(10) }, out _);
+        return await Task.FromResult(schedulerResult);
+    }
+
+    /// <summary>
+    /// 删除作业任务
+    /// </summary>
+    /// <returns></returns>
+    [HttpPost("/sysJob/delete")]
+    public async Task<ScheduleResult> DeleteJob(JobInput input)
+    {
+        var schedulerResult = _schedulerFactory.TryRemoveJob(input.Name, out _);
+        return await Task.FromResult(schedulerResult);
+    }
+}

+ 40 - 40
Admin.NET/Admin.NET.Core/Service/Timer/Dto/TimerInput.cs

@@ -1,47 +1,47 @@
-namespace Admin.NET.Core.Service;
+//namespace Admin.NET.Core.Service;
 
-public class PageTimerInput : BasePageInput
-{
-    /// <summary>
-    /// 任务名称
-    /// </summary>
-    public string TimerName { get; set; }
-}
+//public class PageTimerInput : BasePageInput
+//{
+//    /// <summary>
+//    /// 任务名称
+//    /// </summary>
+//    public string TimerName { get; set; }
+//}
 
-[NotTable]
-public class AddTimerInput : SysTimer
-{
-    /// <summary>
-    /// 任务名称
-    /// </summary>
-    [Required(ErrorMessage = "任务名称不能为空")]
-    public override string TimerName { get; set; }
-}
+//[NotTable]
+//public class AddTimerInput : SysTimer
+//{
+//    /// <summary>
+//    /// 任务名称
+//    /// </summary>
+//    [Required(ErrorMessage = "任务名称不能为空")]
+//    public override string TimerName { get; set; }
+//}
 
-[NotTable]
-public class UpdateTimerInput : AddTimerInput
-{
-}
+//[NotTable]
+//public class UpdateTimerInput : AddTimerInput
+//{
+//}
 
-public class DeleteTimerInput : BaseIdInput
-{
-}
+//public class DeleteTimerInput : BaseIdInput
+//{
+//}
 
-[NotTable]
-public class StopTimerInput : AddTimerInput
-{
-}
+//[NotTable]
+//public class StopTimerInput : AddTimerInput
+//{
+//}
 
-public class SetTimerStatusInput
-{
-    /// <summary>
-    /// 任务名称
-    /// </summary>
-    [Required(ErrorMessage = "任务名称不能为空")]
-    public string TimerName { get; set; }
+//public class SetTimerStatusInput
+//{
+//    /// <summary>
+//    /// 任务名称
+//    /// </summary>
+//    [Required(ErrorMessage = "任务名称不能为空")]
+//    public string TimerName { get; set; }
 
-    /// <summary>
-    /// 任务状态
-    /// </summary>
-    public SpareTimeStatus Status { get; set; } = SpareTimeStatus.Stopped;
-}
+//    /// <summary>
+//    /// 任务状态
+//    /// </summary>
+//    public SpareTimeStatus Status { get; set; } = SpareTimeStatus.Stopped;
+//}

+ 13 - 13
Admin.NET/Admin.NET.Core/Service/Timer/Dto/TimerMethod.cs

@@ -1,15 +1,15 @@
-namespace Admin.NET.Core.Service;
+//namespace Admin.NET.Core.Service;
 
-[NotTable]
-public class TimerMethod : SysTimer
-{
-    /// <summary>
-    /// 方法名
-    /// </summary>
-    public string MethodName { get; set; }
+//[NotTable]
+//public class TimerMethod : SysTimer
+//{
+//    /// <summary>
+//    /// 方法名
+//    /// </summary>
+//    public string MethodName { get; set; }
 
-    /// <summary>
-    /// 所属类
-    /// </summary>
-    public Type DeclaringType { get; set; }
-}
+//    /// <summary>
+//    /// 所属类
+//    /// </summary>
+//    public Type DeclaringType { get; set; }
+//}

+ 17 - 17
Admin.NET/Admin.NET.Core/Service/Timer/Dto/TimerOutput.cs

@@ -1,20 +1,20 @@
-namespace Admin.NET.Core.Service;
+//namespace Admin.NET.Core.Service;
 
-[NotTable]
-public class TimerOutput : SysTimer
-{
-    /// <summary>
-    /// 执行次数
-    /// </summary>
-    public long Tally { get; set; }
+//[NotTable]
+//public class TimerOutput : SysTimer
+//{
+//    /// <summary>
+//    /// 执行次数
+//    /// </summary>
+//    public long Tally { get; set; }
 
-    /// <summary>
-    /// 任务状态
-    /// </summary>
-    public SpareTimeStatus Status { get; set; } = SpareTimeStatus.Stopped;
+//    /// <summary>
+//    /// 任务状态
+//    /// </summary>
+//    public SpareTimeStatus Status { get; set; } = SpareTimeStatus.Stopped;
 
-    /// <summary>
-    /// 异常信息
-    /// </summary>
-    public string Exception { get; set; }
-}
+//    /// <summary>
+//    /// 异常信息
+//    /// </summary>
+//    public string Exception { get; set; }
+//}

+ 247 - 247
Admin.NET/Admin.NET.Core/Service/Timer/SysTimerService.cs

@@ -1,247 +1,247 @@
-namespace Admin.NET.Core.Service;
-
-/// <summary>
-/// 系统定时任务服务
-/// </summary>
-[ApiDescriptionSettings(Order = 188)]
-public class SysTimerService : IDynamicApiController, ITransient
-{
-    private readonly SqlSugarRepository<SysTimer> _sysTimerRep;
-    private readonly SysCacheService _sysCacheService;
-
-    public SysTimerService(SqlSugarRepository<SysTimer> sysTimerRep,
-        SysCacheService sysCacheService)
-    {
-        _sysTimerRep = sysTimerRep;
-        _sysCacheService = sysCacheService;
-    }
-
-    /// <summary>
-    /// 获取任务分页列表
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [HttpGet("/sysTimer/page")]
-    public async Task<SqlSugarPagedList<TimerOutput>> GetTimerPage([FromQuery] PageTimerInput input)
-    {
-        var workers = SpareTime.GetWorkers().ToList();
-
-        var timers = await _sysTimerRep.AsQueryable()
-            .WhereIF(!string.IsNullOrWhiteSpace(input.TimerName), u => u.TimerName.Contains(input.TimerName))
-            .Select<TimerOutput>()
-            .ToPagedListAsync(input.Page, input.PageSize);
-
-        timers.Items.ToList().ForEach(u =>
-        {
-            var timer = workers.FirstOrDefault(m => m.WorkerName == u.TimerName);
-            if (timer != null)
-            {
-                u.Status = timer.Status;
-                u.Tally = timer.Tally;
-                u.Exception = JSON.Serialize(timer.Exception);
-            }
-        });
-        return timers;
-    }
-
-    /// <summary>
-    /// 增加任务
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [HttpPost("/sysTimer/add")]
-    public async Task AddTimer(AddTimerInput input)
-    {
-        var isExist = await _sysTimerRep.IsAnyAsync(u => u.TimerName == input.TimerName);
-        if (isExist) throw Oops.Oh(ErrorCodeEnum.D1100);
-
-        var timer = input.Adapt<SysTimer>();
-        await _sysTimerRep.InsertAsync(timer);
-
-        CreateTimer(timer); // 添加到任务调度里
-    }
-
-    /// <summary>
-    /// 删除任务
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [HttpPost("/sysTimer/delete")]
-    public async Task DeleteTimer(DeleteTimerInput input)
-    {
-        var timer = await _sysTimerRep.GetFirstAsync(u => u.Id == input.Id);
-        if (timer == null) throw Oops.Oh(ErrorCodeEnum.D1101);
-
-        await _sysTimerRep.DeleteAsync(timer);
-
-        SpareTime.Cancel(timer.TimerName); // 从调度器里取消
-    }
-
-    /// <summary>
-    /// 更新任务
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [HttpPost("/sysTimer/update")]
-    public async Task UpdateTimber(UpdateTimerInput input)
-    {
-        var isExist = await _sysTimerRep.IsAnyAsync(u => u.TimerName == input.TimerName && u.Id != input.Id);
-        if (isExist) throw Oops.Oh(ErrorCodeEnum.D1100);
-
-        // 先从调度器里取消
-        var oldTimer = await _sysTimerRep.GetFirstAsync(u => u.Id == input.Id);
-        SpareTime.Cancel(oldTimer.TimerName);
-
-        var timer = input.Adapt<SysTimer>();
-        await _sysTimerRep.AsUpdateable(timer).IgnoreColumns(true).ExecuteCommandAsync();
-
-        CreateTimer(timer); // 再添加到任务调度里
-    }
-
-    /// <summary>
-    /// 设置任务状态
-    /// </summary>
-    /// <param name="input"></param>
-    /// <returns></returns>
-    [HttpPost("/sysTimer/setStatus")]
-    public async void SetStatusTimer(SetTimerStatusInput input)
-    {
-        if (input.Status == SpareTimeStatus.Stopped)
-            SpareTime.Stop(input.TimerName);
-        else if (input.Status == SpareTimeStatus.Running)
-        {
-            var spareTime = SpareTime.GetWorkers().ToList().Find(u => u.WorkerName == input.TimerName);
-            if (spareTime == null)
-            {
-                var timer = await _sysTimerRep.GetFirstAsync(u => u.TimerName == input.TimerName);
-                CreateTimer(timer);
-            }
-            SpareTime.Start(input.TimerName); // 若StartNow=flase则不会启动任务
-        }
-    }
-
-    /// <summary>
-    /// 创建定时任务
-    /// </summary>
-    /// <param name="input"></param>
-    private void CreateTimer(SysTimer input)
-    {
-        Action<SpareTimer, long> action = null;
-        switch (input.RequestType)
-        {
-            case RequestTypeEnum.Run: // 创建本地方法委托
-                {
-                    var taskMethod = GetTimerMethodList()?.FirstOrDefault(m => m.RequestUrl == input.RequestUrl);
-                    if (taskMethod == null) break;
-                    var typeInstance = Activator.CreateInstance(taskMethod.DeclaringType);
-                    action = (Action<SpareTimer, long>)Delegate.CreateDelegate(typeof(Action<SpareTimer, long>), typeInstance, taskMethod.MethodName);
-                    break;
-                }
-            default: // 创建网络任务委托
-                {
-                    action = async (_, _) =>
-                    {
-                        var requestUrl = input.RequestUrl.Trim();
-                        requestUrl = requestUrl?.IndexOf("http") == 0 ? requestUrl : "http://" + requestUrl; // 默认http协议
-                        var requestParametersString = input.RequestPara;
-                        var requestParameters = string.IsNullOrEmpty(requestParametersString)
-                            ? null : JSON.Deserialize<Dictionary<string, string>>(requestParametersString);
-                        var headersString = input.Headers;
-                        var headers = string.IsNullOrEmpty(headersString)
-                            ? null : JSON.Deserialize<Dictionary<string, string>>(headersString);
-                        switch (input.RequestType)
-                        {
-                            case RequestTypeEnum.Get:
-                                await requestUrl.SetHeaders(headers).GetAsync();
-                                break;
-
-                            case RequestTypeEnum.Post:
-                                await requestUrl.SetHeaders(headers).SetQueries(requestParameters).PostAsync();
-                                break;
-
-                            case RequestTypeEnum.Put:
-                                await requestUrl.SetHeaders(headers).SetQueries(requestParameters).PutAsync();
-                                break;
-
-                            case RequestTypeEnum.Delete:
-                                await requestUrl.SetHeaders(headers).DeleteAsync();
-                                break;
-                        }
-                    };
-                    break;
-                }
-        }
-        if (action == null) return;
-
-        // 缓存任务配置参数供任务运行时读取
-        if (input.RequestType == RequestTypeEnum.Run)
-        {
-            var timerParaName = $"{input.TimerName}_para";
-            var timerPara = _sysCacheService.ExistKey(timerParaName);
-            var requestPara = string.IsNullOrEmpty(input.RequestPara);
-
-            // 若没有任务配置但存在缓存则删除
-            if (requestPara && timerPara)
-                _sysCacheService.Remove(timerParaName);
-            else if (!requestPara)
-                _sysCacheService.Set(timerParaName, JSON.Deserialize<Dictionary<string, string>>(input.RequestPara));
-        }
-
-        // 创建定时任务
-        switch (input.TimerType)
-        {
-            case SpareTimeTypes.Interval:
-                if (input.DoOnce)
-                    SpareTime.DoOnce((int)input.Interval * 1000, action, input.TimerName, input.Remark, input.StartNow, executeType: input.ExecuteType);
-                else
-                    SpareTime.Do((int)input.Interval * 1000, action, input.TimerName, input.Remark, input.StartNow, executeType: input.ExecuteType);
-                break;
-
-            case SpareTimeTypes.Cron:
-                SpareTime.Do(input.Cron, action, input.TimerName, input.Remark, input.StartNow, executeType: input.ExecuteType);
-                break;
-        }
-    }
-
-    /// <summary>
-    /// 获取所有定时任务方法列表(贴spareTime特性)
-    /// </summary>
-    /// <returns></returns>
-    private IEnumerable<TimerMethod> GetTimerMethodList()
-    {
-        // 有缓存就返回缓存
-        var timerMethodList = _sysCacheService.Get<IEnumerable<TimerMethod>>(CacheConst.KeyTimer);
-        if (timerMethodList != null) return timerMethodList;
-
-        timerMethodList = 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<SpareTimeAttribute>();
-                return new TimerMethod
-                {
-                    TimerName = 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.Run,
-                    Remark = spareTimeAttribute.Description,
-                    TimerType = string.IsNullOrEmpty(spareTimeAttribute.CronExpression) ? SpareTimeTypes.Interval : SpareTimeTypes.Cron,
-                    MethodName = m.Name,
-                    DeclaringType = m.DeclaringType
-                };
-            }));
-
-        _sysCacheService.Set(CacheConst.KeyTimer, timerMethodList);
-        return timerMethodList;
-    }
-}
+//namespace Admin.NET.Core.Service;
+
+///// <summary>
+///// 系统定时任务服务
+///// </summary>
+//[ApiDescriptionSettings(Order = 188)]
+//public class SysTimerService : IDynamicApiController, ITransient
+//{
+//    private readonly SqlSugarRepository<SysTimer> _sysTimerRep;
+//    private readonly SysCacheService _sysCacheService;
+
+//    public SysTimerService(SqlSugarRepository<SysTimer> sysTimerRep,
+//        SysCacheService sysCacheService)
+//    {
+//        _sysTimerRep = sysTimerRep;
+//        _sysCacheService = sysCacheService;
+//    }
+
+//    /// <summary>
+//    /// 获取任务分页列表
+//    /// </summary>
+//    /// <param name="input"></param>
+//    /// <returns></returns>
+//    [HttpGet("/sysTimer/page")]
+//    public async Task<SqlSugarPagedList<TimerOutput>> GetTimerPage([FromQuery] PageTimerInput input)
+//    {
+//        var workers = SpareTime.GetWorkers().ToList();
+
+//        var timers = await _sysTimerRep.AsQueryable()
+//            .WhereIF(!string.IsNullOrWhiteSpace(input.TimerName), u => u.TimerName.Contains(input.TimerName))
+//            .Select<TimerOutput>()
+//            .ToPagedListAsync(input.Page, input.PageSize);
+
+//        timers.Items.ToList().ForEach(u =>
+//        {
+//            var timer = workers.FirstOrDefault(m => m.WorkerName == u.TimerName);
+//            if (timer != null)
+//            {
+//                u.Status = timer.Status;
+//                u.Tally = timer.Tally;
+//                u.Exception = JSON.Serialize(timer.Exception);
+//            }
+//        });
+//        return timers;
+//    }
+
+//    /// <summary>
+//    /// 增加任务
+//    /// </summary>
+//    /// <param name="input"></param>
+//    /// <returns></returns>
+//    [HttpPost("/sysTimer/add")]
+//    public async Task AddTimer(AddTimerInput input)
+//    {
+//        var isExist = await _sysTimerRep.IsAnyAsync(u => u.TimerName == input.TimerName);
+//        if (isExist) throw Oops.Oh(ErrorCodeEnum.D1100);
+
+//        var timer = input.Adapt<SysTimer>();
+//        await _sysTimerRep.InsertAsync(timer);
+
+//        CreateTimer(timer); // 添加到任务调度里
+//    }
+
+//    /// <summary>
+//    /// 删除任务
+//    /// </summary>
+//    /// <param name="input"></param>
+//    /// <returns></returns>
+//    [HttpPost("/sysTimer/delete")]
+//    public async Task DeleteTimer(DeleteTimerInput input)
+//    {
+//        var timer = await _sysTimerRep.GetFirstAsync(u => u.Id == input.Id);
+//        if (timer == null) throw Oops.Oh(ErrorCodeEnum.D1101);
+
+//        await _sysTimerRep.DeleteAsync(timer);
+
+//        SpareTime.Cancel(timer.TimerName); // 从调度器里取消
+//    }
+
+//    /// <summary>
+//    /// 更新任务
+//    /// </summary>
+//    /// <param name="input"></param>
+//    /// <returns></returns>
+//    [HttpPost("/sysTimer/update")]
+//    public async Task UpdateTimber(UpdateTimerInput input)
+//    {
+//        var isExist = await _sysTimerRep.IsAnyAsync(u => u.TimerName == input.TimerName && u.Id != input.Id);
+//        if (isExist) throw Oops.Oh(ErrorCodeEnum.D1100);
+
+//        // 先从调度器里取消
+//        var oldTimer = await _sysTimerRep.GetFirstAsync(u => u.Id == input.Id);
+//        SpareTime.Cancel(oldTimer.TimerName);
+
+//        var timer = input.Adapt<SysTimer>();
+//        await _sysTimerRep.AsUpdateable(timer).IgnoreColumns(true).ExecuteCommandAsync();
+
+//        CreateTimer(timer); // 再添加到任务调度里
+//    }
+
+//    /// <summary>
+//    /// 设置任务状态
+//    /// </summary>
+//    /// <param name="input"></param>
+//    /// <returns></returns>
+//    [HttpPost("/sysTimer/setStatus")]
+//    public async void SetStatusTimer(SetTimerStatusInput input)
+//    {
+//        if (input.Status == SpareTimeStatus.Stopped)
+//            SpareTime.Stop(input.TimerName);
+//        else if (input.Status == SpareTimeStatus.Running)
+//        {
+//            var spareTime = SpareTime.GetWorkers().ToList().Find(u => u.WorkerName == input.TimerName);
+//            if (spareTime == null)
+//            {
+//                var timer = await _sysTimerRep.GetFirstAsync(u => u.TimerName == input.TimerName);
+//                CreateTimer(timer);
+//            }
+//            SpareTime.Start(input.TimerName); // 若StartNow=flase则不会启动任务
+//        }
+//    }
+
+//    /// <summary>
+//    /// 创建定时任务
+//    /// </summary>
+//    /// <param name="input"></param>
+//    private void CreateTimer(SysTimer input)
+//    {
+//        Action<SpareTimer, long> action = null;
+//        switch (input.RequestType)
+//        {
+//            case RequestTypeEnum.Run: // 创建本地方法委托
+//                {
+//                    var taskMethod = GetTimerMethodList()?.FirstOrDefault(m => m.RequestUrl == input.RequestUrl);
+//                    if (taskMethod == null) break;
+//                    var typeInstance = Activator.CreateInstance(taskMethod.DeclaringType);
+//                    action = (Action<SpareTimer, long>)Delegate.CreateDelegate(typeof(Action<SpareTimer, long>), typeInstance, taskMethod.MethodName);
+//                    break;
+//                }
+//            default: // 创建网络任务委托
+//                {
+//                    action = async (_, _) =>
+//                    {
+//                        var requestUrl = input.RequestUrl.Trim();
+//                        requestUrl = requestUrl?.IndexOf("http") == 0 ? requestUrl : "http://" + requestUrl; // 默认http协议
+//                        var requestParametersString = input.RequestPara;
+//                        var requestParameters = string.IsNullOrEmpty(requestParametersString)
+//                            ? null : JSON.Deserialize<Dictionary<string, string>>(requestParametersString);
+//                        var headersString = input.Headers;
+//                        var headers = string.IsNullOrEmpty(headersString)
+//                            ? null : JSON.Deserialize<Dictionary<string, string>>(headersString);
+//                        switch (input.RequestType)
+//                        {
+//                            case RequestTypeEnum.Get:
+//                                await requestUrl.SetHeaders(headers).GetAsync();
+//                                break;
+
+//                            case RequestTypeEnum.Post:
+//                                await requestUrl.SetHeaders(headers).SetQueries(requestParameters).PostAsync();
+//                                break;
+
+//                            case RequestTypeEnum.Put:
+//                                await requestUrl.SetHeaders(headers).SetQueries(requestParameters).PutAsync();
+//                                break;
+
+//                            case RequestTypeEnum.Delete:
+//                                await requestUrl.SetHeaders(headers).DeleteAsync();
+//                                break;
+//                        }
+//                    };
+//                    break;
+//                }
+//        }
+//        if (action == null) return;
+
+//        // 缓存任务配置参数供任务运行时读取
+//        if (input.RequestType == RequestTypeEnum.Run)
+//        {
+//            var timerParaName = $"{input.TimerName}_para";
+//            var timerPara = _sysCacheService.ExistKey(timerParaName);
+//            var requestPara = string.IsNullOrEmpty(input.RequestPara);
+
+//            // 若没有任务配置但存在缓存则删除
+//            if (requestPara && timerPara)
+//                _sysCacheService.Remove(timerParaName);
+//            else if (!requestPara)
+//                _sysCacheService.Set(timerParaName, JSON.Deserialize<Dictionary<string, string>>(input.RequestPara));
+//        }
+
+//        // 创建定时任务
+//        switch (input.TimerType)
+//        {
+//            case SpareTimeTypes.Interval:
+//                if (input.DoOnce)
+//                    SpareTime.DoOnce((int)input.Interval * 1000, action, input.TimerName, input.Remark, input.StartNow, executeType: input.ExecuteType);
+//                else
+//                    SpareTime.Do((int)input.Interval * 1000, action, input.TimerName, input.Remark, input.StartNow, executeType: input.ExecuteType);
+//                break;
+
+//            case SpareTimeTypes.Cron:
+//                SpareTime.Do(input.Cron, action, input.TimerName, input.Remark, input.StartNow, executeType: input.ExecuteType);
+//                break;
+//        }
+//    }
+
+//    /// <summary>
+//    /// 获取所有定时任务方法列表(贴spareTime特性)
+//    /// </summary>
+//    /// <returns></returns>
+//    private IEnumerable<TimerMethod> GetTimerMethodList()
+//    {
+//        // 有缓存就返回缓存
+//        var timerMethodList = _sysCacheService.Get<IEnumerable<TimerMethod>>(CacheConst.KeyTimer);
+//        if (timerMethodList != null) return timerMethodList;
+
+//        timerMethodList = 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<SpareTimeAttribute>();
+//                return new TimerMethod
+//                {
+//                    TimerName = 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.Run,
+//                    Remark = spareTimeAttribute.Description,
+//                    TimerType = string.IsNullOrEmpty(spareTimeAttribute.CronExpression) ? SpareTimeTypes.Interval : SpareTimeTypes.Cron,
+//                    MethodName = m.Name,
+//                    DeclaringType = m.DeclaringType
+//                };
+//            }));
+
+//        _sysCacheService.Set(CacheConst.KeyTimer, timerMethodList);
+//        return timerMethodList;
+//    }
+//}

+ 2 - 2
Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs

@@ -99,8 +99,8 @@ public static class SqlSugarSetup
             // 新增操作
             if (entityInfo.OperationType == DataFilterType.InsertByObject)
             {
-                // 主键(long类型)且没有值的---赋值雪花Id
-                if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.EntityColumnInfo.PropertyInfo.PropertyType == typeof(long))
+                // 主键(long类型)非自增且没有值的---赋值雪花Id
+                if (entityInfo.EntityColumnInfo.IsPrimarykey && !entityInfo.EntityColumnInfo.IsIdentity && entityInfo.EntityColumnInfo.PropertyInfo.PropertyType == typeof(long))
                 {
                     var id = entityInfo.EntityColumnInfo.PropertyInfo.GetValue(entityInfo.EntityValue);
                     if (id == null || (long)id == 0)

+ 14 - 1
Admin.NET/Admin.NET.Web.Core/Startup.cs

@@ -1,6 +1,8 @@
 using Admin.NET.Core;
+using Admin.NET.Core.Service;
 using AspNetCoreRateLimit;
 using Furion;
+using Furion.Schedule;
 using Furion.SpecificationDocument;
 using IGeekFan.AspNetCore.Knife4jUI;
 using Microsoft.AspNetCore.Builder;
@@ -36,7 +38,18 @@ public class Startup : AppStartup
         // 远程请求
         services.AddRemoteRequest();
         // 任务调度
-        services.AddTaskScheduler();
+        services.AddSchedule(options =>
+        {
+            // 添加作业-清理在线用户
+            options.AddJob(JobBuilder.Create<OnlineUserJob>().SetJobId("jId_onlineUser").SetIncludeAnnotations(true));
+            // 添加作业-清理操作日志
+            options.AddJob(JobBuilder.Create<LogJob>().SetJobId("jId_log").SetIncludeAnnotations(true));
+
+            // 添加作业执行器
+            options.AddExecutor<JobExecutor>();
+            // 作业持久化器
+            options.AddPersistence<DbJobPersistence>();
+        });
         // 脱敏检测
         services.AddSensitiveDetection();
         //// 结果拦截器

+ 0 - 6
Admin.NET/Admin.NET.sln

@@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin.NET.Web.Core", "Admin
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin.NET.Web.Entry", "Admin.NET.Web.Entry\Admin.NET.Web.Entry.csproj", "{11EA630B-4600-4236-A117-CE6C6CD67586}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin.NET.UnitTest", "Admin.NET.UnitTest\Admin.NET.UnitTest.csproj", "{0B2B5465-A4A7-4C1D-BD45-CF410E939D68}"
-EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{662E0B8E-F23E-4C7D-80BD-CAA5707503CC}"
 	ProjectSection(SolutionItems) = preProject
 		.editorconfig = .editorconfig
@@ -40,10 +38,6 @@ Global
 		{11EA630B-4600-4236-A117-CE6C6CD67586}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{11EA630B-4600-4236-A117-CE6C6CD67586}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{11EA630B-4600-4236-A117-CE6C6CD67586}.Release|Any CPU.Build.0 = Release|Any CPU
-		{0B2B5465-A4A7-4C1D-BD45-CF410E939D68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{0B2B5465-A4A7-4C1D-BD45-CF410E939D68}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{0B2B5465-A4A7-4C1D-BD45-CF410E939D68}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{0B2B5465-A4A7-4C1D-BD45-CF410E939D68}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE