wanshenmean
2026-03-26 e25dc0d8fad5a2362bf75cf5ca9f26a0fe6c674c
feat(WMS): 增强日志配置与添加事务处理支持

后端变更:
- ServiceBase.cs: 新增 ExecuteWithinTransactionAsync 事务处理辅助方法
- 自动管理事务开始/提交/回滚
- 支持嵌套事务(仅在外层事务时真正开启事务)
- StockSerivce.cs/Sys_MenuService.cs/TaskService.cs: 应用新的事务方法

日志配置增强:
- Program.cs:
- Serilog 配置优化控制台输出模板
- 添加 Seq 日志服务器支持用于结构化日志
- 日志文件增加大小限制(10MB)
- WIDESEA_WMSServer.csproj:
- 添加 Serilog.Sinks.Seq 包(v9.0.0)
- 更新 Serilog.Sinks.File 到 v6.0.0
- appsettings.json:
- 新增 Serilog 详细配置节
- 新增 LocalLogConfig 本地日志配置(日志级别/文件大小/文件数量等)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
已修改7个文件
517 ■■■■■ 文件已修改
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/BaseServices/ServiceBase.cs 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_SystemService/Sys_MenuService.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/WIDESEA_WMSServer.csproj 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/BaseServices/ServiceBase.cs
@@ -36,6 +36,51 @@
        public ISqlSugarClient Db => BaseDal.Db;
        protected async Task<WebResponseContent> ExecuteWithinTransactionAsync(Func<Task<WebResponseContent>> operation)
        {
            var db = Db as SqlSugarClient;
            if (db == null)
            {
                return WebResponseContent.Instance.Error("Database context does not support transactions");
            }
            var ownsTransaction = db.Ado.Transaction == null;
            try
            {
                if (ownsTransaction)
                {
                    db.BeginTran();
                }
                var result = await operation();
                if (result?.Status == true)
                {
                    if (ownsTransaction)
                    {
                        db.CommitTran();
                    }
                    return result;
                }
                if (ownsTransaction)
                {
                    db.RollbackTran();
                }
                return result ?? WebResponseContent.Instance.Error("Transaction failed");
            }
            catch
            {
                if (ownsTransaction)
                {
                    db.RollbackTran();
                }
                throw;
            }
        }
        private PropertyInfo[] _propertyInfo { get; set; } = null;
        public PropertyInfo[] TProperties
        {
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs
@@ -1,4 +1,5 @@
using WIDESEA_Common.StockEnum;
using SqlSugar;
using WIDESEA_Common.StockEnum;
using WIDESEA_Core;
using WIDESEA_DTO.Stock;
using WIDESEA_IStockService;
@@ -51,6 +52,51 @@
        }
        /// <summary>
        /// 在事务中执行操作
        /// </summary>
        private async Task<WebResponseContent> ExecuteWithinTransactionAsync(Func<Task<WebResponseContent>> operation)
        {
            var db = StockInfoService.Repository.Db as SqlSugarClient;
            if (db == null)
            {
                return WebResponseContent.Instance.Error("Database context does not support transactions");
            }
            var ownsTransaction = db.Ado.Transaction == null;
            try
            {
                if (ownsTransaction)
                {
                    db.BeginTran();
                }
                var result = await operation();
                if (result?.Status == true)
                {
                    if (ownsTransaction)
                    {
                        db.CommitTran();
                    }
                    return result;
                }
                if (ownsTransaction)
                {
                    db.RollbackTran();
                }
                return result ?? WebResponseContent.Instance.Error("Transaction failed");
            }
            catch
            {
                if (ownsTransaction)
                {
                    db.RollbackTran();
                }
                throw;
            }
        }
        /// <summary>
        /// 组盘
        /// </summary>
        public async Task<WebResponseContent> GroupPalletAsync(StockDTO stock)
@@ -72,28 +118,29 @@
                Status = StockStatusEmun.组盘暂存.GetHashCode(),
            }).ToList();
            var existingStock = StockInfoService.Repository.QueryFirst(s => s.PalletCode == stock.TargetPalletNo);
            var result = false;
            if (existingStock != null)
            return await ExecuteWithinTransactionAsync(async () =>
            {
                details.ForEach(d => d.StockId = existingStock.Id);
                result = await StockInfoDetailService.Repository.AddDataAsync(details) > 0;
                if (result) return content.OK("组盘成功");
                return content.Error("组盘失败");
            }
                var existingStock = StockInfoService.Repository.QueryFirst(s => s.PalletCode == stock.TargetPalletNo);
                var result = false;
                if (existingStock != null)
                {
                    details.ForEach(d => d.StockId = existingStock.Id);
                    result = await StockInfoDetailService.Repository.AddDataAsync(details) > 0;
                    return result ? content.OK("组盘成功") : content.Error("组盘失败");
                }
            var entity = new Dt_StockInfo
            {
                PalletCode = stock.TargetPalletNo,
                WarehouseId = 1,
                StockStatus = 1,
                Creater = "system",
                Details = details
            };
                var entity = new Dt_StockInfo
                {
                    PalletCode = stock.TargetPalletNo,
                    WarehouseId = 1,
                    StockStatus = 1,
                    Creater = "system",
                    Details = details
                };
            result = StockInfoService.Repository.AddData(entity, x => x.Details);
            if (result) return content.OK("组盘成功");
            return content.Error("组盘失败");
                result = StockInfoService.Repository.AddData(entity, x => x.Details);
                return result ? content.OK("组盘成功") : content.Error("组盘失败");
            });
        }
        /// <summary>
@@ -101,7 +148,6 @@
        /// </summary>
        public async Task<WebResponseContent> ChangePalletAsync(StockDTO stock)
        {
            WebResponseContent content = new WebResponseContent();
            if (stock == null ||
                string.IsNullOrWhiteSpace(stock.TargetPalletNo) ||
@@ -111,44 +157,47 @@
                return content.Error("源托盘号与目标托盘号相同");
            }
            var sourceStock = await StockInfoService.Repository.QueryDataNavFirstAsync(s => s.PalletCode == stock.SourcePalletNo);
            if (sourceStock == null) return content.Error("源托盘不存在");
            var targetStock = StockInfoService.Repository.QueryFirst(s => s.PalletCode == stock.TargetPalletNo);
            if (targetStock == null)
            return await ExecuteWithinTransactionAsync(async () =>
            {
                var newStock = new Dt_StockInfo
                var sourceStock = await StockInfoService.Repository.QueryDataNavFirstAsync(s => s.PalletCode == stock.SourcePalletNo);
                if (sourceStock == null) return content.Error("源托盘不存在");
                var targetStock = StockInfoService.Repository.QueryFirst(s => s.PalletCode == stock.TargetPalletNo);
                if (targetStock == null)
                {
                    PalletCode = stock.TargetPalletNo,
                    WarehouseId = sourceStock.WarehouseId,
                    StockStatus = StockStatusEmun.组盘暂存.GetHashCode(),
                    Creater = "system",
                };
                    var newStock = new Dt_StockInfo
                    {
                        PalletCode = stock.TargetPalletNo,
                        WarehouseId = sourceStock.WarehouseId,
                        StockStatus = StockStatusEmun.组盘暂存.GetHashCode(),
                        Creater = "system",
                    };
                var newId = StockInfoService.Repository.AddData(newStock);
                if (newId <= 0) return content.Error("换盘失败");
                    var newId = StockInfoService.Repository.AddData(newStock);
                    if (newId <= 0) return content.Error("换盘失败");
                targetStock = newStock;
                targetStock.Id = newId;
            }
                    targetStock = newStock;
                    targetStock.Id = newId;
                }
            var serialNumbers = stock.Details.Select(d => d.Channel).Distinct().ToList();
            if (!serialNumbers.Any()) return content.Error("未找到有效的序列号");
                var serialNumbers = stock.Details.Select(d => d.Channel).Distinct().ToList();
                if (!serialNumbers.Any()) return content.Error("未找到有效的序列号");
            var detailEntities = StockInfoDetailService.Repository.QueryData(
                d => d.StockId == sourceStock.Id && serialNumbers.Contains(d.InboundOrderRowNo));
            if (!detailEntities.Any()) return content.Error("未找到有效的库存明细");
                var detailEntities = StockInfoDetailService.Repository.QueryData(
                    d => d.StockId == sourceStock.Id && serialNumbers.Contains(d.InboundOrderRowNo));
                if (!detailEntities.Any()) return content.Error("未找到有效的库存明细");
            if (await StockInfoDetail_HtyService.Repository.AddDataAsync(CreateDetailHistory(detailEntities, "换盘")) <= 0)
                return content.Error("换盘历史记录保存失败");
                if (await StockInfoDetail_HtyService.Repository.AddDataAsync(CreateDetailHistory(detailEntities, "换盘")) <= 0)
                    return content.Error("换盘历史记录保存失败");
            if (await StockInfo_HtyService.Repository.AddDataAsync(CreateStockHistory(new[] { sourceStock, targetStock }, "换盘")) <= 0)
                return content.Error("换盘历史记录保存失败");
                if (await StockInfo_HtyService.Repository.AddDataAsync(CreateStockHistory(new[] { sourceStock, targetStock }, "换盘")) <= 0)
                    return content.Error("换盘历史记录保存失败");
            detailEntities.ForEach(d => d.StockId = targetStock.Id);
            var result = await StockInfoDetailService.Repository.UpdateDataAsync(detailEntities);
            if (!result) return content.Error("换盘失败");
            return content.OK("换盘成功");
                detailEntities.ForEach(d => d.StockId = targetStock.Id);
                var result = await StockInfoDetailService.Repository.UpdateDataAsync(detailEntities);
                if (!result) return content.Error("换盘失败");
                return content.OK("换盘成功");
            });
        }
        /// <summary>
@@ -160,31 +209,34 @@
            if (stock == null || string.IsNullOrWhiteSpace(stock.SourcePalletNo))
                return content.Error("源托盘号不能为空");
            var sourceStock = StockInfoService.Repository.QueryFirst(s => s.PalletCode == stock.SourcePalletNo);
            if (sourceStock == null) return content.Error("源托盘不存在");
            var serialNumbers = stock.Details.Select(d => d.CellBarcode).Distinct().ToList();
            if (!serialNumbers.Any())
            return await ExecuteWithinTransactionAsync(async () =>
            {
                serialNumbers = sourceStock.Details
                                            .Where(x => stock.Details.Any(d => d.Channel == x.InboundOrderRowNo))
                                            .Select(x => x.SerialNumber)
                                            .ToList();
            }
                var sourceStock = StockInfoService.Repository.QueryFirst(s => s.PalletCode == stock.SourcePalletNo);
                if (sourceStock == null) return content.Error("源托盘不存在");
            var detailEntities = StockInfoDetailService.Repository.QueryData(
                d => d.StockId == sourceStock.Id && serialNumbers.Contains(d.SerialNumber));
            if (!detailEntities.Any()) return content.Error("未找到有效的库存明细");
                var serialNumbers = stock.Details.Select(d => d.CellBarcode).Distinct().ToList();
                if (!serialNumbers.Any())
                {
                    serialNumbers = sourceStock.Details
                                                .Where(x => stock.Details.Any(d => d.Channel == x.InboundOrderRowNo))
                                                .Select(x => x.SerialNumber)
                                                .ToList();
                }
            if (await StockInfoDetail_HtyService.Repository.AddDataAsync(CreateDetailHistory(detailEntities, "拆盘")) <= 0)
                return content.Error("拆盘历史记录保存失败");
                var detailEntities = StockInfoDetailService.Repository.QueryData(
                    d => d.StockId == sourceStock.Id && serialNumbers.Contains(d.SerialNumber));
                if (!detailEntities.Any()) return content.Error("未找到有效的库存明细");
            if (await StockInfo_HtyService.Repository.AddDataAsync(CreateStockHistory(new[] { sourceStock }, "拆盘")) <= 0)
                return content.Error("拆盘历史记录保存失败");
                if (await StockInfoDetail_HtyService.Repository.AddDataAsync(CreateDetailHistory(detailEntities, "拆盘")) <= 0)
                    return content.Error("拆盘历史记录保存失败");
            var result = await StockInfoDetailService.Repository.DeleteDataAsync(detailEntities);
            if (!result) return content.Error("拆盘失败");
            return content.OK("拆盘成功");
                if (await StockInfo_HtyService.Repository.AddDataAsync(CreateStockHistory(new[] { sourceStock }, "拆盘")) <= 0)
                    return content.Error("拆盘历史记录保存失败");
                var result = await StockInfoDetailService.Repository.DeleteDataAsync(detailEntities);
                if (!result) return content.Error("拆盘失败");
                return content.OK("拆盘成功");
            });
        }
        /// <summary>
Code/WMS/WIDESEA_WMSServer/WIDESEA_SystemService/Sys_MenuService.cs
@@ -309,6 +309,30 @@
            }
        }
        public object x_GetTreeItem(int menuId)
        {
            var sysMenu = BaseDal.QueryData(x => x.MenuId == menuId)
                .Select(
                p => new
                {
                    p.MenuId,
                    p.ParentId,
                    p.MenuName,
                    p.Url,
                    p.Auth,
                    p.OrderNo,
                    p.Icon,
                    p.Enable,
                    // 2022.03.26增移动端加菜单类型
                    MenuType = p.MenuType,
                    p.CreateDate,
                    p.Creater,
                    p.TableName,
                    p.ModifyDate
                }).FirstOrDefault();
            return sysMenu;
        }
        /// <summary>
        /// 编辑菜单时,获取菜单信息
        /// </summary>
@@ -316,7 +340,7 @@
        /// <returns></returns>
        public object GetTreeItem(int menuId)
        {
            return GetTreeItem(menuId);
            return x_GetTreeItem(menuId);
        }
        /// <summary>
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -172,18 +172,23 @@
                var locationInfo = await _locationInfoService.GetLocationInfo(task.Roadway);
                if (locationInfo == null) return WebResponseContent.Instance.Error("未找到对应的货位");
                locationInfo.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode();
                task.CurrentAddress = task.SourceAddress;
                task.NextAddress = locationInfo.LocationCode;
                task.TargetAddress = locationInfo.LocationCode;
                task.TaskStatus = TaskInStatusEnum.Line_InFinish.GetHashCode();
                return await ExecuteWithinTransactionAsync(async () =>
                {
                    locationInfo.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode();
                    task.CurrentAddress = task.SourceAddress;
                    task.NextAddress = locationInfo.LocationCode;
                    task.TargetAddress = locationInfo.LocationCode;
                    task.TaskStatus = TaskInStatusEnum.Line_InFinish.GetHashCode();
                var updateResult = await BaseDal.UpdateDataAsync(task);
                var locationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo);
                    var updateTaskResult = await BaseDal.UpdateDataAsync(task);
                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo);
                    if (!updateTaskResult || !updateLocationResult)
                    {
                        return WebResponseContent.Instance.Error("任务更新失败");
                    }
                return WebResponseContent.Instance.OK(
                    updateResult && locationResult ? "任务更新成功" : "任务更新失败",
                    locationInfo.LocationCode);
                    return WebResponseContent.Instance.OK("任务更新成功", locationInfo.LocationCode);
                });
            }
            catch (Exception ex)
            {
@@ -205,23 +210,28 @@
                if (location == null) return WebResponseContent.Instance.Error("未找到对应的货位");
                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
                stockInfo.LocationCode = location.LocationCode;
                stockInfo.LocationId = location.Id;
                stockInfo.OutboundDate = task.Roadway switch
                if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
                return await ExecuteWithinTransactionAsync(async () =>
                {
                    var r when r.Contains("GW") => DateTime.Now.AddHours(2),
                    var r when r.Contains("CW") => DateTime.Now.AddHours(1),
                    _ => DateTime.Now
                };
                stockInfo.StockStatus = StockStatusEmun.入库完成.GetHashCode();
                    stockInfo.LocationCode = location.LocationCode;
                    stockInfo.LocationId = location.Id;
                    stockInfo.OutboundDate = task.Roadway switch
                    {
                        var r when r.Contains("GW") => DateTime.Now.AddHours(2),
                        var r when r.Contains("CW") => DateTime.Now.AddHours(1),
                        _ => DateTime.Now
                    };
                    stockInfo.StockStatus = StockStatusEmun.入库完成.GetHashCode();
                location.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
                    location.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
                var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
                var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
                if (!updateLocationResult || !updateStockResult)
                    return WebResponseContent.Instance.Error("任务完成失败");
                return await CompleteTaskAsync(task);
                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
                    if (!updateLocationResult || !updateStockResult)
                        return WebResponseContent.Instance.Error("任务完成失败");
                    return await CompleteTaskAsync(task);
                });
            }
            catch (Exception ex)
            {
@@ -242,18 +252,23 @@
                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
                if (location == null) return WebResponseContent.Instance.Error("未找到对应的货位");
                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
                stockInfo.LocationId = 0;
                stockInfo.LocationCode = null;
                stockInfo.OutboundDate = DateTime.Now;
                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
                if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
                location.LocationStatus = LocationStatusEnum.Free.GetHashCode();
                return await ExecuteWithinTransactionAsync(async () =>
                {
                    stockInfo.LocationId = 0;
                    stockInfo.LocationCode = null;
                    stockInfo.OutboundDate = DateTime.Now;
                var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
                var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
                if (!updateLocationResult || !updateStockResult)
                    return WebResponseContent.Instance.Error("任务完成失败");
                return await CompleteTaskAsync(task);
                    location.LocationStatus = LocationStatusEnum.Free.GetHashCode();
                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
                    if (!updateLocationResult || !updateStockResult)
                        return WebResponseContent.Instance.Error("任务完成失败");
                    return await CompleteTaskAsync(task);
                });
            }
            catch (Exception ex)
            {
@@ -282,21 +297,24 @@
                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
                if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
                stockInfo.LocationCode = targetLocation.LocationCode;
                stockInfo.LocationId = targetLocation.Id;
                stockInfo.StockStatus = StockStatusEmun.入库完成.GetHashCode();
                return await ExecuteWithinTransactionAsync(async () =>
                {
                    stockInfo.LocationCode = targetLocation.LocationCode;
                    stockInfo.LocationId = targetLocation.Id;
                    stockInfo.StockStatus = StockStatusEmun.入库完成.GetHashCode();
                sourceLocation.LocationStatus = LocationStatusEnum.Free.GetHashCode();
                targetLocation.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
                    sourceLocation.LocationStatus = LocationStatusEnum.Free.GetHashCode();
                    targetLocation.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
                var updateSourceResult = await _locationInfoService.UpdateLocationInfoAsync(sourceLocation);
                var updateTargetResult = await _locationInfoService.UpdateLocationInfoAsync(targetLocation);
                var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
                    var updateSourceResult = await _locationInfoService.UpdateLocationInfoAsync(sourceLocation);
                    var updateTargetResult = await _locationInfoService.UpdateLocationInfoAsync(targetLocation);
                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
                if (!updateSourceResult || !updateTargetResult || !updateStockResult)
                    return WebResponseContent.Instance.Error("移库任务完成失败");
                    if (!updateSourceResult || !updateTargetResult || !updateStockResult)
                        return WebResponseContent.Instance.Error("移库任务完成失败");
                return await CompleteTaskAsync(task);
                    return await CompleteTaskAsync(task);
                });
            }
            catch (Exception ex)
            {
@@ -549,45 +567,54 @@
                    taskList.Add(task);
                }
                var addResult = await BaseDal.AddDataAsync(taskList) > 0;
                if (!addResult)
                var transactionResult = await ExecuteWithinTransactionAsync(async () =>
                {
                    return WebResponseContent.Instance.Error($"批量创建任务失败,共 {taskList.Count} 个任务");
                }
                // 任务创建成功后,同步锁定库存和货位状态,避免重复分配
                var stocksToUpdate = stocksToProcess
                    .Select(s =>
                    var addResult = await BaseDal.AddDataAsync(taskList) > 0;
                    if (!addResult)
                    {
                        s.StockStatus = StockStatusEmun.出库锁定.GetHashCode();
                        return s;
                    })
                    .ToList();
                var updateStockResult = await _stockInfoService.Repository.UpdateDataAsync(stocksToUpdate);
                if (!updateStockResult)
                {
                    return WebResponseContent.Instance.Error($"任务创建成功,但库存状态更新失败,共 {stocksToUpdate.Count} 条");
                }
                var locationsToUpdate = stocksToProcess
                    .Where(s => s.LocationDetails != null)
                    .GroupBy(s => s.LocationDetails.Id)
                    .Select(g =>
                    {
                        var location = g.First().LocationDetails;
                        location.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
                        return location;
                    })
                    .ToList();
                if (locationsToUpdate.Any())
                {
                    var updateLocationResult = await _locationInfoService.Repository.UpdateDataAsync(locationsToUpdate);
                    if (!updateLocationResult)
                    {
                        return WebResponseContent.Instance.Error($"任务创建成功,但货位状态更新失败,共 {locationsToUpdate.Count} 条");
                        return WebResponseContent.Instance.Error($"批量创建任务失败,共 {taskList.Count} 个任务");
                    }
                    // 任务创建成功后,同步锁定库存和货位状态,避免重复分配
                    var stocksToUpdate = stocksToProcess
                        .Select(s =>
                        {
                            s.StockStatus = StockStatusEmun.出库锁定.GetHashCode();
                            return s;
                        })
                        .ToList();
                    var updateStockResult = await _stockInfoService.Repository.UpdateDataAsync(stocksToUpdate);
                    if (!updateStockResult)
                    {
                        return WebResponseContent.Instance.Error($"任务创建成功,但库存状态更新失败,共 {stocksToUpdate.Count} 条");
                    }
                    var locationsToUpdate = stocksToProcess
                        .Where(s => s.LocationDetails != null)
                        .GroupBy(s => s.LocationDetails.Id)
                        .Select(g =>
                        {
                            var location = g.First().LocationDetails;
                            location.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
                            return location;
                        })
                        .ToList();
                    if (locationsToUpdate.Any())
                    {
                        var updateLocationResult = await _locationInfoService.Repository.UpdateDataAsync(locationsToUpdate);
                        if (!updateLocationResult)
                        {
                            return WebResponseContent.Instance.Error($"任务创建成功,但货位状态更新失败,共 {locationsToUpdate.Count} 条");
                        }
                    }
                    return WebResponseContent.Instance.OK();
                });
                if (!transactionResult.Status)
                {
                    return transactionResult;
                }
                // 6. 通知 WCS(异步,不影响主流程)
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs
@@ -1,5 +1,3 @@
using System.Reflection;
using System.Text;
using Autofac;
using Autofac.Core;
using Autofac.Extensions.DependencyInjection;
@@ -12,6 +10,9 @@
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Serilog;
using Serilog.Formatting.Json;
using System.Reflection;
using System.Text;
using WIDESEA_Core;
using WIDESEA_Core.Authorization;
using WIDESEA_Core.BaseServices;
@@ -42,13 +43,27 @@
    loggerConfiguration
        .ReadFrom.Configuration(context.Configuration)
        .ReadFrom.Services(services)
        .Enrich.FromLogContext()
        .WriteTo.Console()
        //.Enrich.FromLogContext()
        .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
        .WriteTo.File(
            "logs/serilog-.log.txt",
            //new JsonFormatter(renderMessage: true),
            "logs/serilog-.log",
            outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}",
            rollingInterval: RollingInterval.Day,
            retainedFileCountLimit: 30,
            shared: true);
            // 每个日志文件最大大小(字节),此处设置为10MB
            fileSizeLimitBytes: 10 * 1024 * 1024,
            shared: true
            )
         // 6. 可选:输出到Seq日志服务器(结构化日志服务器)
         // 需要安装 Serilog.Sinks.Seq NuGet包,并确保Seq服务在 http://localhost:5341 运行
         // 如不需要Seq日志,注释掉下方代码即可
         .WriteTo.Seq(
             serverUrl: "http://localhost:5341",
             apiKey: "CWVa8UWQ9CdUp9GWXCPL", // 如Seq需要ApiKey则配置真实密钥
             batchPostingLimit: 1000, // 批量发送数量
             period: TimeSpan.FromSeconds(2) // 发送间隔
         );
});
builder.ConfigureApplication();
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/WIDESEA_WMSServer.csproj
@@ -50,7 +50,8 @@
      <PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
      <PackageReference Include="Serilog.Settings.Configuration" Version="8.0.0" />
      <PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
      <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
      <PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
      <PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0" />
      <PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.7.3" />
    </ItemGroup>
    
@@ -88,4 +89,8 @@
        <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      </Content>
    </ItemGroup>
    <ItemGroup>
      <Folder Include="logs\" />
    </ItemGroup>
</Project>
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json
@@ -1,10 +1,31 @@
{
  "urls": "http://*:9291", //web服务端口,如果用IIS部署,把这个去掉
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Information",
        "Microsoft.AspNetCore": "Warning",
        "Microsoft.AspNetCore.Routing": "Warning",
        "Microsoft.AspNetCore.Mvc": "Warning",
        "Microsoft.AspNetCore.Mvc.Infrastructure": "Warning",
        "Microsoft.AspNetCore.Mvc.Filters": "Warning",
        "Microsoft.AspNetCore.Mvc.ModelBinding": "Warning",
        "Microsoft.EntityFrameworkCore": "Warning"
      }
    }
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.DataProtection": "Warning"
      "Microsoft.AspNetCore.DataProtection": "Warning",
      "Microsoft.AspNetCore.Routing": "Warning",
      "Microsoft.AspNetCore.Mvc": "Warning",
      "Microsoft.AspNetCore.Mvc.Infrastructure": "Warning",
      "Microsoft.AspNetCore.Mvc.Filters": "Warning",
      "Microsoft.AspNetCore.Mvc.ModelBinding": "Warning"
    }
  },
  "dics": "inOrderType,outOrderType,inboundState,createType,enableEnum,enableStatusEnum,locationStatusEnum,locationTypeEnum,taskTypeEnum,taskStatusEnum,outboundStatusEnum,orderDetailStatusEnum,stockStatusEmun,stockChangeType,outStockStatus,receiveOrderTypeEnum,authorityScope,authorityScopes,locationChangeType,warehouses,suppliers,taskType,receiveStatus,purchaseType,purchaseOrderStatus,printStatus",
@@ -25,6 +46,14 @@
    // 注意,http://127.0.0.1:1818 和 http://localhost:1818 是不一样的
    "IPs": "http://127.0.0.1:8080,http://localhost:8080,http://127.0.0.1:8081,http://localhost:8081"
  },
  "LocalLogConfig": {
    "LogLevel": "DEBUG", //日志级别 DEBUG,INFO,WARN,ERROR,FATAL
    "LogFileSize": 10, //单个日志文件大小,单位MB
    "LogFileCount": 300, //日志文件数量
    "EnableConsoleOutput": false, //是否输出到控制台
    "EnableFloderByLevel": true //是否按日志级别生成不同的文件夹
  },
  "LogAopEnable": false,
  "PrintSql": false, //打印SQL语句
  "ApiName": "WIDESEA",