diff --git a/Tnb.Server.sln b/Tnb.Server.sln
index f7d31d69..8b41da8c 100644
--- a/Tnb.Server.sln
+++ b/Tnb.Server.sln
@@ -140,6 +140,8 @@ 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}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -338,6 +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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -390,6 +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}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {646DDD1C-F143-42C2-894F-F5C7B3A0CE74}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr.Entities/Dto/Inputs/TaskCompleUpInput.cs b/WarehouseMgr/Tnb.WarehouseMgr.Entities/Dto/Inputs/TaskCompleUpInput.cs
index 8e1a68b1..b312aaeb 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr.Entities/Dto/Inputs/TaskCompleUpInput.cs
+++ b/WarehouseMgr/Tnb.WarehouseMgr.Entities/Dto/Inputs/TaskCompleUpInput.cs
@@ -11,15 +11,9 @@ namespace Tnb.WarehouseMgr.Entities.Dto.Inputs
///
public class TaskCompleUpInput
{
- ///
- /// 区分pda与pc端调用,默认pc端,忽略大小写
- ///
- public string prefix { get; set; }
///
/// 任务执行Ids
///
public List disTaskIds { get; set; }
-
-
}
}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr.Entities/Dto/Inputs/TimedtaskInput.cs b/WarehouseMgr/Tnb.WarehouseMgr.Entities/Dto/Inputs/TimedtaskInput.cs
new file mode 100644
index 00000000..398d08b8
--- /dev/null
+++ b/WarehouseMgr/Tnb.WarehouseMgr.Entities/Dto/Inputs/TimedtaskInput.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tnb.WarehouseMgr.Entities.Dto.Inputs
+{
+ public class TimedtaskInput
+ {
+ ///
+ /// 任务Ids
+ ///
+ public List disTaskIds { get; set; }
+ ///
+ /// 设备Ids
+ ///
+ public List EqpIds { get; set; }
+
+ }
+}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/IWareHouseService.cs b/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/IWareHouseService.cs
index 93adbc1c..ed3c0953 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/IWareHouseService.cs
+++ b/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/IWareHouseService.cs
@@ -41,13 +41,18 @@ namespace Tnb.WarehouseMgr.Interfaces
///
///
///
- Task GenPreTask(List preTasks,List preTaskCodes);
+ Task GenPreTask(List preTasks, List preTaskCodes);
///
/// 生成预任务成功后操作
///
///
///
- Task GenInStockTaskHandleAfter(GenPreTaskUpInput input,Expression> setCarryColumnsExp,Expression> setLocaionColumbExp);
+ Task GenInStockTaskHandleAfter(GenPreTaskUpInput input, Expression> setCarryColumnsExp, Expression> setLocaionColumbExp);
+ ///
+ /// 生成任务执行
+ ///
+ ///
+ Task GenTaskExecute(CancellationTokenSource? cts = default);
///
/// 任务完成
///
@@ -66,6 +71,6 @@ namespace Tnb.WarehouseMgr.Interfaces
/// ///
///
Task TaskExecuteAfter(TaskExecuteAfterUpInput input);
-
+
}
}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/IWmsSetSortingService.cs b/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/IWmsSetSortingService.cs
new file mode 100644
index 00000000..73418490
--- /dev/null
+++ b/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/IWmsSetSortingService.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tnb.WarehouseMgr.Interfaces
+{
+ ///
+ /// 齐套分拣业务接口
+ ///
+ public interface IWmsSetSortingService
+ {
+ Task PackSortingByAdd(CancellationTokenSource? cts = default);
+ }
+}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/IWmskittingOutService.cs b/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/IWmskittingOutService.cs
index 9267f90b..25f052e5 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/IWmskittingOutService.cs
+++ b/WarehouseMgr/Tnb.WarehouseMgr.Interfaces/IWmskittingOutService.cs
@@ -18,5 +18,16 @@ namespace Tnb.WarehouseMgr.Interfaces
///
///
Task MESKittingOutStk(List input);
+ ///
+ /// 齐套出库(新增状态)
+ ///
+ ///
+ Task KittingOutByAdd(CancellationTokenSource? cts = default);
+ ///
+ /// 齐套出库,(待配送状态)
+ ///
+ ///
+ ///
+ Task KittingOutByIsToBeShipped(CancellationTokenSource? cts = default);
}
}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/DeviceProviderService.cs b/WarehouseMgr/Tnb.WarehouseMgr/DeviceProviderService.cs
index f3bc23f2..61588ff9 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr/DeviceProviderService.cs
+++ b/WarehouseMgr/Tnb.WarehouseMgr/DeviceProviderService.cs
@@ -30,6 +30,7 @@ namespace Tnb.WarehouseMgr
{
private readonly ISqlSugarClient _db;
private readonly IWareHouseService _wareHouseService;
+
public DeviceProviderService(ISqlSugarRepository repository, IWareHouseService wareHouseService)
{
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/TimedTaskBackgroundService.cs b/WarehouseMgr/Tnb.WarehouseMgr/TimedTaskBackgroundService.cs
new file mode 100644
index 00000000..50d0298e
--- /dev/null
+++ b/WarehouseMgr/Tnb.WarehouseMgr/TimedTaskBackgroundService.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using JNPF;
+using JNPF.Common.Core.Manager;
+using JNPF.Common.Dtos.Message;
+using JNPF.Common.Extension;
+using JNPF.FriendlyException;
+using JNPF.Logging;
+using JNPF.Message.Interfaces.Message;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.Extensions.Hosting;
+using Tnb.Common.Extension;
+using Tnb.WarehouseMgr.Entities.Dto.Inputs;
+using Tnb.WarehouseMgr.Interfaces;
+
+namespace Tnb.WarehouseMgr
+{
+ ///
+ /// 定时任务
+ /// added by ly on 20230802
+ ///
+ public class TimedTaskBackgroundService : BackgroundService
+ {
+ private ISendMessageService? _sendService;
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ await Task.Run(async () =>
+ {
+ _sendService = App.GetRequiredService();
+ var userManager = App.GetRequiredService();
+ List toUserIds = new List() { "25398501929509" };
+ //生成任务执行
+ CancellationTokenSource genTaskCTS = new();
+ CancellationTokenSource kittingOutAddCts = new();
+ CancellationTokenSource kittingOutShippedCts = new();
+ CancellationTokenSource setSortingCts = new();
+
+ var wareHouseService = App.GetRequiredService();
+ await TimedTask(cts => wareHouseService.GenTaskExecute(cts), genTaskCTS, toUserIds);
+ //齐套出库
+
+ var kittingOutService = App.GetRequiredService();
+ await TimedTask(cts => kittingOutService.KittingOutByAdd(cts), kittingOutAddCts, toUserIds);
+ await TimedTask(cts => kittingOutService.KittingOutByIsToBeShipped(cts), kittingOutShippedCts, toUserIds);
+ //齐套分拣
+ var setSortingService = App.GetRequiredService();
+ await TimedTask(cts => setSortingService.PackSortingByAdd(cts), setSortingCts, toUserIds);
+ });
+ }
+
+ private Task? TimedTask(Func? withOutParamAction = null, Func? withParemAction = null, TimedtaskInput? parameter = null)
+ {
+ return parameter != null ? withParemAction?.Invoke(parameter) : withOutParamAction?.Invoke();
+ }
+
+ private Task TimedTask(Func action, CancellationTokenSource cts, List? toUserIds = default)
+ {
+ var token = cts.Token;
+ return Task.Run(async () =>
+ {
+ while (!token.IsCancellationRequested)
+ {
+ await action(cts).Catch(ex =>
+ {
+ //MessageSendModel messageSendModel = new();
+ //_sendService?.SendMessage();
+ //messageService.SentMessage(toUserIds!, ex.Message, ex.ToString());
+ });
+ await Task.Delay(1000);
+ }
+ }, token);
+ }
+
+ }
+}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/Tnb.WarehouseMgr.csproj b/WarehouseMgr/Tnb.WarehouseMgr/Tnb.WarehouseMgr.csproj
index 11bee379..cb4b994d 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr/Tnb.WarehouseMgr.csproj
+++ b/WarehouseMgr/Tnb.WarehouseMgr/Tnb.WarehouseMgr.csproj
@@ -12,6 +12,7 @@
+
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs b/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs
index 1f6da2b4..29484940 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs
+++ b/WarehouseMgr/Tnb.WarehouseMgr/WareHouseService.cs
@@ -29,6 +29,7 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis;
using NPOI.HPSF;
using NPOI.OpenXmlFormats.Wordprocessing;
+using NPOI.SS.Formula.Functions;
using Polly.Timeout;
using Senparc.NeuChar.Helpers;
using Senparc.Weixin.Work.AdvancedAPIs.OaDataOpen;
@@ -187,7 +188,6 @@ namespace Tnb.WarehouseMgr
var whereExprable = Expressionable.Create()
.And((a, b, c) => a.is_lock == 0)
.And((a, b, c) => !string.IsNullOrEmpty(a.location_id))
- //.And((a, b, c) => a.status == (int)EnumCarryStatus.占用)
.And((a, b, c) => c.is_type == ((int)EnumLocationType.存储库位).ToString())
.And((a, b, c) => a.out_status == "0")
.And((a, b, c) => c.wh_id == input.warehouse_id)
@@ -214,11 +214,15 @@ namespace Tnb.WarehouseMgr
///
///
[HttpPost]
- public async Task GenTaskExecute()
+ public async Task GenTaskExecute(CancellationTokenSource? cts = default)
{
+
Stopwatch sw = Stopwatch.StartNew();
- //获取所有未下发的预任务申请
- var preTasks = await _db.Queryable().InnerJoin((a, b) => a.startlocation_id == b.location_id && a.carry_id == b.id)
+ var db = _db.CopyNew();
+ try
+ {
+ //获取所有未下发的预任务申请
+ 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)
.OrderBy(a => new { priority = SqlFunc.Desc(a.priority), a.bill_code })
@@ -227,97 +231,101 @@ namespace Tnb.WarehouseMgr
move_num = c.move_num
}, true)
.ToListAsync();
- var ids = preTasks.Select(x => x.id).Distinct().ToList();
- var preTaskCodes = await _db.Queryable().Where(it => ids.Contains(it.bill_id)).ToListAsync();
- if (preTasks.Count > 0)
- {
- //根据预任务管理区分组,获取到所有分组后的预任务,遍历每个预任务 是否为任务链,通过管理区ID
- var preTaskGroups = preTasks.GroupBy(g => g.area_code).ToList();
- List disTasks = new();
- List distaskCodes = new();
- foreach (var itGroup in preTaskGroups)
+ var ids = preTasks.Select(x => x.id).Distinct().ToList();
+ var preTaskCodes = await db.Queryable().Where(it => ids.Contains(it.bill_id)).ToListAsync();
+ if (preTasks.Count > 0)
{
- var moveNum = itGroup.First().move_num;
- var items = itGroup.Adapt>();
- for (int i = 0, cnt = items.Count; i < cnt; i++)
+ //根据预任务管理区分组,获取到所有分组后的预任务,遍历每个预任务 是否为任务链,通过管理区ID
+ var preTaskGroups = preTasks.GroupBy(g => g.area_code).ToList();
+ List disTasks = new();
+ List distaskCodes = new();
+ foreach (var itGroup in preTaskGroups)
{
- items[i].id = SnowflakeIdHelper.NextId();
- items[i].status = WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID;
- }
- if (moveNum >= 1)
- {
- var areaPreTasks = itGroup.ToList();
- var groupCode = await _billRullService.GetBillNumber(WmsWareHouseConst.WMS_TASK_EXECUTE_ENCODE);
- if (moveNum == 1 || (moveNum > areaPreTasks.Count && areaPreTasks.Count == 1))
+ var items = itGroup.Adapt>();
+ for (int i = 0, cnt = items.Count; i < cnt; i++)
{
- items.ForEach(x =>
- {
- x.is_chain = 0;
- });
- items[0].groups = groupCode;
- items[0].bill_code = $"{groupCode}-1";
+ items[i].id = SnowflakeIdHelper.NextId();
+ items[i].status = WmsWareHouseConst.TASK_BILL_STATUS_DZX_ID;
}
- else if ((moveNum >= areaPreTasks.Count && areaPreTasks.Count > 1) || moveNum < areaPreTasks.Count)
+ var moveNum = itGroup.First().move_num;
+ var itemsCount = items.Count;
+ var mod = itemsCount % moveNum > 0 ? itemsCount / moveNum + 1 : itemsCount / moveNum;
+ var arrary = items.ToArray();
+ for (int i = 1; i <= mod; i++)
{
- items.ForEach(x => x.is_chain = 1);
- var itemsCount = items.Count;
- var start = 0;
- var end = Math.Min(itemsCount, moveNum);
- var arrary = items.ToArray();
- var dic = new Dictionary();
- var mod = itemsCount % moveNum > 0 ? itemsCount / moveNum + 1 : itemsCount / moveNum;
- for (int i = 1; i <= mod; i++)
+ var groupCode = await _billRullService.GetBillNumber(WmsWareHouseConst.WMS_TASK_EXECUTE_ENCODE);
+ if (moveNum >= 1)
{
- while (start < itemsCount)
+ var areaPreTasks = itGroup.ToList();
+
+ if (moveNum == 1 || (moveNum > areaPreTasks.Count && areaPreTasks.Count == 1))
{
- var subArray = arrary[start..end];
- dic[i] = subArray;
- start = end;
- end = Math.Min((end + moveNum), arrary.Length);
+ items.ForEach(x =>
+ {
+ x.is_chain = 0;
+ });
+ items[0].groups = groupCode;
+ items[0].bill_code = $"{groupCode}-1";
}
- }
- foreach (var (i, v) in dic)
- {
- foreach (var it in v)
+ else if ((moveNum >= areaPreTasks.Count && areaPreTasks.Count > 1) || moveNum < areaPreTasks.Count)
{
- it.groups = groupCode;
- it.bill_code = $"{groupCode}-{i}";
+ items.ForEach(x => x.is_chain = 1);
+
+ var start = 0;
+ var end = Math.Min(itemsCount, moveNum);
+ var arrList = new List(mod);
+
+ while (start < itemsCount)
+ {
+ var subArray = arrary[start..end];
+ arrList.Add(subArray);
+ start = end;
+ end = Math.Min((end + moveNum), arrary.Length);
+ }
+
+ foreach (var arr in arrList)
+ {
+ for (int j = 1, len = arr.Length; j <= len; j++)
+ {
+ arr[j - 1].groups = groupCode;
+ arr[j - 1].bill_code = $"{groupCode}-{j}";
+ }
+ }
}
}
}
- }
- if (preTaskCodes?.Count > 0)
- {
- foreach (var disTask in items)
+
+ if (preTaskCodes?.Count > 0)
{
- var curPreTaskCodes = preTaskCodes.FindAll(x => x.bill_id == disTask.pretask_id);
- var curDisTaskCodes = curPreTaskCodes.Adapt>();
- curDisTaskCodes.ForEach(x =>
+ foreach (var disTask in items)
{
- x.id = SnowflakeIdHelper.NextId();
- x.bill_id = disTask.id;
- x.create_time = DateTime.Now;
- });
- distaskCodes.AddRange(curDisTaskCodes);
+ var curPreTaskCodes = preTaskCodes.FindAll(x => x.bill_id == disTask.pretask_id);
+ var curDisTaskCodes = curPreTaskCodes.Adapt>();
+ curDisTaskCodes.ForEach(x =>
+ {
+ x.id = SnowflakeIdHelper.NextId();
+ x.bill_id = disTask.id;
+ x.create_time = DateTime.Now;
+ });
+ distaskCodes.AddRange(curDisTaskCodes);
+ }
}
+
+ disTasks.AddRange(items);
}
- disTasks.AddRange(items);
- }
- try
- {
- await _db.Ado.BeginTranAsync();
+ await db.Ado.BeginTranAsync();
//disTasks.ForEach(x => x.id = SnowflakeIdHelper.NextId());
- var row = await _db.Insertable(disTasks).ExecuteCommandAsync();
+ var row = await db.Insertable(disTasks).ExecuteCommandAsync();
if (preTaskCodes?.Count > 0)
{
- row = await _db.Insertable(distaskCodes).ExecuteCommandAsync();
+ row = await db.Insertable(distaskCodes).ExecuteCommandAsync();
}
if (row > 0)
{
var preTaskIds = preTasks.Select(x => x.id).ToList();
- row = await _db.Updateable().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_YXF_ID }).Where(it => preTaskIds.Contains(it.id)).ExecuteCommandAsync();
+ row = await db.Updateable().SetColumns(it => new WmsPretaskH { status = WmsWareHouseConst.PRETASK_BILL_STATUS_YXF_ID }).Where(it => preTaskIds.Contains(it.id)).ExecuteCommandAsync();
}
//调用AGV创建任务链接口
@@ -332,14 +340,22 @@ namespace Tnb.WarehouseMgr
dynamic reqBody = new ExpandoObject();
sw.Stop();
- Log.Information($"程序运行耗时{sw.ElapsedMilliseconds}ms");
- await _db.Ado.CommitTranAsync();
- }
- catch (Exception)
- {
- await _db.Ado.RollbackTranAsync();
+ JNPF.Logging.Log.Information($"程序运行耗时{sw.ElapsedMilliseconds}ms");
+ await db.Ado.CommitTranAsync();
+
}
}
+ catch (Exception ex)
+ {
+ JNPF.Logging.Log.Error("生成预任务执行时出现错误",ex);
+ await db.Ado.RollbackTranAsync();
+ cts?.Cancel();
+ throw;
+ }
+ finally
+ {
+ cts?.Dispose();
+ }
}
///
/// 任务执行
@@ -513,9 +529,6 @@ namespace Tnb.WarehouseMgr
}
}
-
-
-
///
/// 生成预任务
///
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/WmsSetSortingService.cs b/WarehouseMgr/Tnb.WarehouseMgr/WmsSetSortingService.cs
index 4075f939..08a63096 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr/WmsSetSortingService.cs
+++ b/WarehouseMgr/Tnb.WarehouseMgr/WmsSetSortingService.cs
@@ -30,7 +30,7 @@ namespace Tnb.WarehouseMgr
/// 齐套分拣服务类
///
[ServiceModule(BizTypeId)]
- public class WmsSetSortingService : BaseWareHouseService
+ public class WmsSetSortingService : BaseWareHouseService, IWmsSetSortingService
{
private readonly ISqlSugarClient _db;
private readonly IWareHouseService _wareHouseService;
@@ -51,27 +51,28 @@ namespace Tnb.WarehouseMgr
///
///
[HttpPost]
- public async Task PackSortingByAdd(WmsCarryMat carryMat)
+ public async Task PackSortingByAdd(CancellationTokenSource? cts = default)
{
+ var curDb = _db.CopyNew();
string firstLocationId = "27010980724501", secondLocationId = "27010987857941";
- var endLocation = await _db.Queryable().SingleAsync(it => it.id == secondLocationId);
+ var endLocation = await curDb.Queryable().SingleAsync(it => it.id == secondLocationId);
- var setSortings = await _db.Queryable()
+ var setSortings = await curDb.Queryable()
.Where(a => a.status == WmsWareHouseConst.BILLSTATUS_ADD_ID)
.Select()
.OrderBy(a => a.seq)
.ToListAsync();
- var items = await _db.Queryable().Where(it => it.status == WmsWareHouseConst.BILLSTATUS_ON_ID).ToListAsync();
+ var items = await curDb.Queryable().Where(it => it.status == WmsWareHouseConst.BILLSTATUS_ON_ID).ToListAsync();
var onFlag = items?.Count > 0;
try
{
- await _db.Ado.BeginTranAsync();
+ await curDb.Ado.BeginTranAsync();
if (setSortings?.Count > 0 && !onFlag)
{
var singleSorting = setSortings[0];
- var setSortingDList = await _db.Queryable().Where(it => it.bill_id == singleSorting.id).ToListAsync();
+ var setSortingDList = await curDb.Queryable().Where(it => it.bill_id == singleSorting.id).ToListAsync();
if (setSortingDList?.Count > 0)
{
List carryMats = new();
@@ -82,13 +83,12 @@ namespace Tnb.WarehouseMgr
{
var OutStockStrategyInput = new OutStockStrategyQuery
{
- carry_id = carryMat.carry_id,
material_id = os.material_id,
warehouse_id = os.warehouse_id,
code_batch = os.code_batch,
};
var outStkCarrys = await _wareHouseService.OutStockStrategy(OutStockStrategyInput);
- var carryCodesPart = await _db.Queryable().InnerJoin((a, b) => a.id == b.carry_id).InnerJoin((a, b, c) => a.location_id == c.id)
+ var carryCodesPart = await curDb.Queryable().InnerJoin((a, b) => a.id == b.carry_id).InnerJoin((a, b, c) => a.location_id == c.id)
.Where((a, b) => outStkCarrys.Select(x => x.id).Contains(b.carry_id))
.Select()
.ToListAsync();
@@ -139,7 +139,7 @@ namespace Tnb.WarehouseMgr
return carryMat;
})
.ToList();
- await _db.Insertable(carryMats).ExecuteCommandAsync();
+ await curDb.Insertable(carryMats).ExecuteCommandAsync();
carryIds = carryMats.Select(x => x.carry_id).Distinct().ToList();
await _db.Updateable()
@@ -153,12 +153,11 @@ namespace Tnb.WarehouseMgr
//天益项目不需要
//await _db.Updateable().SetColumns(it => new WmsCarryH { out_status = ((int)EnumOutStatus.分拣出).ToString() }).Where(it => sortingOutIds.Contains(it.id)).ExecuteCommandAsync();
}
- var carrys = await _db.Queryable().Where(it => carryIds.Contains(it.id)).ToArrayAsync();
+ var carrys = await curDb.Queryable().Where(it => carryIds.Contains(it.id)).ToArrayAsync();
if (carrys?.Length > 0)
{
if (setSortings?.Count > 0)
{
-
var curCarry = carrys[^carrys.Length];
var isMatch = await IsCarryAndLocationMatchByCarryStd(curCarry, endLocation);
if (!isMatch) throw new AppFriendlyException("库位与载具规格不匹配", 500);
@@ -193,7 +192,7 @@ namespace Tnb.WarehouseMgr
pretaskCodes.AddRange(curPreTaskCodes);
}
await _wareHouseService.GenPreTask(preTasks, pretaskCodes);
- await _db.Updateable().SetColumns(it => new WmsSetsortingH { status = WmsWareHouseConst.BILLSTATUS_ON_ID }).Where(it => it.id == singleSorting.id).ExecuteCommandAsync();
+ await curDb.Updateable().SetColumns(it => new WmsSetsortingH { status = WmsWareHouseConst.BILLSTATUS_ON_ID }).Where(it => it.id == singleSorting.id).ExecuteCommandAsync();
GenPreTaskUpInput genPreTaskAfterUpInput = new();
genPreTaskAfterUpInput.CarryIds = preTasks.Select(x => x.carry_id).ToList();
genPreTaskAfterUpInput.LocationIds = new HashSet(locIds).ToList();
@@ -203,11 +202,13 @@ namespace Tnb.WarehouseMgr
}
}
- await _db.Ado.CommitTranAsync();
+ await curDb.Ado.CommitTranAsync();
}
- catch (Exception)
+ catch (Exception ex)
{
- await _db.Ado.RollbackTranAsync();
+ JNPF.Logging.Log.Error("齐套分拣执行时出现错误", ex);
+ await curDb.Ado.RollbackTranAsync();
+ cts?.Cancel();
throw;
}
}
diff --git a/WarehouseMgr/Tnb.WarehouseMgr/WmskittingOutService.cs b/WarehouseMgr/Tnb.WarehouseMgr/WmskittingOutService.cs
index cc8523f1..adbee72a 100644
--- a/WarehouseMgr/Tnb.WarehouseMgr/WmskittingOutService.cs
+++ b/WarehouseMgr/Tnb.WarehouseMgr/WmskittingOutService.cs
@@ -59,19 +59,20 @@ namespace Tnb.WarehouseMgr
///
///
[HttpPost]
- public async Task KittingOutByAdd()
+ public async Task KittingOutByAdd(CancellationTokenSource? cts = default)
{
+ var curDb = _db.CopyNew();
try
{
- await _db.Ado.BeginTranAsync();
+ await curDb.Ado.BeginTranAsync();
- var kittingOuts = await _db.Queryable()
+ var kittingOuts = await curDb.Queryable()
.Where(a => a.status == WmsWareHouseConst.BILLSTATUS_ADD_ID)
.OrderBy(a => a.seq)
.ToListAsync();
// 是否有已呼叫的齐套出库任务
// var set = true ; 如果有 把set修改为false
- var items = await _db.Queryable().Where(it => it.status == WmsWareHouseConst.BILLSTATUS_CALLED_ID).ToListAsync();
+ var items = await curDb.Queryable().Where(it => it.status == WmsWareHouseConst.BILLSTATUS_CALLED_ID).ToListAsync();
var isCalled = items?.Count > 0;
if (kittingOuts?.Count > 0)
@@ -82,7 +83,7 @@ namespace Tnb.WarehouseMgr
Expression> whereExp = (ko.carry_id != null) ? a => a.id == ko.carry_id : a => a.carrystd_id == WmsWareHouseConst.CARRY_LJSTD_ID;
- var carrys = await _db.Queryable()
+ var carrys = await curDb.Queryable()
.InnerJoin((a, b) => a.collocation_scheme_id == b.id)
.Where(whereExp.And(a => a.collocation_scheme_id == ko.collocation_scheme_id && a.is_lock == 0))
.OrderBy((a, b) => b.seq)
@@ -116,9 +117,9 @@ namespace Tnb.WarehouseMgr
setSortingH.org_id = _userManager.User.OrganizeId;
setSortingH.create_id = _userManager.UserId;
setSortingH.create_time = DateTime.Now;
- await _db.Insertable(setSortingH).ExecuteCommandAsync();
+ await curDb.Insertable(setSortingH).ExecuteCommandAsync();
- var kittingOutDetails = await _db.Queryable().Where(it => it.bill_id == ko.id).ToListAsync();
+ var kittingOutDetails = await curDb.Queryable().Where(it => it.bill_id == ko.id).ToListAsync();
var setSortDetails = kittingOutDetails.Adapt>();
setSortDetails.ForEach(x =>
{
@@ -129,20 +130,21 @@ namespace Tnb.WarehouseMgr
x.create_id = _userManager.UserId;
x.create_time = DateTime.Now;
});
- await _db.Insertable(setSortDetails).ExecuteCommandAsync();
+ await curDb.Insertable(setSortDetails).ExecuteCommandAsync();
ko.status = WmsWareHouseConst.BILLSTATUS_CALLED_ID;
- await _db.Updateable(ko).UpdateColumns(it => it.status).ExecuteCommandAsync();
+ await curDb.Updateable(ko).UpdateColumns(it => it.status).ExecuteCommandAsync();
isCalled = true;
}
}
}
}
-
- await _db.Ado.CommitTranAsync();
+ await curDb.Ado.CommitTranAsync();
}
- catch (Exception)
+ catch (Exception ex)
{
- await _db.Ado.RollbackTranAsync();
+ JNPF.Logging.Log.Error("齐套出库,新增时出现错误", ex);
+ await curDb.Ado.RollbackTranAsync();
+ cts?.Cancel();
throw;
}
}
@@ -151,11 +153,12 @@ namespace Tnb.WarehouseMgr
///
///
[HttpPost]
- public async Task KittingOutByIsToBeShipped()
+ public async Task KittingOutByIsToBeShipped(CancellationTokenSource? cts = default)
{
+ var curDb = _db.CopyNew();
try
{
- var kittingOuts = await _db.Queryable()
+ var kittingOuts = await curDb.Queryable()
.Where(a => a.status == WmsWareHouseConst.BILLSTATUS_TOBESHIPPED_ID)
.OrderBy(a => a.seq)
.ToListAsync();
@@ -164,21 +167,21 @@ namespace Tnb.WarehouseMgr
var grpList = kittingOuts.GroupBy(g => g.location_id).ToList();
foreach (var koGrp in grpList)
{
- var locs = await _db.Queryable().Where(it => it.id == koGrp.Key && it.is_use == ((int)EnumCarryStatus.空闲).ToString() && it.is_lock == 0).ToListAsync();
+ var locs = await curDb.Queryable().Where(it => it.id == koGrp.Key && it.is_use == ((int)EnumCarryStatus.空闲).ToString() && it.is_lock == 0).ToListAsync();
if (locs?.Count > 0)
{
var arr = koGrp.ToArray();
var ko = arr[^arr.Length];
- var carry = await _db.Queryable().SingleAsync(it => it.id == ko.carry_id);
+ var carry = await curDb.Queryable().SingleAsync(it => it.id == ko.carry_id);
if (carry != null)
{
- WmsPointH sPoint = await _db.Queryable().FirstAsync(it => it.location_id == carry.location_id);
- WmsPointH ePoint = await _db.Queryable().FirstAsync(it => it.location_id == ko.location_id);
+ WmsPointH sPoint = await curDb.Queryable().FirstAsync(it => it.location_id == carry.location_id);
+ WmsPointH ePoint = await curDb.Queryable().FirstAsync(it => it.location_id == ko.location_id);
if (sPoint != null && ePoint != null)
{
//判断目标库位是否自动签收
- var loc = await _db.Queryable().SingleAsync(it => it.id == ePoint.location_id);
+ var loc = await curDb.Queryable().SingleAsync(it => it.id == ePoint.location_id);
var points = await _warehouseService.PathAlgorithms(sPoint.id, ePoint.id);
if (points.Count <= 2) throw new AppFriendlyException("该路径不存在", 500);
if (points?.Count > 0)
@@ -223,11 +226,11 @@ namespace Tnb.WarehouseMgr
preTasks[^1].is_sign = 0; // 修改最后一个元素的是否签收值
}
await _warehouseService.GenPreTask(preTasks, null!);
- var subCarrys = await _db.Queryable().Where(it => it.carry_id == ko.carry_id).ToListAsync();
+ var subCarrys = await curDb.Queryable().Where(it => it.carry_id == ko.carry_id).ToListAsync();
var carryIds = subCarrys.Select(x => x.carry_id).Concat(new[] { ko.carry_id }).Distinct().ToList();
GenPreTaskUpInput genPreTaskInput = new() { CarryIds = carryIds!, LocationIds = new List { carry.location_id!, ko.location_id! } };
await _warehouseService.GenInStockTaskHandleAfter(genPreTaskInput, it => new WmsCarryH { is_lock = 1, carry_status = ((int)EnumCarryStatus.齐套).ToString() }, it => new BasLocation { is_lock = 1 });
- await _db.Updateable().SetColumns(it => it.status == WmsWareHouseConst.BILLSTATUS_ON_ID).Where(it => it.id == ko.id).ExecuteCommandAsync();
+ await curDb.Updateable().SetColumns(it => it.status == WmsWareHouseConst.BILLSTATUS_ON_ID).Where(it => it.id == ko.id).ExecuteCommandAsync();
}
}
}
@@ -243,8 +246,10 @@ namespace Tnb.WarehouseMgr
}
}
}
- catch (Exception)
+ catch (Exception ex)
{
+ JNPF.Logging.Log.Error("齐套出库,待配送时出现错误", ex);
+ cts?.Cancel();
throw;
}
}
diff --git a/apihost/Tnb.API.Entry/Extensions/ConfigureSqlSugarExtensions.cs b/apihost/Tnb.API.Entry/Extensions/ConfigureSqlSugarExtensions.cs
index 7f7378b4..6dbf0ca7 100644
--- a/apihost/Tnb.API.Entry/Extensions/ConfigureSqlSugarExtensions.cs
+++ b/apihost/Tnb.API.Entry/Extensions/ConfigureSqlSugarExtensions.cs
@@ -30,37 +30,43 @@ public static class ConfigureSqlSugarExtensions
InitKeyType = InitKeyType.Attribute,
MoreSettings = new ConnMoreSettings()
{
- IsAutoRemoveDataCache = true // 自动清理缓存
+ IsAutoRemoveDataCache = true, // 自动清理缓存
+ IsAutoToUpper = false,
+ //PgSqlIsAutoToLower = false,
+ DisableNvarchar = true
},
});
services.AddSqlSugar(connectConfigList, client =>
{
- connectConfigList.ForEach(config =>
- {
- var db = ((SqlSugarScope)client).GetConnectionScope((string)config.ConfigId);
+ //connectConfigList.ForEach(config =>
+ //{
+ // var db = ((SqlSugarScope)client).GetConnectionScope((string)config.ConfigId);
- // 设置超时时间
- db.Ado.CommandTimeOut = 30;
- //db.Aop.OnLogExecuted = (sql, pars) =>
- //{
- // var oldColor = Console.ForegroundColor;
- // Console.ForegroundColor = ConsoleColor.Green;
- // var finalSql = UtilMethods.GetSqlString(db.CurrentConnectionConfig.DbType, sql, pars);
- // Console.WriteLine($"【{DateTime.Now.ToString("HH:mm:ss.fff")}——SQL执行完成】{db.Ado.SqlExecutionTime.TotalMilliseconds} ms");
- // Console.WriteLine(finalSql);
- // Console.ForegroundColor = oldColor;
- // if (db.Ado.SqlExecutionTime.TotalMilliseconds > 3000)
- // {
- // Log.Warning($"慢查询: {db.Ado.SqlExecutionTime.TotalMilliseconds}ms, SQL: " + finalSql);
- // }
- // Console.WriteLine();
- //};
- //db.Aop.OnError = (ex) =>
- //{
- // Log.Error(UtilMethods.GetSqlString(db.CurrentConnectionConfig.DbType, ex.Sql, (SugarParameter[])ex.Parametres));
- //};
- });
+ // // 设置超时时间
+ // db.Ado.CommandTimeOut = 30;
+ // db.Aop.OnLogExecuted = (sql, pars) =>
+ // {
+ // var oldColor = Console.ForegroundColor;
+ // Console.ForegroundColor = ConsoleColor.Green;
+ // var finalSql = UtilMethods.GetSqlString(db.CurrentConnectionConfig.DbType, sql, pars);
+ // Console.WriteLine($"【{DateTime.Now.ToString("HH:mm:ss.fff")}——SQL执行完成】{db.Ado.SqlExecutionTime.TotalMilliseconds} ms");
+ // if (db.Ado.SqlExecutionTime.TotalMilliseconds > 3000)
+ // {
+ // Log.Warning($"慢查询: {db.Ado.SqlExecutionTime.TotalMilliseconds}ms, SQL: " + finalSql);
+ // }
+ // else
+ // {
+ // Console.WriteLine(finalSql);
+ // Console.ForegroundColor = oldColor;
+ // Console.WriteLine();
+ // }
+ // };
+ // db.Aop.OnError = (ex) =>
+ // {
+ // Log.Error(UtilMethods.GetSqlString(db.CurrentConnectionConfig.DbType, ex.Sql, (SugarParameter[])ex.Parametres));
+ // };
+ //});
});
services.AddUnitOfWork();
services.AddConfigurableOptions();
diff --git a/apihost/Tnb.API.Entry/Startup.cs b/apihost/Tnb.API.Entry/Startup.cs
index da09f6a4..dd5e6ed7 100644
--- a/apihost/Tnb.API.Entry/Startup.cs
+++ b/apihost/Tnb.API.Entry/Startup.cs
@@ -22,6 +22,7 @@ using Senparc.Weixin;
using Senparc.Weixin.Entities;
using Senparc.Weixin.RegisterServices;
using SqlSugar;
+using Tnb.WarehouseMgr;
namespace JNPF.API.Entry;
@@ -61,6 +62,10 @@ public class Startup : AppStartup
.AddSenparcWeixinServices(App.Configuration); // Senparc.Weixin 注册(如果使用Senparc.Weixin SDK则添加)
services.AddOverideVisualDev();
+
+ //定时任务
+ services.AddHostedService();
+
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider, IOptions senparcSetting, IOptions senparcWeixinSetting)
@@ -110,7 +115,7 @@ public class Startup : AppStartup
SnowflakeIdHelper.InitYitIdWorker();
bool isStartTimeJob = App.GetConfig("IsStartTimeJob");
- if(isStartTimeJob)
+ if (isStartTimeJob)
serviceProvider.GetRequiredService().StartTimerJob();
}
}
\ No newline at end of file
diff --git a/apihost/Tnb.API.Entry/Tnb.API.Entry.csproj b/apihost/Tnb.API.Entry/Tnb.API.Entry.csproj
index e3a54fc6..7e3429bd 100644
--- a/apihost/Tnb.API.Entry/Tnb.API.Entry.csproj
+++ b/apihost/Tnb.API.Entry/Tnb.API.Entry.csproj
@@ -44,6 +44,7 @@
+
diff --git a/common/Tnb.Common/Extension/TaskExtensions.cs b/common/Tnb.Common/Extension/TaskExtensions.cs
new file mode 100644
index 00000000..e31f008f
--- /dev/null
+++ b/common/Tnb.Common/Extension/TaskExtensions.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tnb.Common.Extension
+{
+ public static class TaskEx
+ {
+ public static Task Catch(this Task task, Func onError) where TError : Exception
+ {
+ var tcs = new TaskCompletionSource(); // #A
+ task.ContinueWith(innerTask =>
+ {
+ if (innerTask.IsFaulted && innerTask?.Exception?.InnerException is TError)
+ tcs.SetResult(onError((TError)innerTask.Exception.InnerException)); // #B
+ else if (innerTask.IsCanceled)
+ tcs.SetCanceled(); // #B
+ else if (innerTask.IsFaulted)
+ tcs.SetException(innerTask?.Exception?.InnerException ?? throw new InvalidOperationException()); // #B
+ else
+ tcs.SetResult(innerTask.Result); // #B
+ });
+ return tcs.Task;
+ }
+
+ public static async Task Catch(this Task task, Action exceptionHandler)
+ {
+ try
+ {
+ await task;
+ }
+ catch (Exception ex)
+ {
+ exceptionHandler(ex);
+ }
+ }
+
+ }
+}
diff --git a/message/Tnb.Message/Service/MessageService.cs b/message/Tnb.Message/Service/MessageService.cs
index 1c72e209..65d59f29 100644
--- a/message/Tnb.Message/Service/MessageService.cs
+++ b/message/Tnb.Message/Service/MessageService.cs
@@ -298,9 +298,10 @@ public class MessageService : IMessageService, IDynamicApiController, ITransient
{
try
{
- _repository.AsSugarClient().Insertable(receiveEntityList).ExecuteCommand();
+ //modify ly on 20230803 改为CopyNew模式,否则多线程下会报错
+ _repository.AsSugarClient().CopyNew().Insertable(receiveEntityList).ExecuteCommand();
- return _repository.AsInsertable(entity).IgnoreColumns(ignoreNullColumn: true).CallEntityMethod(m => m.Create()).ExecuteCommand();
+ return _repository.AsSugarClient().CopyNew().Insertable(entity).IgnoreColumns(ignoreNullColumn: true).CallEntityMethod(m => m.Create()).ExecuteCommand();
}
catch (Exception)
{
diff --git a/system/Tnb.OAuth/Dto/LoginInput.cs b/system/Tnb.OAuth/Dto/LoginInput.cs
index 2ba45843..091fba62 100644
--- a/system/Tnb.OAuth/Dto/LoginInput.cs
+++ b/system/Tnb.OAuth/Dto/LoginInput.cs
@@ -12,14 +12,14 @@ public class LoginInput
///
/// 用户名.
///
- /// 13459475357
+ /// admin
[Required(ErrorMessage = "用户名不能为空")]
public string? account { get; set; }
///
/// 密码.
///
- /// e10adc3949ba59abbe56e057f20f883e
+ /// f5252ff163e76623601a9a84d275c842
[Required(ErrorMessage = "密码不能为空")]
public string? password { get; set; }
diff --git a/system/Tnb.Systems/Common/TestService.cs b/system/Tnb.Systems/Common/TestService.cs
index 23d62315..ac3b083f 100644
--- a/system/Tnb.Systems/Common/TestService.cs
+++ b/system/Tnb.Systems/Common/TestService.cs
@@ -24,14 +24,14 @@ namespace JNPF.Systems.Common;
[Route("api")]
public class TestService : IDynamicApiController, ITransient
{
- private readonly ISqlSugarRepository _repository;
+ //private readonly ISqlSugarRepository _repository;
private readonly SqlSugarScope _sugar;
private readonly IDataBaseManager _databaseService;
- public TestService(ISqlSugarRepository repository, IDataBaseManager databaseService)
+ public TestService(ISqlSugarClient db, IDataBaseManager databaseService)
{
- _repository = repository;
- _sugar = (SqlSugarScope)repository.AsSugarClient();
+ //_repository = repository;
+ _sugar = (SqlSugarScope)db;
_databaseService = databaseService;
}
@@ -60,7 +60,7 @@ public class TestService : IDynamicApiController, ITransient
//var xx = App.HttpContext.Request.Host.ToString();
//var sql = "SELECT TOP 1 [F_PARENTID],[F_PROCESSID],[F_ENCODE],[F_FULLNAME],[F_FLOWURGENT],[F_FLOWID],[F_FLOWCODE],[F_FLOWNAME],[F_FLOWTYPE],[F_FLOWCATEGORY],[F_FLOWFORM],[F_FLOWFORMCONTENTJSON],[F_FLOWTEMPLATEJSON],[F_FLOWVERSION],[F_STARTTIME],[F_ENDTIME],[F_THISSTEP],[F_THISSTEPID],[F_GRADE],[F_STATUS],[F_COMPLETION],[F_DESCRIPTION],[F_SORTCODE],[F_ISASYNC],[F_ISBATCH],[F_TASKNODEID],[F_TEMPLATEID],[F_REJECTDATAID],[F_DELEGATEUSER],[F_CREATORTIME],[F_CREATORUSERID],[F_ENABLEDMARK],[F_LastModifyTime],[F_LastModifyUserId],[F_DeleteMark],[F_DeleteTime],[F_DeleteUserId],[F_Id] FROM [FLOW_TASK] WHERE (( [F_DeleteMark] IS NULL ) AND ( [F_Id] = N'367536153122855173' ))";
//var darta = _sqlSugarRepository.AsSugarClient().Ado.SqlQuery(sql);
- var data = await _repository.GetFirstAsync(a => true);
+ var data = await _sugar.Queryable().FirstAsync(a => true);
var json = App.GetService();
return data;
}
diff --git a/taskschedule/Tnb.TaskScheduler.Entitys/Entity/TimeTaskEntity.cs b/taskschedule/Tnb.TaskScheduler.Entitys/Entity/TimeTaskEntity.cs
index acccd160..ea046fd7 100644
--- a/taskschedule/Tnb.TaskScheduler.Entitys/Entity/TimeTaskEntity.cs
+++ b/taskschedule/Tnb.TaskScheduler.Entitys/Entity/TimeTaskEntity.cs
@@ -10,7 +10,7 @@ namespace JNPF.TaskScheduler.Entitys;
/// 版 权:拓通智联科技有限公司(http://www.tuotong-tech.com)
/// 日 期:2021-06-01 .
///
-[SugarTable("BASE_TIMETASK")]
+[SugarTable("base_timetask")]
public class TimeTaskEntity : CLDEntityBase
{
///
diff --git a/visualdev/Tnb.Vmodel/BaseAppService.cs b/visualdev/Tnb.Vmodel/BaseAppService.cs
new file mode 100644
index 00000000..e6fa3d23
--- /dev/null
+++ b/visualdev/Tnb.Vmodel/BaseAppService.cs
@@ -0,0 +1,16 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+using JNPF.DependencyInjection;
+using JNPF.DynamicApiController;
+
+namespace Tnb.VmodelEngine;
+
+///
+/// 增删改查基类
+///
+public class BaseAppService : IDynamicApiController, ITransient
+{
+}
diff --git a/visualdev/Tnb.Vmodel/Constants/DbConsts.cs b/visualdev/Tnb.Vmodel/Constants/DbConsts.cs
new file mode 100644
index 00000000..162f3f70
--- /dev/null
+++ b/visualdev/Tnb.Vmodel/Constants/DbConsts.cs
@@ -0,0 +1,70 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+namespace Tnb.VmodelEngine;
+
+public static class DbConsts
+{
+ public const string DefaultDbCode = "totedp";
+ public const string ConnectNameDapper = "Dapper";
+ public const string ConnectNameMySql = "MySql";
+ public const string ConnectNameSqlServer = "SqlServer";
+ public const string ConnectNameSqlite = "Sqlite";
+ public const string ConnectNamePostgreSql = "PostgreSql";
+ public const string ConnectNameOracle = "Oracle";
+ public const string ConnectNameMongoDb = "MongoDb";
+
+ public const string DbCode = "totemp";
+ public const string DbTablePrefix = "app";
+ public const string DbSchema = null;
+ public const string DbSystemPrefix = "sys";
+ public const string DbMESPrefix = "mes";
+ public const string DbWMSPrefix = "wms";
+ public const string DbBasePrefix = "bas";
+ public const string DbConfigPrefix = "cfg";
+ public const string DbEqpPrefix = "eqp";
+ public const string DbQCPrefix = "qc";
+
+ public const string OneToOne = "OneToOne";
+ public const string OneToMany = "OneToMany";
+ public const string ManyToMany = "ManyToMany";
+
+ public static string[] HiddenFields = new string[] { "Id", "CreationTime", "CreatorId", "LastModificationTime", "LastModifierId" };
+
+ public const string CsString = "string";
+ public const string CsInt = "int";
+ public const string CsShort = "short";
+ public const string CsBool = "bool";
+ public const string CsFloat = "float";
+ public const string CsDouble = "double";
+ public const string CsGuid = "Guid";
+ public const string CsDateTime = "DateTime";
+ public const string CsClass = "class";
+ public const string CsJson = "json";
+ public const string CsEnum = "enum";
+ public const string CsCustomer = "customer";
+
+ public const int LengthXXS = 10;
+ public const int LengthXS = 25;
+ public const int LengthS = 50;
+ public const int LengthM = 100;
+ public const int LengthL = 250;
+ public const int LengthXL = 1000;
+ public const int LengthXXL = 2000;
+ public const int LengthXXXL = 3000;
+ public const int LengthXXXXL = 4000;
+ public const int LengthPassword = 32;
+ public const int LengthNodePath = 742; //20组GUID
+ public const int LengthText = -1;
+
+ public const string SuperAdminName = "admin";
+ public static Guid SuperAdminId = Guid.Parse("39fe9e87-4659-05a7-99ce-9a23d84875df");
+ public static Guid SuperRoleId = Guid.Parse("39fea2c5-088a-32c1-2c2c-b814700aee7e");
+ public static Guid UserAdminId = Guid.Parse("39fe9e87-4695-8eb7-273c-e49b0524aea8");
+ public static Guid RoleAdminId = Guid.Parse("39fea2c8-01a4-75cd-026f-2f0349a94098");
+ public static Guid EventDefaultCategoryId = Guid.Parse("39ffae5d-13d2-1e43-3f0e-98f774f71c8a");
+ public static Guid DefaultViewId = Guid.Parse("39fea2c1-b2dd-cfae-1c3d-89de77f94709");
+
+}
diff --git a/visualdev/Tnb.Vmodel/Constants/ModuleConst.cs b/visualdev/Tnb.Vmodel/Constants/ModuleConst.cs
new file mode 100644
index 00000000..1e99e591
--- /dev/null
+++ b/visualdev/Tnb.Vmodel/Constants/ModuleConst.cs
@@ -0,0 +1,7 @@
+namespace Tnb.VmodelEngine;
+
+public class ModuleConst
+{
+ public const string Tag = "Tnb";
+ public const string Area = "tnb";
+}
diff --git a/visualdev/Tnb.Vmodel/Constants/VmodelEnum.cs b/visualdev/Tnb.Vmodel/Constants/VmodelEnum.cs
new file mode 100644
index 00000000..f4e4fe1b
--- /dev/null
+++ b/visualdev/Tnb.Vmodel/Constants/VmodelEnum.cs
@@ -0,0 +1,109 @@
+/////////////////////////////////////////////////////////////////////////////////
+// 宁波拓通e智造平台 ToTong Next Builder //
+// https://git.tuotong-tech.com/tnb/tnb.server //
+/////////////////////////////////////////////////////////////////////////////////
+
+using System.ComponentModel;
+
+namespace Tnb.VmodelEngine;
+
+public enum eCsType
+{
+ [Description("string")]
+ String = 10,
+ [Description("bool")]
+ Bool = 12,
+ [Description("uuid")]
+ Guid = 14,
+ [Description("int")]
+ Int = 20,
+ [Description("short")]
+ Short = 22,
+ [Description("long")]
+ Long = 24,
+ [Description("float")]
+ Float = 30,
+ [Description("double")]
+ Double = 32,
+ [Description("decimal")]
+ Decimal = 34,
+ [Description("date")]
+ Date = 40,
+ [Description("time")]
+ Time = 42,
+ [Description("datetime")]
+ DateTime = 44,
+ [Description("timestamp")]
+ Timestamp = 46,
+ [Description("enum")]
+ Enum = 50,
+ [Description("dictionary")]
+ Dictionary = 52,
+ [Description("json")]
+ Json = 60,
+ [Description("entity")]
+ Entity = 70,
+ [Description("customer")]
+ Customer = 80,
+}
+public enum eDbType
+{
+ MySql,
+ SqlServer,
+ PostgreSQL,
+ Oracle,
+ Sqlite,
+ MsAccess,
+ Dameng,
+ ClickHouse,
+ Redis,
+ InfluxDb
+}
+public enum eResourceType
+{
+ [Description("菜单")]
+ Menu = 10,
+ [Description("页面")]
+ Page = 20,
+ [Description("按钮")]
+ Button = 30,
+ [Description("接口")]
+ Interface = 40,
+ [Description("视图")]
+ View = 50,
+}
+public enum eSearchType
+{
+ [Description("无")]
+ None = 0,
+ [Description("精准查询")]
+ Exact = 1,
+ [Description("模糊查询")]
+ Fuzzy = 2,
+ [Description("范围查询")]
+ Range = 3,
+}
+public enum eNavigateType
+{
+ [Description("无")]
+ None = 0,
+ [Description("一对一")]
+ OneToOne = 1,
+ [Description("一对多")]
+ OneToMany = 2,
+ [Description("多对多")]
+ ManyToMany = 3,
+}
+
+///
+/// 模型属性类型
+///
+public enum ePropType
+{
+ [Description("表字段")]
+ DbTable = 0,
+ [Description("计算属性")]
+ Calculate = 1,
+ [Description("导航属性")]
+ Navigate = 2,
+}
diff --git a/visualdev/Tnb.Vmodel/DataAccess/DataAccess.cs b/visualdev/Tnb.Vmodel/DataAccess/DataAccess.cs
new file mode 100644
index 00000000..367878d9
--- /dev/null
+++ b/visualdev/Tnb.Vmodel/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.VmodelEngine;
+
+namespace Tnb.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