wanshenmean
6 天以前 37acb8358f5602a9013ee29c04a45e33483c2329
Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
@@ -1,56 +1,68 @@
using HslCommunication.WebSocket;
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Text;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Mapster;
using MapsterMapper;
using WIDESEA_Common.CommonEnum;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Common.TaskEnum;
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_Core.Const;
using WIDESEA_Core.DB;
using WIDESEA_Core.Enums;
using WIDESEA_Core.Helper;
using WIDESEA_Core.Seed;
using WIDESEA_Core.Utilities;
using WIDESEA_DTO.Basic;
using WIDESEA_DTO.Task;
using WIDESEA_IBasicService;
using WIDESEA_IRecordService;
using WIDESEA_Model.Models;
namespace WIDESEA_BasicService
{
    /// <summary>
    /// 货位信息服务实现类
    /// </summary>
    public partial class LocationInfoService : ServiceBase<Dt_LocationInfo, IRepository<Dt_LocationInfo>>, ILocationInfoService
    {
        private readonly IUnitOfWorkManage _unitOfWorkManage;
        private readonly IMapper _mapper;
        private readonly IRepository<Dt_Task> _taskRepository;
        private readonly IRepository<Dt_StockInfo> _stockInfoRepository;
        public IRepository<Dt_LocationInfo> Repository => BaseDal;
        private readonly IRecordService _recordService;
        private readonly IRepository<Dt_Warehouse> _warehouseRepository;
        public LocationInfoService(IRepository<Dt_LocationInfo> BaseDal, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_StockInfo> stockInfoRepository) : base(BaseDal)
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="baseDal">基础数据访问对象</param>
        /// <param name="taskRepository">任务仓储</param>
        /// <param name="stockInfoRepository">库存信息仓储</param>
        public LocationInfoService(
            IRepository<Dt_LocationInfo> baseDal,
            IRepository<Dt_Task> taskRepository,
            IRepository<Dt_StockInfo> stockInfoRepository,
            IRepository<Dt_Warehouse> warehouseRepository,
            IMapper mapper,
            IRecordService recordService) : base(baseDal)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _taskRepository = taskRepository;
            _stockInfoRepository = stockInfoRepository;
            _mapper = mapper;
            _recordService = recordService;
            _warehouseRepository = warehouseRepository;
        }
        /// <summary>
        /// 获取货位信息仓储
        /// </summary>
        public IRepository<Dt_LocationInfo> Repository => BaseDal;
        /// <summary>
        /// 批量启用货位
        /// </summary>
        /// <param name="keys">货位主键数组</param>
        /// <returns></returns>
        /// <returns>操作结果</returns>
        public WebResponseContent LocationEnableStatus(int[] keys)
        {
            List<Dt_LocationInfo> locationInfos = Repository.QueryData(x => keys.Contains(x.Id));
            locationInfos.ForEach(x =>
            {
                x.EnableStatus = EnableStatusEnum.Normal.ObjToInt();
            });
            var locationInfos = Repository.QueryData(x => keys.Contains(x.Id));
            locationInfos.ForEach(x => x.EnableStatus = EnableStatusEnum.Normal.GetHashCode());
            Repository.UpdateData(locationInfos);
            return WebResponseContent.Instance.OK();
        }
@@ -58,16 +70,12 @@
        /// 批量禁用货位
        /// </summary>
        /// <param name="keys">货位主键数组</param>
        /// <returns></returns>
        /// <returns>操作结果</returns>
        public WebResponseContent LocationDisableStatus(int[] keys)
        {
            List<Dt_LocationInfo> locationInfos = Repository.QueryData(x => keys.Contains(x.Id));
            locationInfos.ForEach(x =>
            {
                x.EnableStatus = EnableStatusEnum.Disable.ObjToInt();
            });
            var locationInfos = Repository.QueryData(x => keys.Contains(x.Id));
            locationInfos.ForEach(x => x.EnableStatus = EnableStatusEnum.Disable.GetHashCode());
            Repository.UpdateData(locationInfos);
            return WebResponseContent.Instance.OK();
        }
@@ -75,76 +83,45 @@
        /// 单个启用货位
        /// </summary>
        /// <param name="key">货位主键</param>
        /// <returns></returns>
        public WebResponseContent LocationEnableStatus(int key)
        {
            return LocationEnableStatus(new int[] { key });
        }
        /// <returns>操作结果</returns>
        public WebResponseContent LocationEnableStatus(int key) => LocationEnableStatus(new[] { key });
        /// <summary>
        /// 单个禁用货位
        /// </summary>
        /// <param name="key">货位主键</param>
        /// <returns></returns>
        public WebResponseContent LocationDisableStatus(int key)
        {
            return LocationDisableStatus(new int[] { key });
        }
        /// <returns>操作结果</returns>
        public WebResponseContent LocationDisableStatus(int key) => LocationDisableStatus(new[] { key });
        /// <summary>
        /// 初始化货位
        /// </summary>
        /// <param name="initializationLocationDTO"></param>
        /// <returns></returns>
        public WebResponseContent InitializationLocation(InitializationLocationDTO initializationLocationDTO)
        /// <param name="dto">初始化货位数据传输对象</param>
        /// <returns>操作结果</returns>
        public WebResponseContent InitializationLocation(InitializationLocationDTO dto)
        {
            try
            {
                (bool, string, object?) result = ModelValidate.ValidateModelData(initializationLocationDTO);
                if (!result.Item1) return WebResponseContent.Instance.Error(result.Item2);
                var (isValid, errorMsg, _) = ModelValidate.ValidateModelData(dto);
                if (!isValid) return WebResponseContent.Instance.Error(errorMsg);
                int depth = initializationLocationDTO.Depth;
                List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
                for (int i = 0; i < initializationLocationDTO.MaxRow; i++)
                var locationInfos = new List<Dt_LocationInfo>();
                int depth = dto.Depth;
                for (int row = 1; row <= dto.MaxRow; row++)
                {
                    if ((i + 1) % initializationLocationDTO.MaxRow == 1)
                    depth = CalculateDepth(row, dto.MaxRow, dto.Depth, depth);
                    for (int col = 1; col <= dto.MaxColumn; col++)
                    {
                        depth = initializationLocationDTO.Depth;
                    }
                    else if ((i + 1) % initializationLocationDTO.MaxRow == initializationLocationDTO.Depth + 1)
                    {
                        depth = 1;
                    }
                    else if ((i + 1) % initializationLocationDTO.MaxRow > 1 && (i + 1) % initializationLocationDTO.MaxRow <= initializationLocationDTO.Depth)
                    {
                        depth -= 1;
                    }
                    else
                    {
                        depth += 1;
                    }
                    for (int j = 0; j < initializationLocationDTO.MaxColumn; j++)
                    {
                        for (int k = 0; k < initializationLocationDTO.MaxLayer; k++)
                        for (int layer = 1; layer <= dto.MaxLayer; layer++)
                        {
                            Dt_LocationInfo locationInfo = new Dt_LocationInfo()
                            {
                                WarehouseId = 0,
                                Column = j + 1,
                                EnableStatus = EnableStatusEnum.Normal.ObjToInt(),
                                Layer = k + 1,
                                LocationStatus = LocationStatusEnum.Free.ObjToInt(),
                                LocationType = LocationTypeEnum.Undefined.ObjToInt(),
                                RoadwayNo = $"{initializationLocationDTO.Roadway.ToString()}",
                                Row = i + 1,
                                Depth = depth,
                            };
                            locationInfo.LocationCode = $"{locationInfo.RoadwayNo}-{locationInfo.Row.ToString().PadLeft(3, '0')}-{locationInfo.Column.ToString().PadLeft(3, '0')}-{locationInfo.Layer.ToString().PadLeft(3, '0')}-{locationInfo.Depth.ToString().PadLeft(2, '0')}";
                            locationInfo.LocationName = $"{locationInfo.RoadwayNo}巷道{locationInfo.Row.ToString().PadLeft(3, '0')}行{locationInfo.Column.ToString().PadLeft(3, '0')}列{locationInfo.Layer.ToString().PadLeft(3, '0')}层{locationInfo.Depth.ToString().PadLeft(2, '0')}深";
                            locationInfos.Add(locationInfo);
                            var location = CreateLocationInfo(dto.Roadway, row, col, layer, depth);
                            locationInfos.Add(location);
                        }
                    }
                }
                BaseDal.AddData(locationInfos);
                return WebResponseContent.Instance.OK();
            }
@@ -153,5 +130,409 @@
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        /// <summary>
        /// 根据巷道获取空闲货位信息
        /// </summary>
        /// <param name="roadwayNo">巷道编号</param>
        /// <returns>空闲货位信息,如果未找到则返回null</returns>
        public async Task<Dt_LocationInfo?> GetLocationInfo(string roadwayNo)
        {
            var locations = await BaseDal.QueryDataAsync(x =>
                x.EnableStatus == EnableStatusEnum.Normal.GetHashCode() &&
                x.RoadwayNo == roadwayNo &&
                x.LocationStatus == LocationStatusEnum.Free.GetHashCode());
            return locations?
                .OrderByDescending(x => x.Depth)  // 1. 深度优先(从大到小)
                .ThenBy(x => x.Layer)             // 2. 层数
                .ThenBy(x => x.Column)            // 3. 列
                .ThenBy(x => x.Row)               // 4. 行
                .FirstOrDefault();
        }
        /// <summary>
        /// 根据巷道和货位编码获取空闲货位信息
        /// </summary>
        /// <param name="roadwayNo">巷道编号</param>
        /// <param name="locationCode">货位编码</param>
        /// <returns>货位信息,如果未找到则返回null</returns>
        public async Task<Dt_LocationInfo?> GetLocationInfo(string roadwayNo, string locationCode)
        {
            return await BaseDal.QueryFirstAsync(x => x.RoadwayNo == roadwayNo && x.LocationCode == locationCode);
        }
        /// <summary>
        /// 根据货位编码获取货位信息
        /// </summary>
        /// <param name="locationCode">货位编码</param>
        /// <returns>货位信息</returns>
        public async Task<Dt_LocationInfo> GetLocationInfoAsync(string locationCode)
        {
            return await BaseDal.QueryFirstAsync(x => x.LocationCode == locationCode);
        }
        /// <summary>
        /// 根据货位ID获取货位信息
        /// </summary>
        /// <param name="id">货位id</param>
        /// <returns>货位信息</returns>
        public async Task<Dt_LocationInfo> GetLocationInfoAsync(int id)
        {
            return await BaseDal.QueryFirstAsync(x => x.Id == id);
        }
        /// <summary>
        /// 更新货位信息
        /// </summary>
        /// <param name="locationInfo">货位信息对象</param>
        /// <returns>更新是否成功</returns>
        public async Task<bool> UpdateLocationInfoAsync(Dt_LocationInfo locationInfo)
        {
            var beforeLocation = await BaseDal.QueryFirstAsync(x => x.Id == locationInfo.Id);
            var result = await BaseDal.UpdateDataAsync(locationInfo);
            if (!result)
                return false;
            return beforeLocation == null
                || await _recordService.AddLocationChangeRecordAsync(beforeLocation, locationInfo, LocationChangeType.HandUpdate, remark: "货位更新");
        }
        public override WebResponseContent UpdateData(Dt_LocationInfo entity)
        {
            var beforeLocation = BaseDal.QueryFirst(x => x.Id == entity.Id);
            var result = base.UpdateData(entity);
            if (!result.Status || beforeLocation == null)
                return result;
            var saveRecordResult = _recordService.AddLocationChangeRecordAsync(beforeLocation, entity, LocationChangeType.HandUpdate, remark: "货位更新").GetAwaiter().GetResult();
            return saveRecordResult ? result : WebResponseContent.Instance.Error("货位状态变更记录保存失败");
        }
        public override WebResponseContent UpdateData(List<Dt_LocationInfo> entities)
        {
            var beforeLocations = entities
                .Select(entity => BaseDal.QueryFirst(x => x.Id == entity.Id))
                .Where(location => location != null)
                .ToDictionary(location => location!.Id, location => location!);
            var result = base.UpdateData(entities);
            if (!result.Status)
                return result;
            foreach (var entity in entities)
            {
                if (!beforeLocations.TryGetValue(entity.Id, out var beforeLocation))
                    continue;
                var saveRecordResult = _recordService.AddLocationChangeRecordAsync(beforeLocation, entity, LocationChangeType.HandUpdate, remark: "批量货位更新").GetAwaiter().GetResult();
                if (!saveRecordResult)
                    return WebResponseContent.Instance.Error("货位状态变更记录保存失败");
            }
            return result;
        }
        /// <summary>
        /// 检查并生成移库任务或返回出库任务
        /// </summary>
        /// <param name="taskNum">任务号</param>
        /// <returns>任务信息</returns>
        public async Task<WebResponseContent> TransferCheckAsync(int taskNum)
        {
            var content = new WebResponseContent();
            try
            {
                // 根据任务号获取任务
                var outboundTask = await _taskRepository.QueryFirstAsync(x => x.TaskNum == taskNum);
                if (outboundTask == null)
                    return content.Error("任务不存在");
                var location = await BaseDal.QueryFirstAsync(x => x.LocationCode == outboundTask.SourceAddress && x.RoadwayNo == outboundTask.Roadway);
                // 检查是否需要进行移库
                if (CheckForInternalTransfer(location))
                {
                    // 计算对应位置的相对库位(奇数行的下一行或者偶数行的上一行)
                    var newLocationID = GetRelativeLocationID(location);
                    // 获取新的库位的任务
                    var internalTransferTask = await _taskRepository.QueryFirstAsync(x => x.SourceAddress == newLocationID && x.Roadway == outboundTask.Roadway);
                    // 如果新的库位没有找到对应的任务
                    if (internalTransferTask == null)
                    {
                        return content.OK("获取到移库任务", await HandleNoTaskAtLocation(newLocationID, outboundTask));
                    }
                    // 直接返回一深位出库任务
                    return content.OK("获取到一深位出库任务", internalTransferTask);
                }
                // 返回当前库位的出库任务
                return content.OK("当前出库任务", outboundTask);
            }
            catch (Exception ex)
            {
                return content.Error($"发生错误:{ex.Message}");
            }
        }
        #region 私有方法
        /// <summary>
        /// 计算相对的库位ID
        /// </summary>
        /// <param name="locationInfo">货位信息</param>
        /// <returns>相对的库位ID</returns>
        private static string GetRelativeLocationID(Dt_LocationInfo locationInfo)
        {
            int line = locationInfo.Row;
            // 计算相对的货位行值,奇数行的下一行或者偶数行的上一行
            int relativeLine = line % 2 == 1 ? line + 1 : line - 1;
            // 构建新的库位ID
            string[] newLocationParts = new string[] { relativeLine.ToString().PadLeft(3, '0'), locationInfo.Column.ToString().PadLeft(3, '0'), locationInfo.Layer.ToString().PadLeft(3, '0') };
            return string.Join("-", newLocationParts);
        }
        /// <summary>
        /// 处理没有任务的库位情况
        /// </summary>
        /// <param name="newLocationID">新的库位ID</param>
        /// <param name="outboundTask">出库任务</param>
        /// <returns>生成的移库任务或原始出库任务</returns>
        private async Task<Dt_Task> HandleNoTaskAtLocation(string newLocationID, Dt_Task outboundTask)
        {
            // 判断该位置是否有库存
            var stockInfo = await _stockInfoRepository.QueryDataNavFirstAsync(x =>
                            x.LocationCode == newLocationID &&
                            x.StockStatus == StockStatusEmun.入库完成.GetHashCode() &&
                            x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode());
            if (stockInfo == null)
            {
                // 如果没有库存,直接返回当前出库任务
                return outboundTask;
            }
            // 如果有库存,生成移库任务
            var emptyLocation = await GetTransferLocationEmptyAsync(outboundTask.Roadway);
            var taskNo = await _taskRepository.GetTaskNo();
            var newTransferTask = new Dt_Task
            {
                CreateDate = DateTime.Now,
                Creater = App.User.UserName ?? "system",
                CurrentAddress = newLocationID,
                Grade = 99,
                NextAddress = emptyLocation.LocationCode,
                PalletCode = stockInfo.PalletCode,
                Remark = "移库",
                Roadway = stockInfo.LocationDetails.RoadwayNo,
                SourceAddress = newLocationID,
                TaskNum = taskNo,
                TargetAddress = emptyLocation.LocationCode,
                TaskType = TaskRelocationTypeEnum.Relocation.GetHashCode(),
                TaskStatus = TaskRelocationStatusEnum.RelocationNew.GetHashCode(),
                WarehouseId = stockInfo.WarehouseId
            };
            var createdTask = await _taskRepository.Db.Insertable(newTransferTask).ExecuteReturnEntityAsync();
            var beforeStock = CloneStockSnapshot(stockInfo);
            var beforeSourceLocation = stockInfo.LocationDetails == null ? null : CloneLocationSnapshot(stockInfo.LocationDetails);
            var beforeTargetLocation = CloneLocationSnapshot(emptyLocation);
            // 创建移库任务后,立即锁定库存和相关货位,避免并发重复分配
            stockInfo.StockStatus = StockStatusEmun.移库锁定.GetHashCode();
            var updateStockResult = await _stockInfoRepository.UpdateDataAsync(stockInfo);
            var locationsToUpdate = new List<Dt_LocationInfo>();
            if (stockInfo.LocationDetails != null)
            {
                stockInfo.LocationDetails.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
                locationsToUpdate.Add(stockInfo.LocationDetails);
            }
            emptyLocation.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode();
            locationsToUpdate.Add(emptyLocation);
            var updateLocationResult = await BaseDal.UpdateDataAsync(locationsToUpdate);
            if (!updateStockResult || !updateLocationResult)
            {
                throw new Exception("创建移库任务后更新库存状态或货位状态失败");
            }
            var saveStockRecordResult = await _recordService.AddStockChangeRecordAsync(
                beforeStock,
                stockInfo,
                StockChangeTypeEnum.Relocation,
                createdTask.TaskNum,
                createdTask.OrderNo,
                "移库任务预占库存");
            if (!saveStockRecordResult)
            {
                throw new Exception("创建移库任务后记录库存变更失败");
            }
            if (beforeSourceLocation != null && stockInfo.LocationDetails != null)
            {
                var saveSourceLocationRecordResult = await _recordService.AddLocationChangeRecordAsync(
                    beforeSourceLocation,
                    stockInfo.LocationDetails,
                    LocationChangeType.RelocationAssignLocation,
                    createdTask.TaskNum,
                    createdTask.OrderNo,
                    null,
                    "移库任务锁定源货位");
                if (!saveSourceLocationRecordResult)
                {
                    throw new Exception("创建移库任务后记录源货位变更失败");
                }
            }
            var saveTargetLocationRecordResult = await _recordService.AddLocationChangeRecordAsync(
                beforeTargetLocation,
                emptyLocation,
                LocationChangeType.RelocationAssignLocation,
                createdTask.TaskNum,
                createdTask.OrderNo,
                null,
                "移库任务锁定目标货位");
            if (!saveTargetLocationRecordResult)
            {
                throw new Exception("创建移库任务后记录目标货位变更失败");
            }
            return createdTask;
        }
        private static Dt_LocationInfo CloneLocationSnapshot(Dt_LocationInfo location)
        {
            return new Dt_LocationInfo
            {
                Id = location.Id,
                WarehouseId = location.WarehouseId,
                LocationCode = location.LocationCode,
                LocationName = location.LocationName,
                RoadwayNo = location.RoadwayNo,
                Row = location.Row,
                Column = location.Column,
                Layer = location.Layer,
                Depth = location.Depth,
                LocationType = location.LocationType,
                LocationStatus = location.LocationStatus,
                EnableStatus = location.EnableStatus,
                Remark = location.Remark
            };
        }
        private static Dt_StockInfo CloneStockSnapshot(Dt_StockInfo stockInfo)
        {
            return new Dt_StockInfo
            {
                Id = stockInfo.Id,
                PalletCode = stockInfo.PalletCode,
                PalletType = stockInfo.PalletType,
                LocationId = stockInfo.LocationId,
                LocationCode = stockInfo.LocationCode,
                WarehouseId = stockInfo.WarehouseId,
                StockStatus = stockInfo.StockStatus,
                Remark = stockInfo.Remark,
                OutboundDate = stockInfo.OutboundDate,
                Details = stockInfo.Details?.Select(detail => new Dt_StockInfoDetail
                {
                    Id = detail.Id,
                    StockId = detail.StockId,
                    MaterielCode = detail.MaterielCode,
                    MaterielName = detail.MaterielName,
                    OrderNo = detail.OrderNo,
                    BatchNo = detail.BatchNo,
                    ProductionDate = detail.ProductionDate,
                    EffectiveDate = detail.EffectiveDate,
                    SerialNumber = detail.SerialNumber,
                    StockQuantity = detail.StockQuantity,
                    OutboundQuantity = detail.OutboundQuantity,
                    Status = detail.Status,
                    Unit = detail.Unit,
                    InboundOrderRowNo = detail.InboundOrderRowNo,
                    Remark = detail.Remark
                }).ToList()
            };
        }
        /// <summary>
        /// 检查货位是否需要移库
        /// </summary>
        /// <param name="location">货位信息</param>
        /// <returns>是否需要移库</returns>
        private static bool CheckForInternalTransfer(Dt_LocationInfo location)
        {
            return location.Depth == 2;
        }
        /// <summary>
        /// 根据巷道获取二深位的空库位
        /// </summary>
        /// <param name="roadway">巷道编号</param>
        /// <returns>货位对象</returns>
        private async Task<Dt_LocationInfo> GetTransferLocationEmptyAsync(string roadway)
        {
            return await BaseDal.QueryFirstAsync(x => x.Depth == 2 && x.LocationStatus == LocationStatusEnum.Free.GetHashCode() && x.RoadwayNo == roadway);
        }
        /// <summary>
        /// 计算深度
        /// </summary>
        /// <param name="row">行号</param>
        /// <param name="maxRow">最大行数</param>
        /// <param name="maxDepth">最大深度</param>
        /// <param name="currentDepth">当前深度</param>
        /// <returns>计算后的深度</returns>
        private static int CalculateDepth(int row, int maxRow, int maxDepth, int currentDepth)
        {
            int mod = row % maxRow;
            if (mod == 1) return maxDepth;
            if (mod == maxDepth + 1) return 1;
            if (mod > 1 && mod <= maxDepth) return currentDepth - 1;
            return currentDepth + 1;
        }
        /// <summary>
        /// 创建货位信息
        /// </summary>
        /// <param name="roadwayNo">巷道编号</param>
        /// <param name="row">行号</param>
        /// <param name="col">列号</param>
        /// <param name="layer">层数</param>
        /// <param name="depth">深度</param>
        /// <returns>货位信息对象</returns>
        private Dt_LocationInfo CreateLocationInfo(string roadwayNo, int row, int col, int layer, int depth)
        {
            var warehouse = _warehouseRepository.QueryData(x => x.WarehouseCode == roadwayNo).FirstOrDefault();
            if (warehouse == null)
            {
                throw new InvalidOperationException($"未找到巷道编号为 {roadwayNo} 的仓库信息");
            }
            return new Dt_LocationInfo
            {
                WarehouseId = warehouse.WarehouseId,
                Row = row,
                Column = col,
                Layer = layer,
                Depth = depth,
                RoadwayNo = roadwayNo,
                EnableStatus = EnableStatusEnum.Normal.GetHashCode(),
                LocationStatus = LocationStatusEnum.Free.GetHashCode(),
                LocationType = LocationTypeEnum.Undefined.GetHashCode(),
                //LocationCode = $"{roadwayNo}-{row:D3}-{col:D3}-{layer:D3}",
                LocationCode = $"{row:D3}-{col:D3}-{layer:D3}",
                LocationName = $"{roadwayNo}巷道{row:D3}行{col:D3}列{layer:D3}层{depth:D2}深"
            };
        }
        #endregion 私有方法
    }
}