Merge pull request 'main' (#5) from Admin.NET/Admin.NET.Pro:main into main
Reviewed-on: http://101.43.53.74:3000/shuerchoi/Admin.NET.Pro/pulls/5
This commit is contained in:
commit
6242834a7d
@ -4,6 +4,8 @@
|
|||||||
//
|
//
|
||||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||||
|
|
||||||
|
using NewLife.Caching.Queues;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace Admin.NET.Core;
|
namespace Admin.NET.Core;
|
||||||
@ -11,6 +13,12 @@ namespace Admin.NET.Core;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Redis自定义事件源存储器
|
/// Redis自定义事件源存储器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 在集群部署时,一般每一个消息只由一个服务节点消费一次。
|
||||||
|
/// 有些特殊情情要通知到服务器群中的每一个节点(比如需要强制加载某些配置、重点服务等),
|
||||||
|
/// 在这种情况下就要以“broadcast:”开头来定义EventId,
|
||||||
|
/// 本系统会把“broadcast:”开头的事件视为“广播消息”保证集群中的每一个服务节点都能消费得到这个消息
|
||||||
|
/// </remarks>
|
||||||
public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
|
public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -23,11 +31,9 @@ public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Channel<IEventSource> _channel;
|
private readonly Channel<IEventSource> _channel;
|
||||||
|
|
||||||
///// <summary>
|
private IProducerConsumer<ChannelEventSource> _queueSingle;
|
||||||
///// Redis 连接对象
|
|
||||||
///// </summary>
|
private RedisStream<string> _queueBroadcast;
|
||||||
//private readonly FullRedis _redis;
|
|
||||||
private IProducerConsumer<ChannelEventSource> _queue;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 路由键
|
/// 路由键
|
||||||
@ -54,24 +60,48 @@ public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
|
|||||||
//_redis = redis as FullRedis;
|
//_redis = redis as FullRedis;
|
||||||
_routeKey = routeKey;
|
_routeKey = routeKey;
|
||||||
|
|
||||||
// 创建消息订阅者
|
// 创建广播消息订阅者,即所有服务器节点都能收到消息(用来发布重启、Reload配置等消息)
|
||||||
_queue = cacheProvider.GetQueue<ChannelEventSource>(routeKey);
|
FullRedis redis = (FullRedis)cacheProvider.Cache;
|
||||||
_eventConsumer = new EventConsumer<ChannelEventSource>(_queue);
|
var clusterOpt = App.GetConfig<ClusterOptions>("Cluster", true);
|
||||||
|
_queueBroadcast = redis.GetStream<string>(routeKey + ":broadcast");
|
||||||
|
_queueBroadcast.Group = clusterOpt.ServerId;//根据服务器标识分配到不同的分组里
|
||||||
|
_queueBroadcast.Expire = TimeSpan.FromSeconds(10);//消息10秒过期()
|
||||||
|
_queueBroadcast.ConsumeAsync(OnConsumeBroadcast);
|
||||||
|
|
||||||
|
// 创建队列消息订阅者,只要有一个服务节点消费了消息即可
|
||||||
|
_queueSingle = redis.GetQueue<ChannelEventSource>(routeKey + ":single");
|
||||||
|
_eventConsumer = new EventConsumer<ChannelEventSource>(_queueSingle);
|
||||||
|
|
||||||
// 订阅消息写入 Channel
|
// 订阅消息写入 Channel
|
||||||
_eventConsumer.Received += (send, cr) =>
|
_eventConsumer.Received += (send, cr) =>
|
||||||
{
|
{
|
||||||
// 反序列化消息
|
var oriColor = Console.ForegroundColor;
|
||||||
//var eventSource = JsonConvert.DeserializeObject<ChannelEventSource>(cr);
|
ChannelEventSource ces = (ChannelEventSource)cr;
|
||||||
|
ConsumeChannelEventSource(ces);
|
||||||
// 写入内存管道存储器
|
|
||||||
_channel.Writer.WriteAsync(cr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 启动消费者
|
|
||||||
_eventConsumer.Start();
|
_eventConsumer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Task OnConsumeBroadcast(string source, Message message, CancellationToken token)
|
||||||
|
{
|
||||||
|
ChannelEventSource ces = JsonConvert.DeserializeObject<ChannelEventSource>(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);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将事件源写入存储器
|
/// 将事件源写入存储器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -82,29 +112,28 @@ public sealed class RedisEventSourceStorer : IEventSourceStorer, IDisposable
|
|||||||
{
|
{
|
||||||
// 空检查
|
// 空检查
|
||||||
if (eventSource == default)
|
if (eventSource == default)
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(eventSource));
|
throw new ArgumentNullException(nameof(eventSource));
|
||||||
}
|
|
||||||
|
|
||||||
// 这里判断是否是 ChannelEventSource 或者 自定义的 EventSource
|
// 这里判断是否是 ChannelEventSource 或者 自定义的 EventSource
|
||||||
if (eventSource is ChannelEventSource source)
|
if (eventSource is ChannelEventSource source)
|
||||||
{
|
{
|
||||||
// 序列化消息
|
|
||||||
//var data = JsonSerializer.Serialize(source);
|
|
||||||
|
|
||||||
//// 获取一个订阅对象
|
|
||||||
//var queue = _redis.GetQueue<ChannelEventSource>(_routeKey);
|
|
||||||
|
|
||||||
// 异步发布
|
// 异步发布
|
||||||
await Task.Factory.StartNew(() =>
|
await Task.Factory.StartNew(() =>
|
||||||
{
|
{
|
||||||
//queue.Add(source);
|
if (source.EventId != null && source.EventId.StartsWith("broadcast:"))
|
||||||
_queue.Add(source);
|
{
|
||||||
|
string str = JsonConvert.SerializeObject(source);
|
||||||
|
_queueBroadcast.Add(str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_queueSingle.Add(source);
|
||||||
|
}
|
||||||
}, cancellationToken, TaskCreationOptions.LongRunning, System.Threading.Tasks.TaskScheduler.Default);
|
}, cancellationToken, TaskCreationOptions.LongRunning, System.Threading.Tasks.TaskScheduler.Default);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 这里处理动态订阅问题
|
// 处理动态订阅问题
|
||||||
await _channel.Writer.WriteAsync(eventSource, cancellationToken);
|
await _channel.Writer.WriteAsync(eventSource, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -380,7 +380,7 @@ public static class SqlSugarSetup
|
|||||||
.Where(u => !u.GetCustomAttributes<IgnoreTableAttribute>().Any())
|
.Where(u => !u.GetCustomAttributes<IgnoreTableAttribute>().Any())
|
||||||
.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(SugarTable), false) &&
|
.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();
|
!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)
|
foreach (var entityType in entityTypes)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -78,7 +78,7 @@
|
|||||||
@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
@:<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
|
||||||
@:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
|
@:<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
|
||||||
@:<el-select clearable v-model="state.ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
|
@:<el-select clearable v-model="state.ruleForm.@(@column.LowerPropertyName)" placeholder="请选择@(@column.ColumnComment)">
|
||||||
@:<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')" :key="index" :value="@(@column.NetType.StartsWith("string") ? "item.code" : "Number(item.value)")" :label="`[${item.code}] ${item.value}`"></el-option>
|
@:<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')" :key="index" :value="@(@column.NetType.StartsWith("string") ? "item.code" : "Number(item.value)")" :label="`${item.name} [${item.code}] ${item.value}`"></el-option>
|
||||||
@:
|
@:
|
||||||
</el-select>
|
</el-select>
|
||||||
@:
|
@:
|
||||||
|
|||||||
@ -53,7 +53,7 @@
|
|||||||
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb5" v-if="state.showAdvanceQueryUI">
|
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="6" class="mb5" v-if="state.showAdvanceQueryUI">
|
||||||
<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
|
<el-form-item label="@column.ColumnComment" prop="@(@column.LowerPropertyName)">
|
||||||
<el-select v-model="state.queryParams.@(@column.LowerPropertyName)" filterable placeholder="请选择@(@column.ColumnComment)" clearable @@keyup.enter.native="handleQuery" >
|
<el-select v-model="state.queryParams.@(@column.LowerPropertyName)" filterable placeholder="请选择@(@column.ColumnComment)" clearable @@keyup.enter.native="handleQuery" >
|
||||||
<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')" :key="index" :value="item.code" :label="`${item.name} [${item.code}] ${item.value}`" />
|
<el-option v-for="(item,index) in dl('@(@column.DictTypeCode)')" :key="index" :value="item.value" :label="`${item.name} [${item.code}] ${item.value}`" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -194,7 +194,7 @@ import { Local } from '/@@/utils/storage';
|
|||||||
@:import { codeToName, getConstType } from "/@@/utils/constHelper";
|
@:import { codeToName, getConstType } from "/@@/utils/constHelper";
|
||||||
}
|
}
|
||||||
@if(@Model.TableField.Any(x=>x.EffectType == "Select") || @Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
|
@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")){
|
@if(@Model.TableField.Any(x=>x.EffectType == "EnumSelector")){
|
||||||
@:import { getDictLabelByVal as dv } from '/@@/utils/dict-utils';
|
@:import { getDictLabelByVal as dv } from '/@@/utils/dict-utils';
|
||||||
@ -313,7 +313,7 @@ const options = useVxeTable<@(@Model.ClassName)>(
|
|||||||
// 工具栏配置
|
// 工具栏配置
|
||||||
toolbarConfig: { export: false },
|
toolbarConfig: { export: false },
|
||||||
// 行设置
|
// 行设置
|
||||||
rowConfig: { height: 80 },
|
// rowConfig: { height: 80 },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -78,7 +78,6 @@ onMounted(async () => {
|
|||||||
|
|
||||||
// 查询操作
|
// 查询操作
|
||||||
const handleQuery = async () => {
|
const handleQuery = async () => {
|
||||||
debugger;
|
|
||||||
state.queryParams.startTime = GetMonthFirstDay(state.calendarValue);
|
state.queryParams.startTime = GetMonthFirstDay(state.calendarValue);
|
||||||
state.queryParams.endTime = GetMonthLastDay(state.calendarValue);
|
state.queryParams.endTime = GetMonthLastDay(state.calendarValue);
|
||||||
|
|
||||||
|
|||||||
@ -78,8 +78,6 @@ const cancel = () => {
|
|||||||
|
|
||||||
// 提交
|
// 提交
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
console.log(JSON.stringify(state.ruleForm));
|
|
||||||
|
|
||||||
ruleFormRef.value.validate(async (valid: boolean) => {
|
ruleFormRef.value.validate(async (valid: boolean) => {
|
||||||
if (!valid) return;
|
if (!valid) return;
|
||||||
if (state.ruleForm.id != undefined && state.ruleForm.id > 0) {
|
if (state.ruleForm.id != undefined && state.ruleForm.id > 0) {
|
||||||
|
|||||||
@ -48,7 +48,7 @@ const showDialog = (hiprintTemplate: any, printData: {}, width = 210, printType
|
|||||||
|
|
||||||
const print = () => {
|
const print = () => {
|
||||||
state.waitShowPrinter = true;
|
state.waitShowPrinter = true;
|
||||||
debugger;
|
// debugger;
|
||||||
// 判断是否已成功连接
|
// 判断是否已成功连接
|
||||||
if (state.printType == 2) {
|
if (state.printType == 2) {
|
||||||
// 注意:连接是异步的
|
// 注意:连接是异步的
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user