diff --git a/Tnb.Server.sln b/Tnb.Server.sln
index 8b41da8c..f9236fb7 100644
--- a/Tnb.Server.sln
+++ b/Tnb.Server.sln
@@ -140,7 +140,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tnb.PerMgr.Entities", "PerM
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tnb.PerMgr.Interfaces", "PerMgr\Tnb.PerMgr.Interfaces\Tnb.PerMgr.Interfaces.csproj", "{F3656494-27D3-4BD7-B831-8D909DFBD7B9}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tnb.VmodelEngine", "visualdev\Tnb.Vmodel\Tnb.VmodelEngine.csproj", "{437AE0E4-66AE-4627-9ACD-29F5BB9E6642}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tnb.Vengine", "visualdev\Tnb.Vengine\Tnb.Vengine.csproj", "{644FEE7B-A58A-40F3-A011-3B73235B81D6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -340,10 +340,10 @@ Global
{F3656494-27D3-4BD7-B831-8D909DFBD7B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3656494-27D3-4BD7-B831-8D909DFBD7B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3656494-27D3-4BD7-B831-8D909DFBD7B9}.Release|Any CPU.Build.0 = Release|Any CPU
- {437AE0E4-66AE-4627-9ACD-29F5BB9E6642}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {437AE0E4-66AE-4627-9ACD-29F5BB9E6642}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {437AE0E4-66AE-4627-9ACD-29F5BB9E6642}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {437AE0E4-66AE-4627-9ACD-29F5BB9E6642}.Release|Any CPU.Build.0 = Release|Any CPU
+ {644FEE7B-A58A-40F3-A011-3B73235B81D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {644FEE7B-A58A-40F3-A011-3B73235B81D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {644FEE7B-A58A-40F3-A011-3B73235B81D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {644FEE7B-A58A-40F3-A011-3B73235B81D6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -396,7 +396,7 @@ Global
{D41946CF-09C6-4CA4-A1F4-42E7E1538BF7} = {74AB6486-1090-4CC9-9D1A-F1245E3ECFC3}
{42AD083D-D199-4B09-ADD8-89251011C959} = {74AB6486-1090-4CC9-9D1A-F1245E3ECFC3}
{F3656494-27D3-4BD7-B831-8D909DFBD7B9} = {74AB6486-1090-4CC9-9D1A-F1245E3ECFC3}
- {437AE0E4-66AE-4627-9ACD-29F5BB9E6642} = {161853F8-ADB9-4281-B706-E2E23D40D0F1}
+ {644FEE7B-A58A-40F3-A011-3B73235B81D6} = {161853F8-ADB9-4281-B706-E2E23D40D0F1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {646DDD1C-F143-42C2-894F-F5C7B3A0CE74}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr.Entities/Attributes/TimedAttribute.cs b/WarehouseMgr/Tnb.WarehouseMgr.Entities/Attributes/TimedAttribute.cs
new file mode 100644
index 00000000..1b8edfd3
--- /dev/null
+++ b/WarehouseMgr/Tnb.WarehouseMgr.Entities/Attributes/TimedAttribute.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tnb.WarehouseMgr.Entities.Attributes
+{
+ ///
+ ///定时任务特性
+ ///
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
+ public class TimedAttribute : Attribute
+ {
+ public string Name { get; set; }
+ }
+}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr.Entities/Dto/Inputs/NotifyMessage.cs b/WarehouseMgr/Tnb.WarehouseMgr.Entities/Dto/Inputs/NotifyMessage.cs
new file mode 100644
index 00000000..0e4362e2
--- /dev/null
+++ b/WarehouseMgr/Tnb.WarehouseMgr.Entities/Dto/Inputs/NotifyMessage.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tnb.WarehouseMgr.Entities.Dto.Inputs
+{
+ ///
+ /// 通知消息类
+ ///
+ public class NotifyMessage
+ {
+ ///
+ /// 任务名称
+ ///
+ public string TaskName { get; set; } = string.Empty;
+ }
+}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/ITaskMessageNotify.cs b/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/ITaskMessageNotify.cs
new file mode 100644
index 00000000..1b6eb44a
--- /dev/null
+++ b/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/ITaskMessageNotify.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Channels;
+using System.Threading.Tasks;
+using Tnb.WarehouseMgr.Entities.Dto.Inputs;
+
+namespace Tnb.WarehouseMgr.Interfaces
+{
+ ///
+ /// 任务消息通知接口
+ ///
+ public interface ITaskMessageNotify
+ {
+ ChannelReader Reader { get; }
+ ChannelWriter Writer { get; }
+ }
+}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/Filters/NotifyFilterAttribute.cs b/WarehouseMgr/Tnb.WarehouseMgr/Filters/NotifyFilterAttribute.cs
new file mode 100644
index 00000000..059efe54
--- /dev/null
+++ b/WarehouseMgr/Tnb.WarehouseMgr/Filters/NotifyFilterAttribute.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.Extensions.DependencyInjection;
+using Tnb.WarehouseMgr.Entities.Dto.Inputs;
+using Tnb.WarehouseMgr.Interfaces;
+
+namespace Tnb.WarehouseMgr.Filters
+{
+ public class NotifyFilterAttribute : ActionFilterAttribute
+ {
+
+ public override async void OnActionExecuted(ActionExecutedContext context)
+ {
+ var actionName = context.ActionDescriptor.RouteValues["action"]!;
+ var taskMessageNotify = context.HttpContext.RequestServices.GetRequiredService();
+ if (taskMessageNotify != null)
+ {
+ NotifyMessage message = new() { TaskName = actionName };
+ await taskMessageNotify.Writer.WriteAsync(message);
+ }
+ }
+ }
+}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/TaskMesageNotify.cs b/WarehouseMgr/Tnb.WarehouseMgr/TaskMesageNotify.cs
new file mode 100644
index 00000000..6e0b6cc1
--- /dev/null
+++ b/WarehouseMgr/Tnb.WarehouseMgr/TaskMesageNotify.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Channels;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Tnb.WarehouseMgr.Entities.Dto.Inputs;
+using Tnb.WarehouseMgr.Interfaces;
+
+namespace Tnb.WarehouseMgr
+{
+ ///
+ /// 任务消息通知
+ ///
+ public class TaskMesageNotify : ITaskMessageNotify
+ {
+ private readonly Channel _channel = Channel.CreateUnbounded();
+
+ public ChannelReader Reader => _channel.Reader;
+ public ChannelWriter Writer => _channel.Writer;
+
+ }
+
+
+ //public static class TaskMesageNotify
+ //{
+ // private static readonly Channel _channel = Channel.CreateUnbounded();
+
+
+ // public static ChannelReader Reader => _channel.Reader;
+ // public static ChannelWriter Writer => _channel.Writer;
+
+
+ //}
+
+ public static class TaskMessageNotifyExtensions
+ {
+ public static IServiceCollection AddTaskMessageNotify(this IServiceCollection services)
+ {
+ services.AddSingleton();
+ return services;
+ }
+ }
+}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/TimedTaskBackgroundService.cs b/WarehouseMgr/Tnb.WarehouseMgr/TimedTaskBackgroundService.cs
index 55ba81df..4e999c48 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr/TimedTaskBackgroundService.cs
+++ b/WarehouseMgr/Tnb.WarehouseMgr/TimedTaskBackgroundService.cs
@@ -1,6 +1,9 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
+using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Aop.Api.Domain;
@@ -17,9 +20,12 @@ using JNPF.Message.Interfaces.Message;
using JNPF.Systems.Entitys.System;
using Mapster;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
+using Natasha.CSharp;
using Tnb.Common.Extension;
+using Tnb.WarehouseMgr.Entities.Attributes;
using Tnb.WarehouseMgr.Entities.Dto.Inputs;
using Tnb.WarehouseMgr.Entities.Exceptions;
using Tnb.WarehouseMgr.Interfaces;
@@ -33,27 +39,58 @@ namespace Tnb.WarehouseMgr
public class TimedTaskBackgroundService : BackgroundService
{
private IEventPublisher _eventPublisher = default!;
+ private readonly IServiceProvider _serviceProvider;
+ private static Dictionary> _timedFuncMap = new(StringComparer.OrdinalIgnoreCase);
+ static TimedTaskBackgroundService()
+ {
+ _timedFuncMap = App.EffectiveTypes.AsParallel().Where(t => !t.Namespace.IsNullOrWhiteSpace() && t.Namespace.Contains("Tnb.WarehouseMgr")).SelectMany(t => t.GetMethods())
+ .Where(m => m.GetCustomAttribute() != null)
+ .ToDictionary(x => x.Name, x =>
+ (Func)Delegate.CreateDelegate(typeof(Func), App.GetService(x.DeclaringType), x));
+ }
+ public TimedTaskBackgroundService(IServiceProvider serviceProvider)
+ {
+ _serviceProvider = serviceProvider;
+ }
- protected override Task ExecuteAsync(CancellationToken stoppingToken) => Task.Run(() =>
+ protected override Task ExecuteAsync(CancellationToken stoppingToken) => Task.Run(async () =>
{
- _eventPublisher = App.GetRequiredService();
- List toUserIds = new List() { "25398501929509" };
- //生成任务执行
- CancellationTokenSource genTaskCTS = new();
- CancellationTokenSource kittingOutAddCts = new();
- CancellationTokenSource kittingOutShippedCts = new();
- CancellationTokenSource setSortingCts = new();
- var wareHouseService = App.GetRequiredService();
- TimedTask(cts => wareHouseService.GenTaskExecute(cts), genTaskCTS, toUserIds);
- //齐套出库
+ var channelReader = _serviceProvider.GetRequiredService().Reader;
- var kittingOutService = App.GetRequiredService();
- TimedTask(cts => kittingOutService.KittingOutByAdd(cts), kittingOutAddCts, toUserIds);
- TimedTask(cts => kittingOutService.KittingOutByIsToBeShipped(cts), kittingOutShippedCts, toUserIds);
- //齐套分拣
- var setSortingService = App.GetRequiredService();
- TimedTask(cts => setSortingService.PackSortingByAdd(cts), setSortingCts, toUserIds);
+ CancellationTokenSource? cts = new();
+
+ while (channelReader != null && await channelReader.WaitToReadAsync())
+ {
+ while (channelReader.TryRead(out var message))
+ {
+ if (_timedFuncMap.ContainsKey(message.TaskName))
+ {
+ await _timedFuncMap[message.TaskName].Invoke(cts);
+ }
+ }
+ }
+
+ #region 定时
+ //_eventPublisher = App.GetRequiredService();
+ //List toUserIds = new List() { "25398501929509" };
+ ////生成任务执行
+ //CancellationTokenSource genTaskCTS = new();
+ //CancellationTokenSource kittingOutAddCts = new();
+ //CancellationTokenSource kittingOutShippedCts = new();
+ //CancellationTokenSource setSortingCts = new();
+
+ //var wareHouseService = App.GetRequiredService();
+ //TimedTask(cts => wareHouseService.GenTaskExecute(cts), genTaskCTS, toUserIds);
+ ////齐套出库
+
+ //var kittingOutService = App.GetRequiredService();
+ //TimedTask(cts => kittingOutService.KittingOutByAdd(cts), kittingOutAddCts, toUserIds);
+ //TimedTask(cts => kittingOutService.KittingOutByIsToBeShipped(cts), kittingOutShippedCts, toUserIds);
+ ////齐套分拣
+ //var setSortingService = App.GetRequiredService();
+ //TimedTask(cts => setSortingService.PackSortingByAdd(cts), setSortingCts, toUserIds);
+ #endregion
}, stoppingToken);
private Task TimedTask(Func action, CancellationTokenSource cts, List? toUserIds = default)
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs b/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs
index 110667c6..0cf4ba48 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs
+++ b/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs
@@ -43,6 +43,7 @@ using Tnb.BasicData.Entities.Enums;
using Tnb.Common.Extension;
using Tnb.Common.Utils;
using Tnb.WarehouseMgr.Entities;
+using Tnb.WarehouseMgr.Entities.Attributes;
using Tnb.WarehouseMgr.Entities.Configs;
using Tnb.WarehouseMgr.Entities.Consts;
using Tnb.WarehouseMgr.Entities.Dto;
@@ -224,16 +225,16 @@ namespace Tnb.WarehouseMgr
/// 生成任务执行
///
///
- [HttpPost]
+ [HttpPost, Timed(Name = nameof(IWareHouseService.GenTaskExecute))]
public async Task GenTaskExecute(CancellationTokenSource? cts = default)
{
Stopwatch sw = Stopwatch.StartNew();
CancellationTokenSource agvCts = new();
//获取用户登录令牌
- var aToken = await _cacheManager.GetAsync("AsscessToken");
- if (aToken.IsNullOrWhiteSpace()) return;
- var curUser = await GetUserIdentity(aToken);
+ //var aToken = await _cacheManager.GetAsync("AsscessToken");
+ //if (aToken.IsNullOrWhiteSpace()) return;
+ //var curUser = await GetUserIdentity(aToken);
var db = _db.CopyNew();
try
@@ -241,7 +242,7 @@ namespace Tnb.WarehouseMgr
//获取所有未下发的预任务申请
var preTasks = await db.Queryable().InnerJoin((a, b) => a.startlocation_id == b.location_id && a.carry_id == b.id)
.InnerJoin((a, b, c) => a.area_id == c.id)
- .Where(a => a.status == WmsWareHouseConst.PRETASK_BILL_STATUS_DXF_ID)
+ .Where(a => a.status == WmsWareHouseConst.PRETASK_BILL_STATUS_DXF_ID && !string.IsNullOrWhiteSpace(a.startlocation_id))
.OrderBy(a => new { priority = SqlFunc.Desc(a.priority), a.bill_code })
.Select((a, b, c) => new WmsPretaskH
{
@@ -381,18 +382,9 @@ namespace Tnb.WarehouseMgr
catch (Exception ex)
{
Log.Error("生成预任务执行时出现错误", ex);
- var opts = curUser.FindFirst(ClaimConst.CONNECTIONCONFIG)?.Value;
-
- TimedTaskErrorInfo ei = new()
- {
- RequestURL = App.HttpContext?.Request?.Path,
- RequestMethod = App.HttpContext?.Request?.Method,
- userIdentity = curUser,
- };
- var timedTaskEx = ex.ToTimedTaskException(ei);
await db.Ado.RollbackTranAsync();
cts?.Cancel();
- throw timedTaskEx;
+ throw;
}
finally
{
@@ -533,11 +525,15 @@ namespace Tnb.WarehouseMgr
locIts.Add(loc);
}
+
await _db.Updateable(carryIts).UpdateColumns(it => new { it.is_lock, it.location_id, it.location_code }).ExecuteCommandAsync();
+
//更新条码的库位和仓库信息
- await _db.Updateable(carryCodeIts).UpdateColumns(it => new { it.warehouse_id, it.location_id, it.location_code }).Where(it => multiList.Select(x => x.carry_id).Contains(it.carry_id)).ExecuteCommandAsync();
+ var carryIdItor = multiList.Select(x => x.carry_id);
+ await _db.Updateable(carryCodeIts).UpdateColumns(it => new { it.warehouse_id, it.location_id, it.location_code }).Where(it => it.carry_id.In(carryIdItor)).ExecuteCommandAsync();
//更新库位信息,使用状态为 使用,锁定状态为未锁定
- await _db.Updateable(locIts).UpdateColumns(it => new { it.is_use, it.is_lock }).Where(it => multiList.Select(x => x.endlocation_id).Contains(it.id)).ExecuteCommandAsync();
+ var locIdItor = multiList.Select(x => x.endlocation_id);
+ await _db.Updateable(locIts).UpdateColumns(it => new { it.is_use, it.is_lock }).Where(it => it.id.In(locIdItor)).ExecuteCommandAsync();
//更新业务主表的单据状态
foreach (var dt in disTasks)
{
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/WmsEmptyInstockService.cs b/WarehouseMgr/Tnb.WarehouseMgr/WmsEmptyInstockService.cs
index 01d00f36..f9b5abd4 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr/WmsEmptyInstockService.cs
+++ b/WarehouseMgr/Tnb.WarehouseMgr/WmsEmptyInstockService.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Threading.Channels;
using System.Threading.Tasks;
using JNPF.Common.Contracts;
using JNPF.Common.Core.Manager;
@@ -24,6 +25,7 @@ using Tnb.WarehouseMgr.Entities.Attributes;
using Tnb.WarehouseMgr.Entities.Consts;
using Tnb.WarehouseMgr.Entities.Dto;
using Tnb.WarehouseMgr.Entities.Dto.Inputs;
+using Tnb.WarehouseMgr.Filters;
using Tnb.WarehouseMgr.Interfaces;
namespace Tnb.WarehouseMgr
@@ -44,13 +46,16 @@ namespace Tnb.WarehouseMgr
private readonly IWareHouseService _wareHouseService;
private readonly IBillRullService _billRullService;
private readonly IUserManager _userManager;
+ private readonly ChannelWriter _channelWriter;
public WmsEmptyInstockService(
ISqlSugarRepository repository,
IRunService runService,
IVisualDevService visualDevService,
IWareHouseService wareHouseService,
IUserManager userManager,
- IBillRullService billRullService)
+ IBillRullService billRullService,
+ ITaskMessageNotify taskMesageNotify
+ )
{
_db = repository.AsSugarClient();
_runService = runService;
@@ -58,9 +63,9 @@ namespace Tnb.WarehouseMgr
_wareHouseService = wareHouseService;
_userManager = userManager;
_billRullService = billRullService;
+ _channelWriter = taskMesageNotify.Writer;
OverideFuncs.CreateAsync = WmsEmptyIn;
}
-
private async Task WmsEmptyIn(VisualDevModelDataCrInput input)
{
@@ -159,6 +164,7 @@ namespace Tnb.WarehouseMgr
await _wareHouseService.GenInStockTaskHandleAfter(preTaskUpInput,
it => new WmsCarryH { is_lock = 1, location_id = preTaskUpInput.CarryStartLocationId, location_code = preTaskUpInput.CarryStartLocationCode },
it => new BasLocation { is_lock = 1 });
+
}
else
{
@@ -179,6 +185,12 @@ namespace Tnb.WarehouseMgr
await _db.Ado.RollbackTranAsync();
throw;
}
+ finally
+ {
+ //向队列写入消息
+ NotifyMessage message = new() { TaskName = nameof(IWareHouseService.GenTaskExecute) };
+ await _channelWriter.WriteAsync(message);
+ }
return Task.FromResult(true);
}
///
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/WmsSetSortingService.cs b/WarehouseMgr/Tnb.WarehouseMgr/WmsSetSortingService.cs
index b63baf28..e519dfa0 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr/WmsSetSortingService.cs
+++ b/WarehouseMgr/Tnb.WarehouseMgr/WmsSetSortingService.cs
@@ -58,7 +58,7 @@ namespace Tnb.WarehouseMgr
/// 齐套分拣(新增状态)
///
///
- [HttpPost]
+ [HttpPost, Timed(Name = nameof(PackSortingByAdd))]
public async Task PackSortingByAdd(CancellationTokenSource? cts = default)
{
var aToken = await _cacheManager.GetAsync("AccessToken");
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/WmskittingOutService.cs b/WarehouseMgr/Tnb.WarehouseMgr/WmskittingOutService.cs
index 2e8fd185..7b603c0f 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr/WmskittingOutService.cs
+++ b/WarehouseMgr/Tnb.WarehouseMgr/WmskittingOutService.cs
@@ -75,7 +75,7 @@ namespace Tnb.WarehouseMgr
/// 齐套出库(新增状态)
///
///
- [HttpPost]
+ [HttpPost, Timed(Name = nameof(KittingOutByAdd))]
public async Task KittingOutByAdd(CancellationTokenSource? cts = default)
{
var aToken = await _cacheManager.GetAsync("AsscessToken");
@@ -163,7 +163,7 @@ namespace Tnb.WarehouseMgr
}
catch (Exception ex)
{
- JNPF.Logging.Log.Error("齐套出库,新增时出现错误", ex);
+ Log.Error("齐套出库,新增时出现错误", ex);
await curDb.Ado.RollbackTranAsync();
TimedTaskErrorInfo ei = new()
{
@@ -175,12 +175,16 @@ namespace Tnb.WarehouseMgr
cts?.Cancel();
throw timedTaskEx;
}
+ finally
+ {
+
+ }
}
///
/// 齐套出库,(待配送状态)
///
///
- [HttpPost]
+ [HttpPost, Timed(Name = nameof(KittingOutByIsToBeShipped))]
public async Task KittingOutByIsToBeShipped(CancellationTokenSource? cts = default)
{
var aToken = await _cacheManager.GetAsync("AsscessToken");
@@ -272,7 +276,7 @@ namespace Tnb.WarehouseMgr
}
catch (Exception ex)
{
- JNPF.Logging.Log.Error("齐套出库,待配送时出现错误", ex);
+ Log.Error("齐套出库,待配送时出现错误", ex);
TimedTaskErrorInfo ei = new()
{
RequestURL = App.HttpContext?.Request?.Path,
diff --git a/apihost/Tnb.API.Entry/Startup.cs b/apihost/Tnb.API.Entry/Startup.cs
index dd5e6ed7..2d5ed531 100644
--- a/apihost/Tnb.API.Entry/Startup.cs
+++ b/apihost/Tnb.API.Entry/Startup.cs
@@ -62,7 +62,8 @@ public class Startup : AppStartup
.AddSenparcWeixinServices(App.Configuration); // Senparc.Weixin 注册(如果使用Senparc.Weixin SDK则添加)
services.AddOverideVisualDev();
-
+ //注册任务消息通知 added by ly on 20230814
+ services.AddTaskMessageNotify();
//定时任务
services.AddHostedService();
diff --git a/apihost/Tnb.API.Entry/Tnb.API.Entry.csproj b/apihost/Tnb.API.Entry/Tnb.API.Entry.csproj
index 7e3429bd..e3a54fc6 100644
--- a/apihost/Tnb.API.Entry/Tnb.API.Entry.csproj
+++ b/apihost/Tnb.API.Entry/Tnb.API.Entry.csproj
@@ -44,7 +44,6 @@
-
diff --git a/common/Tnb.Common/Extension/EnumerableExtensions.cs b/common/Tnb.Common/Extension/EnumerableExtensions.cs
index 0c7d30ae..a1922799 100644
--- a/common/Tnb.Common/Extension/EnumerableExtensions.cs
+++ b/common/Tnb.Common/Extension/EnumerableExtensions.cs
@@ -70,4 +70,21 @@ public static class EnumerableExtensions
collection = collection as IList ?? collection.ToList();
return !collection.Any();
}
+ ///
+ /// Contains扩展
+ /// added by ly on 20230814
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static bool In(this T obj, IEnumerable collection)
+ {
+ var result = false;
+ foreach (var item in collection)
+ {
+ result |= item.Equals(obj);
+ }
+ return result;
+ }
}
\ No newline at end of file
diff --git a/system/Tnb.OAuth/OAuthService.cs b/system/Tnb.OAuth/OAuthService.cs
index 9c389e8f..a701c81b 100644
--- a/system/Tnb.OAuth/OAuthService.cs
+++ b/system/Tnb.OAuth/OAuthService.cs
@@ -142,7 +142,7 @@ public class OAuthService : IDynamicApiController, ITransient
private readonly IMHandler _imHandler;
-
+
///
/// 初始化一个类型的新实例.
@@ -607,8 +607,7 @@ public class OAuthService : IDynamicApiController, ITransient
}, tokenTimeout);
//modify by ly on 20230804
- UserManager.AsscessToken = accessToken;
- await _cacheManager.SetAsync("AsscessToken", accessToken,TimeSpan.FromMinutes(30));
+ await _cacheManager.SetAsync("AsscessToken", accessToken, TimeSpan.FromMinutes(-1));
// 单点登录标识缓存
if (_oauthOptions.Enabled) _cacheManager.Set("OnlineTicket_" + input.online_ticket, options.ConfigId);
diff --git a/visualdev/Tnb.Vmodel/BaseAppService.cs b/visualdev/Tnb.Vengine/AppService/BaseAppService.cs
similarity index 93%
rename from visualdev/Tnb.Vmodel/BaseAppService.cs
rename to visualdev/Tnb.Vengine/AppService/BaseAppService.cs
index e6fa3d23..f657fff2 100644
--- a/visualdev/Tnb.Vmodel/BaseAppService.cs
+++ b/visualdev/Tnb.Vengine/AppService/BaseAppService.cs
@@ -6,7 +6,7 @@
using JNPF.DependencyInjection;
using JNPF.DynamicApiController;
-namespace Tnb.VmodelEngine;
+namespace Tnb.Vengine.AppService;
///
/// 增删改查基类
diff --git a/visualdev/Tnb.Vmodel/IVmodelAppService.cs b/visualdev/Tnb.Vengine/AppService/IVmodelAppService.cs
similarity index 93%
rename from visualdev/Tnb.Vmodel/IVmodelAppService.cs
rename to visualdev/Tnb.Vengine/AppService/IVmodelAppService.cs
index 746bd7f2..f00093b5 100644
--- a/visualdev/Tnb.Vmodel/IVmodelAppService.cs
+++ b/visualdev/Tnb.Vengine/AppService/IVmodelAppService.cs
@@ -5,7 +5,7 @@
using JNPF.DependencyInjection;
-namespace Tnb.VmodelEngine;
+namespace Tnb.Vengine.AppService;
///
/// 视图模型服务接口
diff --git a/visualdev/Tnb.Vmodel/IVmodelPageAppService.cs b/visualdev/Tnb.Vengine/AppService/IVmodelPageAppService.cs
similarity index 93%
rename from visualdev/Tnb.Vmodel/IVmodelPageAppService.cs
rename to visualdev/Tnb.Vengine/AppService/IVmodelPageAppService.cs
index 4b19ef28..33a0d1ce 100644
--- a/visualdev/Tnb.Vmodel/IVmodelPageAppService.cs
+++ b/visualdev/Tnb.Vengine/AppService/IVmodelPageAppService.cs
@@ -5,7 +5,7 @@
using JNPF.DependencyInjection;
-namespace Tnb.VmodelEngine;
+namespace Tnb.Vengine.AppService;
///
/// 视图模型服务接口
diff --git a/visualdev/Tnb.Vengine/AppService/VmAppService.cs b/visualdev/Tnb.Vengine/AppService/VmAppService.cs
new file mode 100644
index 00000000..1ef08ede
--- /dev/null
+++ b/visualdev/Tnb.Vengine/AppService/VmAppService.cs
@@ -0,0 +1,198 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+using JNPF.Common.Security;
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using Tnb.Vengine.DataAccess;
+using Tnb.Vengine.Domain;
+
+namespace Tnb.Vengine.AppService;
+
+///
+/// 增删改查基类
+///
+[ApiDescriptionSettings(Tag = ModuleConst.Tag, Area = ModuleConst.Area, Order = 10, KeepVerb = true)]
+[Route("api")]
+public class VmAppService : BaseAppService
+{
+ private readonly IDataAccess _dataAccess;
+ private readonly ISqlSugarClient _db;
+
+ ///
+ /// 构造函数
+ ///
+ public VmAppService(IDataAccess da)
+ {
+ _dataAccess = da;
+ _db = _dataAccess.GetSqlSugar();
+ }
+
+ #region 根据vmodel id进行增删改查接口
+ ///
+ /// 获取一条 数据信息
+ ///
+ [HttpGet("[area]/[controller]/{vmid}/[action]")]
+ public async Task GetAsync(string vmid, [FromQuery] VmGetInput input)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(vmid, true);
+ VmQueryInput arg = input.Adapt();
+ if (input.id != null)
+ {
+ if (arg.q == null) arg.q = new DObject();
+ arg.q.Add(vm.GetPrimary().code, input.id);
+ }
+ var ls = await _dataAccess.QueryDataAsync(vm, arg);
+ return ls.items.FirstOrDefault();
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ [HttpGet("[area]/[controller]/{vmid}/[action]")]
+ public async Task GetListAsync(string vmid, [FromQuery] VmGetListInput input)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(vmid, true);
+ VmQueryInput arg = input.Adapt();
+ if (!string.IsNullOrEmpty(input.q))
+ {
+ arg.q = input.q.ToObject();
+ }
+ var ls = await _dataAccess.QueryDataAsync(vm, arg);
+ return ls;
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ [HttpPost("[area]/[controller]/{vmid}/[action]")]
+ public async Task QueryAsync(string vmid, [FromBody] VmQueryInput input)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(vmid, true);
+ var ls = await _dataAccess.QueryDataAsync(vm, input);
+ return ls;
+ }
+
+ ///
+ /// 新增 数据
+ ///
+ [HttpPost("[area]/[controller]/{vmid}/[action]")]
+ public async Task CreateAsync(string vmid, VmCreateInput input)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(vmid);
+ var ret = await _dataAccess.CreateDataAsync(vm, input);
+ return ret;
+ }
+
+ ///
+ /// 更新 数据
+ ///
+ [HttpPut("[area]/[controller]/{vmid}/[action]")]
+ public async Task UpdateAsync(string vmid, VmUpdateInput input)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(vmid);
+ var ret = await _dataAccess.UpdateDataAsync(vm, input);
+ return ret;
+ }
+
+ ///
+ /// 删除 数据
+ ///
+ [HttpDelete("[area]/[controller]/{vmid}/[action]")]
+ public async Task DeleteAsync(string vmid, [FromQuery] VmDeleteInput input)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(vmid);
+ var ret = await _dataAccess.DeleteDataAsync(vm, input);
+ return ret;
+ }
+
+ private async Task GetVmodelAsync(string area, string vmCode)
+ {
+ var vm = await _dataAccess.GetVmodelAsync(area.SnakeToPascalCase(), vmCode.SnakeToPascalCase(), true);
+ return vm;
+ }
+ #endregion
+
+ #region 根据vmode的area和code进行增删改查接口
+ ///
+ /// 获取一条 数据信息
+ ///
+ [HttpGet("{areaCode}/{vmCode}/[action]")]
+ public async Task GetAsync(string areaCode, string vmCode, [FromQuery] VmGetInput input)
+ {
+ var vm = await GetVmodelAsync(areaCode, vmCode);
+ VmQueryInput arg = input.Adapt();
+ if (input.id != null)
+ {
+ if (arg.q == null) arg.q = new DObject();
+ arg.q.Add(vm.GetPrimary().code, input.id);
+ }
+ var ls = await _dataAccess.QueryDataAsync(vm, arg);
+ return ls.items.FirstOrDefault();
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ [HttpGet("{areaCode}/{vmCode}/[action]")]
+ public async Task GetListAsync(string areaCode, string vmCode, [FromQuery] VmGetListInput input)
+ {
+ var vm = await GetVmodelAsync(areaCode, vmCode);
+ VmQueryInput arg = input.Adapt();
+ if (!string.IsNullOrEmpty(input.q))
+ {
+ arg.q = input.q.ToObject();
+ }
+ var ls = await _dataAccess.QueryDataAsync(vm, arg);
+ return ls;
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ [HttpPost("{areaCode}/{vmCode}/[action]")]
+ public async Task QueryAsync(string areaCode, string vmCode, [FromBody] VmQueryInput input)
+ {
+ var vm = await GetVmodelAsync(areaCode, vmCode);
+ var ls = await _dataAccess.QueryDataAsync(vm, input);
+ return ls;
+ }
+
+ ///
+ /// 新增 数据
+ ///
+ [HttpPost("{areaCode}/{vmCode}/[action]")]
+ public async Task CreateAsync(string areaCode, string vmCode, VmCreateInput input)
+ {
+ var vm = await GetVmodelAsync(areaCode, vmCode);
+ var ret = await _dataAccess.CreateDataAsync(vm, input);
+ return ret;
+ }
+
+ ///
+ /// 更新 数据
+ ///
+ [HttpPut("{areaCode}/{vmCode}/[action]")]
+ public async Task UpdateAsync(string areaCode, string vmCode, VmUpdateInput input)
+ {
+ var vm = await GetVmodelAsync(areaCode, vmCode);
+ var ret = await _dataAccess.UpdateDataAsync(vm, input);
+ return ret;
+ }
+
+ ///
+ /// 删除 数据
+ ///
+ [HttpDelete("{areaCode}/{vmCode}/[action]")]
+ public async Task DeleteAsync(string areaCode, string vmCode, [FromQuery] VmDeleteInput input)
+ {
+ var vm = await GetVmodelAsync(areaCode, vmCode);
+ var ret = await _dataAccess.DeleteDataAsync(vm, input);
+ return ret;
+ }
+ #endregion
+
+}
diff --git a/visualdev/Tnb.Vengine/AppService/VmAppServiceT.cs b/visualdev/Tnb.Vengine/AppService/VmAppServiceT.cs
new file mode 100644
index 00000000..4919858d
--- /dev/null
+++ b/visualdev/Tnb.Vengine/AppService/VmAppServiceT.cs
@@ -0,0 +1,120 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+using JNPF.Common.Contracts;
+using JNPF.Common.Security;
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using Tnb.Vengine.DataAccess;
+using Tnb.Vengine.Domain;
+
+namespace Tnb.Vengine.AppService;
+
+///
+/// 增删改查基类
+///
+[ApiDescriptionSettings(Area = ModuleConst.Area, Order = 10, KeepVerb = true)]
+[Route("api/[area]/[controller]/[action]")]
+public class VmAppService : BaseAppService where TEntity : IEntity
+{
+ protected readonly IDataAccess _dataAccess;
+ protected readonly ISqlSugarClient _db;
+
+ ///
+ /// 构造函数
+ ///
+ public VmAppService(IDataAccess dataAccess)
+ {
+ _dataAccess = dataAccess;
+ _db = _dataAccess.GetSqlSugar();
+ }
+
+ protected async Task GetVmodelAsync()
+ {
+ var tp = typeof(TEntity);
+ if (string.IsNullOrEmpty(tp?.Namespace))
+ {
+ throw new ArgumentNullException($"类型 {nameof(tp)} 的命名空间不可为空");
+ }
+ var area = tp.Namespace.Split('.').Last().ToKebabCase();
+ var vm = await _dataAccess.GetVmodelAsync(area, tp.Name, true);
+
+ return vm;
+ }
+
+ ///
+ /// 获取一条 数据信息
+ ///
+ public virtual async Task GetAsync([FromQuery] VmGetInput input)
+ {
+ var vm = await GetVmodelAsync();
+ VmQueryInput arg = input.Adapt();
+ if (input.id != null)
+ {
+ if (arg.q == null) arg.q = new DObject();
+ arg.q.Add(vm.GetPrimary().code, input.id);
+ }
+ var ls = await _dataAccess.QueryDataAsync(vm, arg);
+ return ls.items.FirstOrDefault()!;
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ public virtual async Task GetListAsync([FromQuery] VmGetListInput input)
+ {
+ var vm = await GetVmodelAsync();
+ VmQueryInput arg = input.Adapt();
+ if (!string.IsNullOrEmpty(input.q))
+ {
+ arg.q = input.q.ToObject();
+ }
+ var ls = await _dataAccess.QueryDataAsync(vm, arg);
+ return ls;
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ [HttpPost]
+ public virtual async Task QueryAsync([FromBody] VmQueryInput input)
+ {
+ var vm = await GetVmodelAsync();
+ var ls = await _dataAccess.QueryDataAsync(vm, input);
+ return ls;
+ }
+
+ ///
+ /// 新增 数据
+ ///
+ public virtual async Task CreateAsync(VmCreateInput input)
+ {
+ var vm = await GetVmodelAsync();
+ var ret = await _dataAccess.CreateDataAsync(vm, input);
+ return ret;
+ }
+
+ ///
+ /// 更新 数据
+ ///
+ public virtual async Task UpdateAsync(VmUpdateInput input)
+ {
+ var vm = await GetVmodelAsync();
+ var ret = await _dataAccess.UpdateDataAsync(vm, input);
+ return ret;
+ }
+
+ ///
+ /// 删除 数据
+ ///
+ public virtual async Task DeleteAsync([FromQuery] VmDeleteInput input)
+ {
+ var vm = await GetVmodelAsync();
+ var ret = await _dataAccess.DeleteDataAsync(vm, input);
+ return ret;
+ }
+
+}
diff --git a/visualdev/Tnb.Vengine/AppService/VmodelAppService.cs b/visualdev/Tnb.Vengine/AppService/VmodelAppService.cs
new file mode 100644
index 00000000..d77245c3
--- /dev/null
+++ b/visualdev/Tnb.Vengine/AppService/VmodelAppService.cs
@@ -0,0 +1,151 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
+using Tnb.Vengine.DataAccess;
+using Tnb.Vengine.Domain;
+
+namespace Tnb.Vengine.AppService;
+
+///
+/// 视图模型服务类
+///
+public class VmodelAppService : VmAppService, IVmodelAppService
+{
+
+ ///
+ /// 构造函数
+ ///
+ public VmodelAppService(IDataAccess da) : base(da)
+ {
+ }
+
+ ///
+ /// 获取一条 数据信息
+ ///
+ public override async Task GetAsync(VmGetInput input)
+ {
+ //return await _dataAccess.GetVmodelAsync(input.id);
+ var query = _db.Queryable().Where(a => a.deleted == 0);
+ Vmodel vm;
+ if (long.TryParse(input.id, out long id))
+ {
+ vm = await query.FirstAsync(a => a.id == input.id);
+ }
+ else
+ {
+ vm = await query.FirstAsync(a => a.vmCode == input.id);
+ }
+ return vm;
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ public override async Task GetListAsync(VmGetListInput input)
+ {
+ VmPagedOutput ret = new();
+ var q = _db.Queryable().WhereIF(!string.IsNullOrEmpty(input.k), a => a.vmCode.Contains(input.k!) || a.vmName.Contains(input.k!));
+ RefAsync total = 0;
+ var data = await q.OrderBy(input.sort).ToPageListAsync((input.pnum - 1) * input.psize, input.psize, total);
+ ret.total = total;
+ ret.items = data.ConvertAll(a => a);
+ return ret;
+ }
+
+ [NonAction]
+ public override Task QueryAsync(VmQueryInput input)
+ {
+ return base.QueryAsync(input);
+ }
+
+ ///
+ /// 新增 模型
+ ///
+ public override async Task CreateAsync(VmCreateInput input)
+ {
+ //ThrowIf.IsNull(input.data, nameof(input));
+ ArgumentNullException.ThrowIfNull(input.data);
+ Vmodel vm = input.data.Adapt();
+ await _db.Insertable(vm).ExecuteCommandAsync();
+ return input;
+ }
+
+ ///
+ /// 更新 数据
+ ///
+ public override async Task UpdateAsync(VmUpdateInput input)
+ {
+ ArgumentNullException.ThrowIfNull(input.data);
+ Vmodel vm = input.data.Adapt();
+ await _db.Updateable(vm).WhereColumns(a => a.id).ExecuteCommandAsync();
+ return input;
+ }
+
+ ///
+ /// 删除 数据
+ ///
+ public override async Task DeleteAsync(VmDeleteInput input)
+ {
+ var ret = await _db.Deleteable(input.id).ExecuteCommandAsync();
+ return ret;
+ }
+
+ ///
+ /// 从数据表创建模型
+ ///
+ public async Task> CreateFromTable(VmodelCreateFromTableInput input)
+ {
+ ThrowIf.IsNull(input.tableName, nameof(input.tableName));
+ var sugar = _dataAccess.GetSqlSugar(input.dbCode);
+ var lsTable = sugar.DbMaintenance.GetTableInfoList().WhereIF(input.tableName != "ALL", a => a.Name == input.tableName);
+
+ List lsToAdd = new List();
+ List lsToUpdate = new List();
+ foreach (var tb in lsTable)
+ {
+ if (!string.IsNullOrEmpty(input.removePrefix) && !tb.Name.StartsWith(input.removePrefix)) continue;
+ var colInfo = sugar.DbMaintenance.GetColumnInfosByTableName(tb.Name);
+ Vmodel model = new() { dbCode = input.dbCode, vmName = tb.Description, tableName = tb.Name };
+ model.area = input.area;
+ model.vmCode = (string.IsNullOrEmpty(input.removePrefix) ? tb.Name : tb.Name.RemovePreFix(input.removePrefix)).SnakeToPascalCase();
+ //model.createId = CurrentUser.Id;
+ int n = 1;
+ foreach (var p in colInfo)
+ {
+ var prop = p.Adapt();
+ prop.ordinal = n++;
+ prop.csType = sugar.Ado.DbBind.GetPropertyTypeName(p.DataType);
+ model.dbProps.Add(prop);
+ }
+ var exist = await _db.Queryable().FirstAsync(a => a.dbCode == input.dbCode && a.tableName == tb.Name);
+ if (exist == null)
+ {
+ lsToAdd.Add(model);
+ }
+ else
+ {
+ exist.area = model.area;
+ model.dbProps.Adapt(exist.dbProps);
+ //exist.dbProps.Clear();
+ //exist.dbProps.AddRange(model.dbProps.OrderBy(a => a.ordinal));
+ lsToUpdate.Add(exist);
+ }
+ }
+ if (lsToAdd.Count > 0)
+ {
+ await _db.Insertable(lsToAdd).ExecuteCommandAsync();
+ }
+ if (lsToUpdate.Count > 0)
+ {
+ await _db.Updateable(lsToUpdate).ExecuteCommandAsync();
+ }
+ return lsToAdd.Union(lsToUpdate).ToList();
+ }
+
+
+}
diff --git a/visualdev/Tnb.Vengine/AppService/VmodelPageAppService.cs b/visualdev/Tnb.Vengine/AppService/VmodelPageAppService.cs
new file mode 100644
index 00000000..553960f8
--- /dev/null
+++ b/visualdev/Tnb.Vengine/AppService/VmodelPageAppService.cs
@@ -0,0 +1,165 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+using System.Text;
+using JNPF.Common.Security;
+using Mapster;
+using Newtonsoft.Json.Linq;
+using SqlSugar;
+using Tnb.Vengine.DataAccess;
+using Tnb.Vengine.Domain;
+
+namespace Tnb.Vengine.AppService;
+
+///
+/// 视图模型服务类
+///
+public class VmodelPageAppService : VmAppService, IVmodelPageAppService
+{
+ ///
+ /// 构造函数
+ ///
+ public VmodelPageAppService(IDataAccess da) : base(da)
+ {
+ }
+
+ ///
+ /// 获取一条 数据信息
+ ///
+ public override async Task GetAsync(VmGetInput input)
+ {
+ var query = _db.Queryable().Where(a => a.deleted == 0);
+ VmodelPage vm = await query.FirstAsync(a => a.id == input.id);
+ return vm;
+ }
+
+ ///
+ /// 获取多条 数据列表
+ ///
+ public override async Task GetListAsync(VmGetListInput input)
+ {
+ VmPagedOutput ret = new();
+ var q = _db.Queryable().WhereIF(!string.IsNullOrEmpty(input.k), a => a.code.Contains(input.k!) || a.name.Contains(input.k!));
+ RefAsync total = 0;
+ var data = await q.OrderBy(input.sort).ToPageListAsync((input.pnum - 1) * input.psize, input.psize, total);
+ ret.total = total;
+ ret.items = data.ConvertAll(a => a);
+ return ret;
+ }
+
+ ///
+ /// 新增 模型
+ ///
+ public override async Task CreateAsync(VmCreateInput input)
+ {
+ ArgumentNullException.ThrowIfNull(input.data);
+ VmodelPage vpage = input.data.Adapt();
+ await _db.Insertable(vpage).ExecuteCommandAsync();
+ return vpage;
+ }
+
+ ///
+ /// 更新 数据
+ ///
+ public override async Task UpdateAsync(VmUpdateInput input)
+ {
+ ArgumentNullException.ThrowIfNull(input.data);
+ if (!input.data.ContainsKey(nameof(VmodelPage.id)))
+ {
+ throw new Exception($"更新数据时主键({nameof(VmodelPage.id)})不可为空");
+ }
+ var id = input.data[nameof(VmodelPage.id)].ToString();
+ var model = await _db.Queryable().FirstAsync(a => a.id == id);
+ ArgumentNullException.ThrowIfNull(model);
+ input.data.Adapt(model, TypeAdapter.IgnoreNull);
+ await _db.Updateable(model).WhereColumns(a => a.id).ExecuteCommandAsync();
+ return model;
+ }
+
+ ///
+ /// 删除 数据
+ ///
+ public override async Task DeleteAsync(VmDeleteInput input)
+ {
+ var ret = await _db.Deleteable(input.id).ExecuteCommandAsync();
+ return ret;
+ }
+
+ ///
+ /// 从数据表创建模型
+ ///
+ public async Task CreateByVmodel(CreatePageFromVmodelInput input)
+ {
+ ArgumentNullException.ThrowIfNull(input.vmid);
+ var vm = await _dataAccess.GetVmodelAsync(input.vmid);
+ ArgumentNullException.ThrowIfNull(vm);
+
+ var page = await _db.Queryable().FirstAsync(a => a.vmid == vm.id);
+ if (page == null)
+ {
+ page = new VmodelPage { vmid = vm.id, code = vm.vmCode, name = vm.vmName };
+ page.pageSchema = CreatePageSchema(vm, page.id);
+ await _db.Insertable(page).ExecuteCommandAsync();
+ }
+ else
+ {
+ page.pageSchema = CreatePageSchema(vm, page.id);
+ await _db.Updateable(page).ExecuteCommandAsync();
+ }
+ return page;
+ }
+
+ private JObject CreatePageSchema(Vmodel vm, string pageid)
+ {
+ StringBuilder str = new StringBuilder();
+ str.AppendLine("{");
+ str.AppendLine($"\"page\": {{ \"loadList\": true, \"watchClient\": false }},");
+ str.AppendLine($"\"queryData\": {{ }},");
+ str.AppendLine($"\"queryForm\": {{");
+ str.AppendLine($"\"show\": false,");
+ str.AppendLine($"\"attr\": {{ \"labelWidth\": \"106px\", \"hasKeyword\":false }},");
+ str.AppendLine($"\"cols\": {{");
+ var pQuery = vm.dbProps.Skip(1).Take(1).FirstOrDefault();
+ if (pQuery != null)
+ {
+ str.AppendLine($"\"{pQuery.code}\": {{ \"label\": \"{pQuery.name}\", \"span\": 8, \"qtype\": 2, \"isQuick\": true, \"comp\": {{ \"type\": \"el-input\", \"attr\": {{ \"placeholder\": \"{pQuery.name}\", \"clearable\": true, \"maxlength\": 20 }} }} }}");
+ }
+ str.AppendLine($"}}");
+ str.AppendLine($"}},");
+ str.AppendLine($"\"list\": {{");
+ str.AppendLine($"\"opt\": {{ \"isPage\": true, \"isCheck\": true, \"sortBy\": \"\", \"pkey\": \"{vm.GetPrimary().code}\" }},");
+ str.AppendLine($"\"attr\": {{ \"border\": false }},");
+ str.AppendLine($"\"cols\": {{");
+ foreach (var p in vm.dbProps)
+ {
+ str.AppendLine($"\"{p.code}\":{{ \"label\": \"{p.name}\", \"show\": true, \"attr\": {{ {p.GetDefaultWidth()} }}, \"comp\": {{}} }},");
+ }
+ str.AppendLine($"}}");
+ str.AppendLine($"}},");
+ str.AppendLine($"\"editData\": {vm.GetDefaultDObject().ToJsonString()},");
+ str.AppendLine($"\"editDlg\": {{ \"isAdd\": true, \"tabHeight\": 300, \"name\": \"{vm.vmName}\" }},");
+ str.AppendLine($"\"editForm\": {{");
+ str.AppendLine($"\"attr\": {{ \"labelWidth\": \"106px\" }},");
+ str.AppendLine($"\"rules\": {{");
+ foreach (var p in vm.dbProps.Where(a => a.required && !a.pkey))
+ {
+ str.AppendLine($"\"{p.code}\": [{{ \"required\": true, \"message\": \"必填项不能为空\", \"trigger\": \"blur\" }}],");
+ }
+ str.AppendLine($"}},");
+ str.AppendLine($"\"cols\": {{");
+ foreach (var p in vm.dbProps)
+ {
+ str.AppendLine($"\"{p.code}\": {{ \"label\": \"{p.name}\", \"show\": true, \"comp\": {p.GetDefaultComp().ToJsonString()} }},");
+ }
+ str.AppendLine($"}}");
+ str.AppendLine($"}},");
+ str.AppendLine($"\"tree\": {{ \"key\": \"id\", \"height\": 300, \"props\": {{ \"label\": \"enumName\" }}, \"data\": [] }}");
+ str.AppendLine($"}}");
+ var s = str.ToString();
+ Console.WriteLine(s);
+ return JObject.Parse(s);
+ }
+
+}
diff --git a/visualdev/Tnb.Vmodel/Constants/DbConsts.cs b/visualdev/Tnb.Vengine/Constants/DbConsts.cs
similarity index 99%
rename from visualdev/Tnb.Vmodel/Constants/DbConsts.cs
rename to visualdev/Tnb.Vengine/Constants/DbConsts.cs
index 162f3f70..bed8b683 100644
--- a/visualdev/Tnb.Vmodel/Constants/DbConsts.cs
+++ b/visualdev/Tnb.Vengine/Constants/DbConsts.cs
@@ -3,7 +3,7 @@
// https://git.tuotong-tech.com/tnb/tnb.server //
/////////////////////////////////////////////////////////////////////////////////
-namespace Tnb.VmodelEngine;
+namespace Tnb.Vengine;
public static class DbConsts
{
diff --git a/visualdev/Tnb.Vmodel/Constants/ModuleConst.cs b/visualdev/Tnb.Vengine/Constants/ModuleConst.cs
similarity index 77%
rename from visualdev/Tnb.Vmodel/Constants/ModuleConst.cs
rename to visualdev/Tnb.Vengine/Constants/ModuleConst.cs
index 1e99e591..52c2abbe 100644
--- a/visualdev/Tnb.Vmodel/Constants/ModuleConst.cs
+++ b/visualdev/Tnb.Vengine/Constants/ModuleConst.cs
@@ -1,4 +1,4 @@
-namespace Tnb.VmodelEngine;
+namespace Tnb.Vengine;
public class ModuleConst
{
diff --git a/visualdev/Tnb.Vmodel/Constants/VmodelEnum.cs b/visualdev/Tnb.Vengine/Constants/VmodelEnum.cs
similarity index 98%
rename from visualdev/Tnb.Vmodel/Constants/VmodelEnum.cs
rename to visualdev/Tnb.Vengine/Constants/VmodelEnum.cs
index f4e4fe1b..c328ad33 100644
--- a/visualdev/Tnb.Vmodel/Constants/VmodelEnum.cs
+++ b/visualdev/Tnb.Vengine/Constants/VmodelEnum.cs
@@ -5,7 +5,7 @@
using System.ComponentModel;
-namespace Tnb.VmodelEngine;
+namespace Tnb.Vengine;
public enum eCsType
{
diff --git a/visualdev/Tnb.Vengine/DataAccess/DataAccess.cs b/visualdev/Tnb.Vengine/DataAccess/DataAccess.cs
new file mode 100644
index 00000000..6d287556
--- /dev/null
+++ b/visualdev/Tnb.Vengine/DataAccess/DataAccess.cs
@@ -0,0 +1,422 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+using System.Collections.Concurrent;
+using JNPF;
+using JNPF.DependencyInjection;
+using Mapster;
+using SqlSugar;
+using Tnb.Vengine.Domain;
+
+namespace Tnb.Vengine.DataAccess;
+
+///
+///
+///
+public class DataAccess : IDataAccess, ITransient, IDisposable
+{
+ const int MAX_PAGE_SIZE = 1000;
+ private ISqlSugarClient? sugar;
+ protected ISqlSugarClient Db
+ {
+ get
+ {
+ if (sugar == null)
+ {
+ ConnectionStringsOptions conn = App.GetConfig("ConnectionStrings", true);
+ //var DBType = (DbType)Enum.Parse(typeof(DbType), conn.DBType);
+ sugar = new SqlSugarScope(new ConnectionConfig
+ {
+ ConnectionString = conn.ConnectString,
+ DbType = conn.DBType.Adapt(),
+ IsAutoCloseConnection = true,
+ ConfigId = conn.ConfigId,
+ InitKeyType = InitKeyType.Attribute,
+ MoreSettings = new ConnMoreSettings()
+ {
+ IsAutoRemoveDataCache = true, // 自动清理缓存
+ IsAutoToUpper = false,
+ PgSqlIsAutoToLower = false,
+ DisableNvarchar = true
+ },
+ }, SugarHelper.ConfigSugar);
+ }
+ return sugar;
+ }
+ }
+
+ ///
+ /// 全局缓存
+ ///
+ static ConcurrentDictionary DbCache = new ConcurrentDictionary();
+
+ ///
+ /// 构造
+ ///
+ public DataAccess()
+ {
+ }
+
+ ///
+ /// 释放
+ ///
+ public void Dispose()
+ {
+ foreach (var item in DbCache)
+ {
+ item.Value.Dispose();
+ }
+ DbCache.Clear();
+ }
+
+ ///
+ /// 获取 ISqlSugarClient
+ ///
+ public ISqlSugarClient GetSqlSugar(string? dbCode = null)
+ {
+ if (string.IsNullOrEmpty(dbCode) || dbCode == DbConsts.DefaultDbCode)
+ {
+ return Db;
+ }
+ if (DbCache.ContainsKey(dbCode))
+ {
+ return DbCache[dbCode];
+ }
+
+ var dblink = GetVmLink(dbCode);
+ if (dblink == null)
+ {
+ throw new Exception($"没有此数据库{dbCode}连接信息");
+ }
+ var dbType = Enum.Parse(dblink.dbType.ToString(), true);
+ var sugar = new SqlSugarScope(new ConnectionConfig
+ {
+ ConnectionString = dblink.dbConnection,
+ DbType = dbType,
+ IsAutoCloseConnection = true,
+ ConfigId = dblink.dbCode,
+ InitKeyType = InitKeyType.Attribute,
+ MoreSettings = new ConnMoreSettings()
+ {
+ IsAutoRemoveDataCache = true, // 自动清理缓存
+ IsAutoToUpper = false,
+ PgSqlIsAutoToLower = false,
+ DisableNvarchar = true
+ },
+ }, SugarHelper.ConfigSugar);
+ if (sugar.Ado.IsValidConnection())
+ {
+ DbCache[dbCode] = sugar;
+ }
+ else
+ {
+ sugar.Dispose();
+ throw new Exception($"无法连接到数据库{dbCode}");
+ }
+ return DbCache[dbCode];
+ }
+
+ ///
+ /// 获取 DbLink
+ ///
+ public VmodelLink GetVmLink(string dbCode)
+ {
+ var model = Db.Queryable().First(a => a.dbCode == dbCode);
+ return model;
+ }
+
+ ///
+ /// 获取 Vmodel, 为空时不抛异常
+ ///
+ public async Task TryGetVmodelAsync(string id, bool loadNavigate = false)
+ {
+ Vmodel vm = await Db.Queryable().FirstAsync(a => a.id == id && a.deleted == 0);
+ if (vm != null && loadNavigate)
+ {
+ await LoadVmodelNavigateAsync(vm);
+ }
+ return vm;
+ }
+
+ ///
+ /// 获取 Vmodel, 为空时抛异常
+ ///
+ public async Task GetVmodelAsync(string id, bool loadNavigate = false)
+ {
+ Vmodel vm = await Db.Queryable().FirstAsync(a => a.id == id && a.deleted == 0);
+ ArgumentNullException.ThrowIfNull(vm, $"找不到vmid={id}的模型");
+ if (loadNavigate)
+ {
+ await LoadVmodelNavigateAsync(vm);
+ }
+ return vm;
+ }
+
+ ///
+ /// 获取 Vmodel, 为空时不抛异常
+ ///
+ public async Task TryGetVmodelAsync(string area, string vmCode, bool loadNavigate = false)
+ {
+ Vmodel vm = await Db.Queryable().FirstAsync(a => a.area == area && a.vmCode == vmCode && a.deleted == 0);
+ if (vm != null && loadNavigate)
+ {
+ await LoadVmodelNavigateAsync(vm);
+ }
+
+ return vm;
+ }
+
+ ///
+ /// 获取 Vmodel, 为空时抛异常
+ ///
+ public async Task GetVmodelAsync(string area, string vmCode, bool loadNavigate = false)
+ {
+ Vmodel vm = await Db.Queryable().FirstAsync(a => a.area == area && a.vmCode == vmCode && a.deleted == 0);
+ ArgumentNullException.ThrowIfNull(vm, $"找不到area={area}, vmCode={vmCode}的模型");
+ if (loadNavigate)
+ {
+ await LoadVmodelNavigateAsync(vm);
+ }
+
+ return vm;
+ }
+
+ /////
+ ///// 获取 Vmodel
+ /////
+ //public async Task GetVmodelAsync(string tableName, string? dbCode)
+ //{
+ // Vmodel vm = await _db.Queryable().FirstAsync(a => a.tableName == tableName && a.dbCode == dbCode && a.deleted == 0);
+ // return vm;
+ //}
+
+ ///
+ /// 加载模型的导航属性
+ ///
+ ///
+ ///
+ private async Task LoadVmodelNavigateAsync(Vmodel vm)
+ {
+ Dictionary dictVm = new();
+ foreach (var navProp in vm.navProps)
+ {
+ if (!dictVm.ContainsKey(navProp.vmid))
+ {
+ var navModel = await GetVmodelAsync(navProp.vmid);
+ dictVm.Add(navProp.vmid, navModel);
+ }
+ navProp.naviModel = dictVm[navProp.vmid];
+ }
+ }
+
+ ///
+ /// 查询数据 默认方法
+ ///
+ public async Task QueryDataAsync(Vmodel vm, VmQueryInput input)
+ {
+ ISqlSugarClient db = GetSqlSugar(vm.dbCode);
+ var query = db.Queryable