diff --git a/Admin.NET/Admin.NET.Core/EventBus/RedisEventSourceStorer.cs b/Admin.NET/Admin.NET.Core/EventBus/RedisEventSourceStorer.cs
index 272b5ba1..475bdf41 100644
--- a/Admin.NET/Admin.NET.Core/EventBus/RedisEventSourceStorer.cs
+++ b/Admin.NET/Admin.NET.Core/EventBus/RedisEventSourceStorer.cs
@@ -4,6 +4,8 @@
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
+using NewLife.Caching.Queues;
+using Newtonsoft.Json;
using System.Threading.Channels;
namespace Admin.NET.Core;
@@ -11,6 +13,12 @@ namespace Admin.NET.Core;
///
/// Redis自定义事件源存储器
///
+///
+/// 在集群部署时,一般每一个消息只由一个服务节点消费一次。
+/// 有些特殊情情要通知到服务器群中的每一个节点(比如需要强制加载某些配置、重点服务等),
+/// 在这种情况下就要以“broadcast:”开头来定义EventId,
+/// 本系统会把“broadcast:”开头的事件视为“广播消息”保证集群中的每一个服务节点都能消费得到这个消息
+///
public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
{
///
@@ -23,11 +31,9 @@ public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
///
private readonly Channel _channel;
- /////
- ///// Redis 连接对象
- /////
- //private readonly FullRedis _redis;
- private IProducerConsumer _queue;
+ private IProducerConsumer _queueSingle;
+
+ private RedisStream _queueBroadcast;
///
/// 路由键
@@ -54,24 +60,48 @@ public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
//_redis = redis as FullRedis;
_routeKey = routeKey;
- // 创建消息订阅者
- _queue = cacheProvider.GetQueue(routeKey);
- _eventConsumer = new EventConsumer(_queue);
+ // 创建广播消息订阅者,即所有服务器节点都能收到消息(用来发布重启、Reload配置等消息)
+ FullRedis redis = (FullRedis)cacheProvider.Cache;
+ var clusterOpt = App.GetConfig("Cluster", true);
+ _queueBroadcast = redis.GetStream(routeKey + ":broadcast");
+ _queueBroadcast.Group = clusterOpt.ServerId;//根据服务器标识分配到不同的分组里
+ _queueBroadcast.Expire = TimeSpan.FromSeconds(10);//消息10秒过期()
+ _queueBroadcast.ConsumeAsync(OnConsumeBroadcast);
+
+ // 创建队列消息订阅者,只要有一个服务节点消费了消息即可
+ _queueSingle = redis.GetQueue(routeKey + ":single");
+ _eventConsumer = new EventConsumer(_queueSingle);
// 订阅消息写入 Channel
_eventConsumer.Received += (send, cr) =>
{
- // 反序列化消息
- //var eventSource = JsonConvert.DeserializeObject(cr);
-
- // 写入内存管道存储器
- _channel.Writer.WriteAsync(cr);
+ var oriColor = Console.ForegroundColor;
+ ChannelEventSource ces = (ChannelEventSource)cr;
+ ConsumeChannelEventSource(ces);
};
-
- // 启动消费者
_eventConsumer.Start();
}
+ private Task OnConsumeBroadcast(string source, Message message, CancellationToken token)
+ {
+ ChannelEventSource ces = JsonConvert.DeserializeObject(source);
+ ConsumeChannelEventSource(ces);
+ return Task.CompletedTask;
+ }
+
+ private void ConsumeChannelEventSource(ChannelEventSource ces)
+ {
+ // 打印测试事件
+ if (ces.EventId != null && ces.EventId.IndexOf(":Test") > 0)
+ {
+ var oriColor = Console.ForegroundColor;
+ Console.ForegroundColor = ConsoleColor.Green;
+ Console.WriteLine($"有消息要处理{ces.EventId},{ces.Payload}");
+ Console.ForegroundColor = oriColor;
+ }
+ _channel.Writer.WriteAsync(ces);
+ }
+
///
/// 将事件源写入存储器
///
@@ -82,29 +112,28 @@ public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
{
// 空检查
if (eventSource == default)
- {
throw new ArgumentNullException(nameof(eventSource));
- }
// 这里判断是否是 ChannelEventSource 或者 自定义的 EventSource
if (eventSource is ChannelEventSource source)
{
- // 序列化消息
- //var data = JsonSerializer.Serialize(source);
-
- //// 获取一个订阅对象
- //var queue = _redis.GetQueue(_routeKey);
-
// 异步发布
await Task.Factory.StartNew(() =>
{
- //queue.Add(source);
- _queue.Add(source);
+ if (source.EventId != null && source.EventId.StartsWith("broadcast:"))
+ {
+ string str = JsonConvert.SerializeObject(source);
+ _queueBroadcast.Add(str);
+ }
+ else
+ {
+ _queueSingle.Add(source);
+ }
}, cancellationToken, TaskCreationOptions.LongRunning, System.Threading.Tasks.TaskScheduler.Default);
}
else
{
- // 这里处理动态订阅问题
+ // 处理动态订阅问题
await _channel.Writer.WriteAsync(eventSource, cancellationToken);
}
}
diff --git a/Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs b/Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs
index b621a55a..7d684e28 100644
--- a/Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs
+++ b/Admin.NET/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs
@@ -380,7 +380,7 @@ public static class SqlSugarSetup
.Where(u => !u.GetCustomAttributes().Any())
.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) &&
!u.IsDefined(typeof(SysTableAttribute), false) && !u.IsDefined(typeof(LogTableAttribute), false) && !u.IsDefined(typeof(TenantAttribute), false)).ToList();
- if (!entityTypes.Any()) return;
+ if (entityTypes.Count == 0) return;
foreach (var entityType in entityTypes)
{
diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/editDialog.vue.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/editDialog.vue.vm
index 83b5d7df..21874350 100644
--- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/editDialog.vue.vm
+++ b/Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/editDialog.vue.vm
@@ -78,7 +78,7 @@
@:
@:
@:
- @:
+ @:
@:
@:
diff --git a/Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/index.vue.vm b/Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/index.vue.vm
index 8eb33055..1877b132 100644
--- a/Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/index.vue.vm
+++ b/Admin.NET/Admin.NET.Web.Entry/wwwroot/Template/index.vue.vm
@@ -53,7 +53,7 @@
-
+
@@ -194,7 +194,7 @@ import { Local } from '/@@/utils/storage';
@:import { codeToName, getConstType } from "/@@/utils/constHelper";
}
@if(@Model.TableField.Any(x=>x.EffectType == "Select") || @Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
-@:import { getDictDataItem as di, getDictDataList as dl } from '/@@/utils/dict-utils';
+@:import { getDictLabelByVal as di, getDictDataList as dl } from '/@@/utils/dict-utils';
}
@if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
@:import { getDictLabelByVal as dv } from '/@@/utils/dict-utils';
@@ -313,7 +313,7 @@ const options = useVxeTable<@(@Model.ClassName)>(
// 工具栏配置
toolbarConfig: { export: false },
// 行设置
- rowConfig: { height: 80 },
+ // rowConfig: { height: 80 },
}
);
diff --git a/Web/src/views/home/widgets/components/schedule.vue b/Web/src/views/home/widgets/components/schedule.vue
index f6f2535a..2437c671 100644
--- a/Web/src/views/home/widgets/components/schedule.vue
+++ b/Web/src/views/home/widgets/components/schedule.vue
@@ -78,7 +78,6 @@ onMounted(async () => {
// 查询操作
const handleQuery = async () => {
- debugger;
state.queryParams.startTime = GetMonthFirstDay(state.calendarValue);
state.queryParams.endTime = GetMonthLastDay(state.calendarValue);
diff --git a/Web/src/views/home/widgets/components/scheduleEdit.vue b/Web/src/views/home/widgets/components/scheduleEdit.vue
index 983c55ef..cee9a032 100644
--- a/Web/src/views/home/widgets/components/scheduleEdit.vue
+++ b/Web/src/views/home/widgets/components/scheduleEdit.vue
@@ -78,8 +78,6 @@ const cancel = () => {
// 提交
const submit = () => {
- console.log(JSON.stringify(state.ruleForm));
-
ruleFormRef.value.validate(async (valid: boolean) => {
if (!valid) return;
if (state.ruleForm.id != undefined && state.ruleForm.id > 0) {
diff --git a/Web/src/views/system/print/component/hiprint/preview.vue b/Web/src/views/system/print/component/hiprint/preview.vue
index e453dd22..0cf5aa0a 100644
--- a/Web/src/views/system/print/component/hiprint/preview.vue
+++ b/Web/src/views/system/print/component/hiprint/preview.vue
@@ -48,7 +48,7 @@ const showDialog = (hiprintTemplate: any, printData: {}, width = 210, printType
const print = () => {
state.waitShowPrinter = true;
- debugger;
+ // debugger;
// 判断是否已成功连接
if (state.printType == 2) {
// 注意:连接是异步的