diff --git a/README.md b/README.md index 409bedf..429c5dd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ -# ZRAdmin.NET +

ZRAdmin.NET后台管理系统

+

基于.NET5 + vue前后端分离的.net快速开发框架

+ + +

+ + +

## 🍟概述 * 本项目适合有一定NetCore和 vue基础的开发人员 @@ -11,7 +18,7 @@ * 七牛云通用云产品优惠券:[点我进入](https://s.qiniu.com/FzEfay)。 * 腾讯云秒杀场:[点我进入](https://curl.qcloud.com/4yEoRquq)。 * 腾讯云优惠券:[点我领取](https://curl.qcloud.com/5J4nag8D)。 -* + ``` 如果对您有帮助,您可以点右上角 “Star” 收藏一下 ,这样作者才有继续免费下去的动力,谢谢!~ ``` @@ -123,6 +130,7 @@ Vue版前端技术栈 :基于vue、vuex、vue-router 、vue-cli 、axios 和 e ## 💐 特别鸣谢 - 👉Ruoyi.vue:[Ruoyi](http://www.ruoyi.vip/) - 👉SqlSugar:[SqlSugar](https://gitee.com/dotnetchina/SqlSugar) +- 👉vue-element-admin:[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) ## 🎀捐赠 如果这个项目对您有所帮助,请扫下方二维码就当打发要饭的吧。 diff --git a/ZR.Admin.WebApi/Controllers/System/TasksController.cs b/ZR.Admin.WebApi/Controllers/System/TasksController.cs index c7895a6..651ca6e 100644 --- a/ZR.Admin.WebApi/Controllers/System/TasksController.cs +++ b/ZR.Admin.WebApi/Controllers/System/TasksController.cs @@ -86,7 +86,7 @@ namespace ZR.Admin.WebApi.Controllers { throw new CustomException($"添加 {parm.Name} 失败,该用任务存在,不能重复!"); } - if (string.IsNullOrEmpty(parm.Cron) || !CronExpression.IsValidExpression(parm.Cron)) + if (!string.IsNullOrEmpty(parm.Cron) && !CronExpression.IsValidExpression(parm.Cron)) { throw new CustomException($"cron表达式不正确"); } @@ -116,11 +116,11 @@ namespace ZR.Admin.WebApi.Controllers { throw new CustomException($"更新 {parm.Name} 失败,该用任务存在,不能重复!"); } - if (string.IsNullOrEmpty(parm.Cron)) + if (string.IsNullOrEmpty(parm.Cron) && parm.TriggerType == 1) { throw new CustomException($"触发器 Corn 模式下,运行时间表达式必须填写"); } - if (!CronExpression.IsValidExpression(parm.Cron)) + if (!string.IsNullOrEmpty(parm.Cron) && !CronExpression.IsValidExpression(parm.Cron)) { throw new CustomException($"cron表达式不正确"); } @@ -150,7 +150,7 @@ namespace ZR.Admin.WebApi.Controllers if (response > 0) { //先暂停原先的任务 - var respon = await _schedulerServer.UpdateTaskScheduleAsync(tasksQz, tasksQz.JobGroup); + var respon = await _schedulerServer.UpdateTaskScheduleAsync(tasksQz); } return SUCCESS(response); @@ -264,11 +264,6 @@ namespace ZR.Admin.WebApi.Controllers var tasksQz = _tasksQzService.GetFirst(m => m.ID == id); var taskResult = await _schedulerServer.RunTaskScheduleAsync(tasksQz); - if (taskResult.Code == 200) - { - //_tasksQzService.Update(tasksQz); - } - return ToResponse(taskResult); } diff --git a/ZR.Admin.WebApi/Extensions/DbExtension.cs b/ZR.Admin.WebApi/Extensions/DbExtension.cs index 27fdec2..d54cd59 100644 --- a/ZR.Admin.WebApi/Extensions/DbExtension.cs +++ b/ZR.Admin.WebApi/Extensions/DbExtension.cs @@ -28,7 +28,7 @@ namespace ZR.Admin.WebApi.Extensions { string connStr = Configuration.GetConnectionString(OptionsSetting.ConnAdmin); string connStrBus = Configuration.GetConnectionString(OptionsSetting.ConnBus); - string dbKey = Configuration[OptionsSetting.DbKey]; + int dbType = Convert.ToInt32(Configuration[OptionsSetting.ConnDbType]); int dbType_bus = Convert.ToInt32(Configuration[OptionsSetting.ConnBusDbType]); @@ -45,37 +45,44 @@ namespace ZR.Admin.WebApi.Extensions IsAutoCloseConnection = true } }); - //每次Sql执行前事件 - DbScoped.SugarScope.GetConnection(0).Aop.OnLogExecuting = (sql, pars) => + SugarIocServices.ConfigurationSugar(db => { - var param = DbScoped.SugarScope.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)); + #region db0 + db.GetConnection(0).Aop.OnLogExecuting = (sql, pars) => + { + var param = DbScoped.SugarScope.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)); - FilterData(); + FilterData(); - logger.Info($"{sql},{param}"); - }; - //出错打印日志 - DbScoped.SugarScope.GetConnection(0).Aop.OnError = (e) => - { - logger.Error(e, $"执行SQL出错:{e.Message}"); - }; - //SQL执行完 - DbScoped.SugarScope.GetConnection(0).Aop.OnLogExecuted = (sql, pars) => - { - //执行完了可以输出SQL执行时间 (OnLogExecutedDelegate) - }; - //Db1 - DbScoped.SugarScope.GetConnection(1).Aop.OnLogExecuting = (sql, pars) => - { - var param = DbScoped.SugarScope.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)); + logger.Info($"{sql},{param}"); + }; - logger.Info($"Sql语句:{sql}, {param}"); - }; - //Db1错误日志 - DbScoped.SugarScope.GetConnection(1).Aop.OnError = (e) => - { - logger.Error($"执行Sql语句失败:{e.Sql},原因:{e.Message}"); - }; + db.GetConnection(0).Aop.OnError = (e) => + { + logger.Error(e, $"执行SQL出错:{e.Message}"); + }; + //SQL执行完 + db.GetConnection(0).Aop.OnLogExecuted = (sql, pars) => + { + //执行完了可以输出SQL执行时间 (OnLogExecutedDelegate) + }; + #endregion + + #region db1 + //Db1 + db.GetConnection(1).Aop.OnLogExecuting = (sql, pars) => + { + var param = DbScoped.SugarScope.Utilities.SerializeObject(pars.ToDictionary(it => it.ParameterName, it => it.Value)); + + logger.Info($"Sql语句:{sql}, {param}"); + }; + //Db1错误日志 + db.GetConnection(1).Aop.OnError = (e) => + { + logger.Error($"执行Sql语句失败:{e.Sql},原因:{e.Message}"); + }; + #endregion + }); } /// diff --git a/ZR.Admin.WebApi/Extensions/TasksExtension.cs b/ZR.Admin.WebApi/Extensions/TasksExtension.cs index 0d75969..2ca150e 100644 --- a/ZR.Admin.WebApi/Extensions/TasksExtension.cs +++ b/ZR.Admin.WebApi/Extensions/TasksExtension.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Quartz.Spi; using System; +using System.Threading.Tasks; using ZR.Service.System.IService; using ZR.Tasks; @@ -20,7 +21,7 @@ namespace ZR.Admin.WebApi.Extensions //添加Quartz服务 services.AddSingleton(); //添加我们的服务 - services.AddTransient(); + //services.AddTransient(); services.AddTransient(); } @@ -41,7 +42,11 @@ namespace ZR.Admin.WebApi.Extensions //程序启动后注册所有定时任务 foreach (var task in tasks) { - _schedulerServer.AddTaskScheduleAsync(task); + var result = _schedulerServer.AddTaskScheduleAsync(task); + if (result.Result.Code == 200) + { + Console.WriteLine($"注册任务[{task.Name}]ID:{task.ID}成功"); + } } return app; diff --git a/ZR.Admin.WebApi/NLog.config b/ZR.Admin.WebApi/NLog.config index 4fb0fae..dc78b69 100644 --- a/ZR.Admin.WebApi/NLog.config +++ b/ZR.Admin.WebApi/NLog.config @@ -6,64 +6,68 @@ internalLogLevel="Info" internalLogFile="nlog-internal.log"> - - - - + + + + - - - - + + + + - - - - - - - + + + + + + + - - + + - - + + - - - + - - - - - - - - - - + + + - - - - + + + + + + + + + + + + + + + + diff --git a/ZR.Model/System/SysTasksQz.cs b/ZR.Model/System/SysTasksQz.cs index 26988b6..58deb98 100644 --- a/ZR.Model/System/SysTasksQz.cs +++ b/ZR.Model/System/SysTasksQz.cs @@ -153,5 +153,9 @@ namespace ZR.Model.System [SugarColumn(IsOnlyIgnoreInsert = true)]//设置后插入数据不会有此字段 [JsonProperty(propertyName: "UpdateTime")] public DateTime Update_time { get; set; } = DateTime.Now; + /// + /// 最后运行时间 + /// + public DateTime? LastRunTime { get; set; } } } diff --git a/ZR.Service/System/SysDictService.cs b/ZR.Service/System/SysDictService.cs index ba21897..f90fcb1 100644 --- a/ZR.Service/System/SysDictService.cs +++ b/ZR.Service/System/SysDictService.cs @@ -63,7 +63,7 @@ namespace ZR.Service.System foreach (var dictId in dictIds) { SysDictType dictType = DictRepository.GetFirst(x => x.DictId == dictId); - if (DictRepository.Count(f => f.DictType == dictType.DictType) > 0) + if (DictDataRepository.Count(f => f.DictType == dictType.DictType) > 0) { throw new CustomException($"{dictType.DictName}已分配,不能删除"); } diff --git a/ZR.Tasks/ITaskSchedulerServer.cs b/ZR.Tasks/ITaskSchedulerServer.cs index f9e6b73..2a6e7b9 100644 --- a/ZR.Tasks/ITaskSchedulerServer.cs +++ b/ZR.Tasks/ITaskSchedulerServer.cs @@ -23,6 +23,6 @@ namespace ZR.Tasks Task RunTaskScheduleAsync(SysTasksQz tasksQz); - Task UpdateTaskScheduleAsync(SysTasksQz tasksQz, string groupName); + Task UpdateTaskScheduleAsync(SysTasksQz tasksQz); } } diff --git a/ZR.Tasks/TaskScheduler/JobBase.cs b/ZR.Tasks/TaskScheduler/JobBase.cs index 48b9a6b..38a0780 100644 --- a/ZR.Tasks/TaskScheduler/JobBase.cs +++ b/ZR.Tasks/TaskScheduler/JobBase.cs @@ -2,7 +2,6 @@ using NLog; using Quartz; using System; -using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; using ZR.Model.System; @@ -22,7 +21,7 @@ namespace ZR.Tasks /// /// 作业上下文 /// 业务逻辑方法 - public async Task> ExecuteJob(IJobExecutionContext context, Func job) + public async Task ExecuteJob(IJobExecutionContext context, Func job) { double elapsed = 0; int status = 0; @@ -37,54 +36,54 @@ namespace ZR.Tasks await job(); stopwatch.Stop(); elapsed = stopwatch.Elapsed.TotalMilliseconds; - logMsg = "Succeed"; + logMsg = "success"; } catch (Exception ex) { - JobExecutionException e2 = new JobExecutionException(ex); - //true 是立即重新执行任务 - e2.RefireImmediately = true; + JobExecutionException e2 = new(ex) + { + //true 是立即重新执行任务 + RefireImmediately = true + }; status = 1; logMsg = $"Fail,Exception:{ex.Message}"; } - Dictionary dic = new Dictionary(); - dic.Add("elapsed", elapsed); - dic.Add("status", status); - dic.Add("content", logMsg); - RecordTaskLog(context, dic); - return dic; + var logModel = new SysTasksLog() + { + Elapsed = elapsed, + Status = status.ToString(), + JobMessage = logMsg + }; + + RecordTaskLog(context, logModel); + return logModel; } /// /// 记录到日志 /// /// - /// - protected void RecordTaskLog(IJobExecutionContext context, Dictionary executeLog) + /// + protected void RecordTaskLog(IJobExecutionContext context, SysTasksLog logModel) { var tasksLogService = (ISysTasksLogService)App.GetRequiredService(typeof(ISysTasksLogService)); var taskQzService = (ISysTasksQzService)App.GetRequiredService(typeof(ISysTasksQzService)); // 可以直接获取 JobDetail 的值 IJobDetail job = context.JobDetail; - //var param = context.MergedJobDataMap; - - // 也可以通过数据库配置,获取传递过来的参数 - //JobDataMap data = context.JobDetail.JobDataMap; - //int jobId = data.GetInt("JobParam"); - - var logModel = new SysTasksLog(); logModel.InvokeTarget = job.JobType.FullName; - logModel.Elapsed = (double)executeLog.GetValueOrDefault("elapsed", "0"); - logModel.JobMessage = executeLog.GetValueOrDefault("content").ToString(); - logModel.Status = executeLog.GetValueOrDefault("status", "0").ToString(); logModel = tasksLogService.AddTaskLog(job.Key.Name, logModel); - taskQzService.Update(f => f.ID == job.Key.Name, f => new SysTasksQz() + //成功后执行次数+1 + if (logModel.Status == "0") { - RunTimes = f.RunTimes + 1 - }); + taskQzService.Update(f => f.ID == job.Key.Name, f => new SysTasksQz() + { + RunTimes = f.RunTimes + 1, + LastRunTime = DateTime.Now + }); + } logger.Info($"执行任务【{job.Key.Name}|{logModel.JobName}】结果={logModel.JobMessage}"); } } diff --git a/ZR.Tasks/TaskScheduler/Job_SyncTest.cs b/ZR.Tasks/TaskScheduler/Job_SyncTest.cs index 137f981..5494c43 100644 --- a/ZR.Tasks/TaskScheduler/Job_SyncTest.cs +++ b/ZR.Tasks/TaskScheduler/Job_SyncTest.cs @@ -1,11 +1,14 @@ -using Quartz; +using Infrastructure.Attribute; +using Quartz; using System.Threading.Tasks; -namespace ZR.Tasks +namespace ZR.Tasks.TaskScheduler { /// /// 定时任务测试 + /// 使用如下注册后TaskExtensions里面不用再注册了 /// + [AppService(ServiceType = typeof(Job_SyncTest), ServiceLifetime = LifeTime.Scoped)] public class Job_SyncTest : JobBase, IJob { //private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); @@ -20,6 +23,7 @@ namespace ZR.Tasks await Task.Delay(1); //TODO 业务逻辑 + System.Console.WriteLine("job test"); } } } diff --git a/ZR.Tasks/TaskSchedulerServer.cs b/ZR.Tasks/TaskSchedulerServer.cs index 4be4403..8570680 100644 --- a/ZR.Tasks/TaskSchedulerServer.cs +++ b/ZR.Tasks/TaskSchedulerServer.cs @@ -2,10 +2,13 @@ using NLog; using Quartz; using Quartz.Impl; +using Quartz.Impl.Matchers; using Quartz.Impl.Triggers; using Quartz.Spi; using System; +using System.Collections.Generic; using System.Collections.Specialized; +using System.Linq; using System.Reflection; using System.Threading.Tasks; using ZR.Model.System; @@ -60,7 +63,7 @@ namespace ZR.Tasks }; StdSchedulerFactory factory = new StdSchedulerFactory(collection); - + return _scheduler = factory.GetScheduler(); } @@ -153,11 +156,15 @@ namespace ZR.Tasks trigger = CreateCronTrigger(tasksQz); //解决Quartz启动后第一次会立即执行问题解决办法 ((CronTriggerImpl)trigger).MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing; - - // 5、将触发器和任务器绑定到调度器中 - await _scheduler.Result.ScheduleJob(job, trigger); } - + else + { + trigger = CreateSimpleTrigger(tasksQz); + ((SimpleTriggerImpl)trigger).MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing; + } + + // 5、将触发器和任务器绑定到调度器中 + await _scheduler.Result.ScheduleJob(job, trigger); //任务没有启动、暂停任务 if (!tasksQz.IsStart) { @@ -252,8 +259,19 @@ namespace ZR.Tasks try { JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup); + List jobKeys = _scheduler.Result.GetJobKeys(GroupMatcher.GroupEquals(tasksQz.JobGroup)).Result.ToList(); + if (jobKeys == null || jobKeys.Count == 0) + { + return new ApiResult(110, $"未找到分组[{ tasksQz.JobGroup }]"); + } + var triggers = await _scheduler.Result.GetTriggersOfJob(jobKey); + if (triggers.Count <= 0) + { + return new ApiResult(110, $"未找到触发器[{jobKey.Name}]"); + } await _scheduler.Result.TriggerJob(jobKey); + return ApiResult.Success($"运行计划任务:【{tasksQz.Name}】成功"); } catch (Exception ex) @@ -267,11 +285,11 @@ namespace ZR.Tasks /// /// /// - public async Task UpdateTaskScheduleAsync(SysTasksQz tasksQz, string groupName) + public async Task UpdateTaskScheduleAsync(SysTasksQz tasksQz) { try { - JobKey jobKey = new JobKey(tasksQz.ID, groupName); + JobKey jobKey = new JobKey(tasksQz.ID, tasksQz.JobGroup); if (await _scheduler.Result.CheckExists(jobKey)) { //防止创建时存在数据问题 先移除,然后在执行创建操作 @@ -294,33 +312,30 @@ namespace ZR.Tasks /// /// /// - //private ITrigger CreateSimpleTrigger(SysTasksQz tasksQz) - //{ - // if (tasksQz.RunTimes > 0) - // { - // ITrigger trigger = TriggerBuilder.Create() - // .WithIdentity(tasksQz.ID, tasksQz.JobGroup) - // .StartAt(tasksQz.BeginTime.Value) - // .EndAt(tasksQz.EndTime.Value) - // .WithSimpleSchedule(x => - // x.WithIntervalInSeconds(tasksQz.IntervalSecond) - // .WithRepeatCount(tasksQz.RunTimes)).ForJob(tasksQz.ID, tasksQz.JobGroup).Build(); - // return trigger; - // } - // else - // { - // ITrigger trigger = TriggerBuilder.Create() - // .WithIdentity(tasksQz.ID, tasksQz.JobGroup) - // .StartAt(tasksQz.BeginTime.Value) - // .EndAt(tasksQz.EndTime.Value) - // .WithSimpleSchedule(x => - // x.WithIntervalInSeconds(tasksQz.IntervalSecond) - // .RepeatForever()).ForJob(tasksQz.ID, tasksQz.JobGroup).Build(); - // return trigger; - // } - // // 触发作业立即运行,然后每10秒重复一次,无限循环 - - //} + private ITrigger CreateSimpleTrigger(SysTasksQz tasksQz) + { + if (tasksQz.RunTimes > 0) + { + ITrigger trigger = TriggerBuilder.Create() + .WithIdentity(tasksQz.ID, tasksQz.JobGroup) + .StartAt(tasksQz.BeginTime.Value) + .EndAt(tasksQz.EndTime.Value) + .WithSimpleSchedule(x => x.WithIntervalInSeconds(tasksQz.IntervalSecond) + .WithRepeatCount(tasksQz.RunTimes)).ForJob(tasksQz.ID, tasksQz.JobGroup).Build(); + return trigger; + } + else + { + ITrigger trigger = TriggerBuilder.Create() + .WithIdentity(tasksQz.ID, tasksQz.JobGroup) + .StartAt(tasksQz.BeginTime.Value) + .EndAt(tasksQz.EndTime.Value) + .WithSimpleSchedule(x => x.WithIntervalInSeconds(tasksQz.IntervalSecond) + .RepeatForever()).ForJob(tasksQz.ID, tasksQz.JobGroup).Build(); + return trigger; + } + // 触发作业立即运行,然后每10秒重复一次,无限循环 + } /// /// 创建类型Cron的触发器 diff --git a/ZR.Vue/src/layout/components/Sidebar/Item.vue b/ZR.Vue/src/layout/components/Sidebar/Item.vue index b515f61..0b584e1 100644 --- a/ZR.Vue/src/layout/components/Sidebar/Item.vue +++ b/ZR.Vue/src/layout/components/Sidebar/Item.vue @@ -17,11 +17,19 @@ export default { const vnodes = [] if (icon) { - vnodes.push() + vnodes.push() } if (title) { - vnodes.push({(title)}) + if (title.length > 5) { + vnodes.push( + + {title} + + ) + } else { + vnodes.push({title}) + } } return vnodes } diff --git a/ZR.Vue/src/settings.js b/ZR.Vue/src/settings.js index 643618f..ee93a3e 100644 --- a/ZR.Vue/src/settings.js +++ b/ZR.Vue/src/settings.js @@ -2,7 +2,7 @@ module.exports = { /** * 框架版本号 */ - version: '3.7.7', + version: '3.7.8', title: 'ZrAdmin.NET-后台管理', /** * 主题颜色 diff --git a/ZR.Vue/src/views/monitor/job/index.vue b/ZR.Vue/src/views/monitor/job/index.vue index f87b4c5..2809c55 100644 --- a/ZR.Vue/src/views/monitor/job/index.vue +++ b/ZR.Vue/src/views/monitor/job/index.vue @@ -34,23 +34,23 @@ - - + - - + + - - + + + - - + + +