647556386
2026-03-31 6f0ffcf4a6e75ac3a76c6cfd75e02de3a17d46e3
同库区移库
已修改9个文件
620 ■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/config/buttons.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/stock/stockView.js 311 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Common/TaskEnum/TaskTypeEnum.cs 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/config/buttons.js
@@ -355,6 +355,14 @@
    type: 'warning',
    onClick: function () {
    }
},{
    name: "库存同区域移库",
    icon: '',
    class: '',
    value: 'SelectStockAreaOut',
    type: 'danger',
    onClick: function () {
    }
}
]
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/stock/stockView.js
@@ -159,169 +159,182 @@
      })
      let SelectTake = this.buttons.find(x => x.value == 'SelectStockTake');
if (SelectTake) {
  // æ”¹ä¸ºç®­å¤´å‡½æ•°ç¡®ä¿this指向Vue组件实例
  SelectTake.onClick = async () => {
    // èŽ·å–é€‰ä¸­æ•°æ®ï¼ˆä¸ŽåŽŸé€»è¾‘ä¸€è‡´ï¼Œç¡®ä¿æ˜¯æ•°ç»„æ ¼å¼ï¼‰
    let stockViews = this.$refs.table.getSelected();
    // æ•°æ®æ ¡éªŒï¼šè‡³å°‘选择一条数据
    if (stockViews.length === 0) return ElMessage.error("请选择需要操作的数据!");
    // ç«™å°é€‰é¡¹ï¼ˆå¯æ ¹æ®å®žé™…业务调整选项值)
    const stationOptions = [
      { label: "站台2", value: "2-1" },
      { label: "站台3", value: "3-1" },
      { label: "站台4", value: "4-1" },
    ];
    // åˆ›å»ºå¼¹çª—挂载节点
    const mountNode = document.createElement("div");
    document.body.appendChild(mountNode);
    // è¡¨å•数据(绑定选中的站台)
    const formData = reactive({
      outStation: stationOptions[0].value, // é»˜è®¤é€‰ä¸­ç¬¬ä¸€ä¸ªç«™å°
    });
    // åˆ›å»ºå¼¹çª—VNode
    const vnode = createVNode(
      ElDialog,
      {
        title: "库存操作 - é€‰æ‹©å‡ºåº“站台",
        width: "500px",
        modelValue: true,
        appendToBody: true,
        "onUpdate:modelValue": (isVisible) => {
          if (!isVisible) {
            render(null, mountNode);
            document.body.removeChild(mountNode);
          }
        },
        style: {
          padding: "20px 0",
          borderRadius: "8px",
        },
      },
      {
        default: () =>
          h(
            ElForm,
      if (SelectTake) {
        // æ”¹ä¸ºç®­å¤´å‡½æ•°ç¡®ä¿this指向Vue组件实例
        SelectTake.onClick = async () => {
          // èŽ·å–é€‰ä¸­æ•°æ®ï¼ˆä¸ŽåŽŸé€»è¾‘ä¸€è‡´ï¼Œç¡®ä¿æ˜¯æ•°ç»„æ ¼å¼ï¼‰
          let stockViews = this.$refs.table.getSelected();
          // æ•°æ®æ ¡éªŒï¼šè‡³å°‘选择一条数据
          if (stockViews.length === 0) return ElMessage.error("请选择需要操作的数据!");
          // ç«™å°é€‰é¡¹ï¼ˆå¯æ ¹æ®å®žé™…业务调整选项值)
          const stationOptions = [
            { label: "站台2", value: "2-1" },
            { label: "站台3", value: "3-1" },
            { label: "站台4", value: "4-1" },
          ];
          // åˆ›å»ºå¼¹çª—挂载节点
          const mountNode = document.createElement("div");
          document.body.appendChild(mountNode);
          // è¡¨å•数据(绑定选中的站台)
          const formData = reactive({
            outStation: stationOptions[0].value, // é»˜è®¤é€‰ä¸­ç¬¬ä¸€ä¸ªç«™å°
          });
          // åˆ›å»ºå¼¹çª—VNode
          const vnode = createVNode(
            ElDialog,
            {
              model: formData,
              rules: {
                outStation: [
                  { required: true, message: "请选择出库站台", trigger: "change" },
                ],
              title: "库存操作 - é€‰æ‹©å‡ºåº“站台",
              width: "500px",
              modelValue: true,
              appendToBody: true,
              "onUpdate:modelValue": (isVisible) => {
                if (!isVisible) {
                  render(null, mountNode);
                  document.body.removeChild(mountNode);
                }
              },
              ref: "stockTakeForm", // è¡¨å•ref标识
              labelWidth: "100px",
              style: {
                padding: "0 30px",
                padding: "20px 0",
                borderRadius: "8px",
              },
            },
            [
              // ç«™å°é€‰æ‹©ä¸‹æ‹‰æ¡†
              h(ElFormItem, {
                label: "出库站台",
                prop: "outStation",
                style: { marginBottom: "24px" },
              }, [
                h(ElSelect, {
                  placeholder: "请选择出库站台",
                  modelValue: formData.outStation,
                  "onUpdate:modelValue": (val) => {
                    formData.outStation = val;
            {
              default: () =>
                h(
                  ElForm,
                  {
                    model: formData,
                    rules: {
                      outStation: [
                        { required: true, message: "请选择出库站台", trigger: "change" },
                      ],
                    },
                    ref: "stockTakeForm", // è¡¨å•ref标识
                    labelWidth: "100px",
                    style: {
                      padding: "0 30px",
                    },
                  },
                  style: {
                    width: "100%",
                    height: "40px",
                    borderRadius: "4px",
                    borderColor: "#dcdfe6",
                  },
                }, stationOptions.map((station) =>
                  h(ElOption, { label: station.label, value: station.value })
                )),
              ]),
              // åº•部按钮区域
              h("div", {
                style: {
                  textAlign: "right",
                  marginTop: "8px",
                  paddingRight: "4px",
                },
              }, [
                // å–消按钮
                h(ElButton, {
                  type: "text",
                  onClick: () => {
                    render(null, mountNode);
                    document.body.removeChild(mountNode);
                    ElMessage.info("取消库存操作");
                  },
                  style: { marginRight: "8px", color: "#606266" },
                }, "取消"),
                // ç¡®å®šæŒ‰é’®
                h(ElButton, {
                  type: "primary",
                  onClick: async () => {
                    // ç¡®ä¿è¡¨å•ref已挂载
                    await this.$nextTick();
                    const formRef = vnode.component.refs.stockTakeForm;
                  [
                    // ç«™å°é€‰æ‹©ä¸‹æ‹‰æ¡†
                    h(ElFormItem, {
                      label: "出库站台",
                      prop: "outStation",
                      style: { marginBottom: "24px" },
                    }, [
                      h(ElSelect, {
                        placeholder: "请选择出库站台",
                        modelValue: formData.outStation,
                        "onUpdate:modelValue": (val) => {
                          formData.outStation = val;
                        },
                        style: {
                          width: "100%",
                          height: "40px",
                          borderRadius: "4px",
                          borderColor: "#dcdfe6",
                        },
                      }, stationOptions.map((station) =>
                        h(ElOption, { label: station.label, value: station.value })
                      )),
                    ]),
                    // åº•部按钮区域
                    h("div", {
                      style: {
                        textAlign: "right",
                        marginTop: "8px",
                        paddingRight: "4px",
                      },
                    }, [
                      // å–消按钮
                      h(ElButton, {
                        type: "text",
                        onClick: () => {
                          render(null, mountNode);
                          document.body.removeChild(mountNode);
                          ElMessage.info("取消库存操作");
                        },
                        style: { marginRight: "8px", color: "#606266" },
                      }, "取消"),
                      // ç¡®å®šæŒ‰é’®
                      h(ElButton, {
                        type: "primary",
                        onClick: async () => {
                          // ç¡®ä¿è¡¨å•ref已挂载
                          await this.$nextTick();
                          const formRef = vnode.component.refs.stockTakeForm;
                    if (!formRef) {
                      ElMessage.error("表单初始化失败,请重试");
                      return;
                    }
                          if (!formRef) {
                            ElMessage.error("表单初始化失败,请重试");
                            return;
                          }
                    // è¡¨å•验证
                    try {
                      await formRef.validate();
                    } catch (err) {
                      return;
                    }
                          // è¡¨å•验证
                          try {
                            await formRef.validate();
                          } catch (err) {
                            return;
                          }
                    const requestBody = stockViews;
                    const outStation = formData.outStation;
                          const requestBody = stockViews;
                          const outStation = formData.outStation;
                    try {
                          try {
                      const url = `api/Task/TakeOutbound?outStation=${encodeURIComponent(outStation)}`;
                      const x = await this.http.post(
                        url, // å¸¦æŸ¥è¯¢å‚æ•°çš„URL
                        requestBody, // è¯·æ±‚体:stockViews数组
                        "数据处理中"
                      );
                            const url = `api/Task/TakeOutbound?outStation=${encodeURIComponent(outStation)}`;
                            const x = await this.http.post(
                              url, // å¸¦æŸ¥è¯¢å‚æ•°çš„URL
                              requestBody, // è¯·æ±‚体:stockViews数组
                              "数据处理中"
                            );
                      if (!x.status) {
                        ElMessage.error(x.message || "库存操作失败");
                        return;
                      }
                            if (!x.status) {
                              ElMessage.error(x.message || "库存操作失败");
                              return;
                            }
                      ElMessage.success("操作成功");
                      this.refresh(); // åŽŸåˆ·æ–°é€»è¾‘ä¿ç•™
                    } catch (error) {
                      console.error("库存接口请求失败:", error);
                      ElMessage.error("请求失败,请稍后重试");
                    } finally {
                      // æ— è®ºæˆåŠŸå¤±è´¥ï¼Œå…³é—­å¼¹çª—
                      render(null, mountNode);
                      document.body.removeChild(mountNode);
                    }
                  },
                  style: { borderRadius: "4px", padding: "8px 20px" },
                }, "确定操作"),
              ]),
            ]),
                            ElMessage.success("操作成功");
                            this.refresh(); // åŽŸåˆ·æ–°é€»è¾‘ä¿ç•™
                          } catch (error) {
                            console.error("库存接口请求失败:", error);
                            ElMessage.error("请求失败,请稍后重试");
                          } finally {
                            // æ— è®ºæˆåŠŸå¤±è´¥ï¼Œå…³é—­å¼¹çª—
                            render(null, mountNode);
                            document.body.removeChild(mountNode);
                          }
                        },
                        style: { borderRadius: "4px", padding: "8px 20px" },
                      }, "确定操作"),
                    ]),
                  ]),
            }
          );
          // ç»‘定app上下文(确保弹窗内组件正常工作)
          vnode.appContext = this.$.appContext;
          render(vnode, mountNode);
        };
      }
    );
    // ç»‘定app上下文(确保弹窗内组件正常工作)
    vnode.appContext = this.$.appContext;
    render(vnode, mountNode);
  };
}
      let SelectTakeArea = this.buttons.find(x => x.value == 'SelectStockAreaOut');
      if (SelectTakeArea) {
        SelectTakeArea.onClick = function () {
          let stockViews = this.$refs.table.getSelected();
          this.http
            .post("api/Task/AreaOutbound",stockViews, "数据处理中")
            .then((x) => {
              if (!x.status) return this.$message.error(x.message);
              this.$message.success("操作成功");
              this.refresh();
            });
        }
      }
    },
    onInited() {
      //框架初始化配置后
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
@@ -156,7 +156,6 @@
                    { nameof(Dt_LocationInfo.Row),OrderByType.Asc },
                    { nameof(Dt_LocationInfo.Column),OrderByType.Asc },
                    { nameof(Dt_LocationInfo.Depth),OrderByType.Desc },
                };
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Common/TaskEnum/TaskTypeEnum.cs
@@ -147,8 +147,14 @@
        /// å··é“内移库
        /// </summary>
        [Description("巷道内移库")]
        Relocation = 900
        Relocation = 900,
        /// <summary>
        /// åŒåŒºåŸŸç§»åº“
        /// </summary>
        [Description("同区域移库")]
        AreaRelocation = 910
    }
    public enum TaskTypeGroup
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs
@@ -79,5 +79,7 @@
        Task<WebResponseContent> HandCompleteTask(string TaskNum);
        Task<WebResponseContent> TaskCancel(List<int> taskCodes);
        Task<WebResponseContent> AreaOutbound(List<StockViewDTO> stockViews);
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs
@@ -1376,13 +1376,8 @@
                        Func<Dt_OutStockLockInfo, bool> wareWhere = x => string.IsNullOrEmpty(outboundOrderDetails.First().WarehouseCode) ? true : x.WarehouseCode == outboundOrderDetails.First().WarehouseCode;
                        var idStr = outboundOrderDetails.First().Id.ToString();
                        var stockLockInfos = _outboundLockInfoRepository.QueryData(x =>
                            (x.OrderDetailIds == idStr ||
                             x.OrderDetailIds.StartsWith(idStr + ",") ||
                             x.OrderDetailIds.EndsWith("," + idStr) ||
                             x.OrderDetailIds.Contains("," + idStr + ",")) &&
                            x.OrderDetailIds == lockInfo.OrderDetailIds &&
                            x.OrderNo == request.OrderNo &&
                            x.MaterielCode == stockInfoDetail.MaterielCode)
                            .Where(supWhere)
@@ -1813,13 +1808,8 @@
                        Func<Dt_OutStockLockInfo, bool> wareWhere = x => string.IsNullOrEmpty(outboundOrderDetails.First().WarehouseCode) ? true : x.WarehouseCode == outboundOrderDetails.First().WarehouseCode;
                        var idStr = outboundOrderDetails.First().Id.ToString();
                        var stockLockInfos = _outboundLockInfoRepository.QueryData(x =>
                            (x.OrderDetailIds == idStr ||
                             x.OrderDetailIds.StartsWith(idStr + ",") ||
                             x.OrderDetailIds.EndsWith("," + idStr) ||
                             x.OrderDetailIds.Contains("," + idStr + ",")) &&
                            x.OrderDetailIds == lockInfo.OrderDetailIds &&
                            x.OrderNo == request.OrderNo &&
                            x.MaterielCode == stockDetail.MaterielCode)
                            .Where(supWhere)
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -2583,6 +2583,134 @@
                return WebResponseContent.Instance.Error($"任务取消失败:{ex.Message}");
            }
        }
        public async Task<WebResponseContent> AreaRelocationTaskCompleted(Dt_Task task)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
                if (task == null || string.IsNullOrEmpty(task.PalletCode) || string.IsNullOrEmpty(task.TargetAddress))
                {
                    return WebResponseContent.Instance.Error("移库任务信息不完整(托盘号/目标货位为空)");
                }
                // 2. æŸ¥è¯¢æ‰˜ç›˜åº“存信息
                Dt_StockInfo stockInfo = await _stockRepository.Db.Queryable<Dt_StockInfo>()
                    .Includes(x => x.Details)
                    .Where(x => x.PalletCode == task.PalletCode)
                    .FirstAsync();
                if (stockInfo == null)
                {
                    return WebResponseContent.Instance.Error($"未找到托盘[{task.PalletCode}]对应的组盘信息");
                }
                // éžç©ºæ‰˜ç›˜å¿…须有明细
                if (stockInfo.Details.Count == 0 && stockInfo.PalletType != PalletTypeEnum.Empty.ObjToInt())
                {
                    _logger.LogInformation($"TaskService RelocationTaskCompleted: æœªæ‰¾åˆ°è¯¥æ‰˜ç›˜åº“存明细信息.{task.TaskNum}");
                    return WebResponseContent.Instance.Error($"未找到该托盘[{task.PalletCode}]库存明细信息");
                }
                // 3. æŸ¥è¯¢ç›®æ ‡è´§ä½+原货位信息
                Dt_LocationInfo targetLocationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == task.TargetAddress);
                if (targetLocationInfo == null)
                {
                    return content.Error($"未找到对应的终点货位[{task.TargetAddress}]信息");
                }
                // åŽŸè´§ä½ä¿¡æ¯
                Dt_LocationInfo oldLocationInfo = null;
                if (!string.IsNullOrEmpty(stockInfo.LocationCode))
                {
                    oldLocationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == stockInfo.LocationCode);
                    if (oldLocationInfo == null)
                    {
                        return content.Error($"未找到原货位[{stockInfo.LocationCode}]信息");
                    }
                }
                // 4. è´§ä½çŠ¶æ€æ ¡éªŒ
                if (targetLocationInfo.LocationStatus == LocationStatusEnum.InStock.ObjToInt())
                {
                    return WebResponseContent.Instance.Error($"目标货位[{task.TargetAddress}]状态不正确(当前为已占用)");
                }
                // 5. å¼€å¯äº‹åŠ¡å¤„ç†æ ¸å¿ƒé€»è¾‘
                _unitOfWorkManage.BeginTran();
                // 5.1 è®°å½•目标货位原状态,更新为占用
                var beforeTargetLocationStatus = targetLocationInfo.LocationStatus;
                targetLocationInfo.LocationStatus = stockInfo.PalletType == PalletTypeEnum.Empty.ObjToInt()
                        ? LocationStatusEnum.Pallet.ObjToInt()
                        : LocationStatusEnum.InStock.ObjToInt();
                _locationInfoService.Repository.UpdateData(targetLocationInfo);
                // 5.2 é‡Šæ”¾åŽŸè´§ä½
                int beforeOldLocationStatus = 0;
                if (oldLocationInfo != null)
                {
                    beforeOldLocationStatus = oldLocationInfo.LocationStatus;
                    // åŽŸè´§ä½æ¢å¤ä¸ºç©ºé—²
                    oldLocationInfo.LocationStatus = LocationStatusEnum.Free.ObjToInt();
                    _locationInfoService.Repository.UpdateData(oldLocationInfo);
                }
                // 5.4 æ›´æ–°åº“存主信息(绑定新货位)
                string oldLocationCode = stockInfo.LocationCode; // è®°å½•原货位
                stockInfo.LocationCode = targetLocationInfo.LocationCode; // ç»‘定目标货位
                stockInfo.PalletCode = task.PalletCode;
                stockInfo.StockStatus = StockStatusEmun.入库完成.ObjToInt();
                _stockRepository.UpdateData(stockInfo);
                // 5.5 æ›´æ–°ä»»åŠ¡çŠ¶æ€ä¸ºå®Œæˆ
                task.TaskStatus = TaskStatusEnum.Finish.ObjToInt();
                var result = _task_HtyService.DeleteAndMoveIntoHty(task, OperateTypeEnum.自动完成);
                // æäº¤äº‹åŠ¡
                _unitOfWorkManage.CommitTran();
                // ä»»åŠ¡å½’æ¡£å¤±è´¥åˆ™ç›´æŽ¥åˆ é™¤
                if (!result)
                {
                    await Db.Deleteable(task).ExecuteCommandAsync();
                }
                try
                {
                    // è®°å½•目标货位状态变更
                    _locationStatusChangeRecordService.AddLocationStatusChangeRecord(
                        targetLocationInfo,
                        beforeTargetLocationStatus,
                        StockChangeType.Inbound.ObjToInt(),
                        $"移库入库(原货位:{oldLocationCode})",
                        task.TaskNum);
                    // è®°å½•原货位状态变更(若有)
                    if (oldLocationInfo != null)
                    {
                        _locationStatusChangeRecordService.AddLocationStatusChangeRecord(
                            oldLocationInfo,
                            beforeOldLocationStatus,
                            StockChangeType.Outbound.ObjToInt(),
                            $"移库出库(目标货位:{targetLocationInfo.LocationCode})",
                            task.TaskNum);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogInformation($"RelocationTaskCompleted AddLocationStatusChangeRecord : {ex.Message} ");
                }
                return content.OK();
            }
            catch (Exception ex)
            {
                // äº‹åŠ¡å›žæ»š
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"RelocationTaskCompleted å¤„理失败:{ex.Message}", ex);
                return await Task.FromResult(WebResponseContent.Instance.Error($"移库任务处理失败:{ex.Message}"));
            }
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs
@@ -10,6 +10,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using WIDESEA_BasicService;
using WIDESEA_Common.CommonEnum;
using WIDESEA_Common.LocationEnum;
@@ -1243,7 +1244,7 @@
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                content.Error(ex.Message);
                return await Task.FromResult(WebResponseContent.Instance.Error(ex.Message));
            }
            return content;
        }
@@ -1309,6 +1310,137 @@
                return code;
            }
        }
        /// <summary>
        /// é€‰å®šåº“存同区域移库
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<WebResponseContent> AreaOutbound(List<StockViewDTO> stockViews)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
                List<int> ids = stockViews.Select(x => x.StockId).ToList();
                //获取库存
                List<Dt_StockInfo> stockInfos = _stockRepository.Db.Queryable<Dt_StockInfo>().Where(x => ids.Contains(x.Id)).Includes(x => x.Details).ToList();
                if (stockInfos.Count != stockViews.Count)
                {
                    StockViewDTO? stockViewDTO = stockViews.FirstOrDefault(x => !stockInfos.Select(x => x.PalletCode).ToList().Contains(x.PalletCode));
                    return content.Error($"未找到{stockViewDTO?.PalletCode}库存");
                }
                //获取货位
                List<string> locStrs = stockInfos.Select(x => x.LocationCode).ToList();
                List<Dt_LocationInfo> locationInfos = _locationInfoService.Db.Queryable<Dt_LocationInfo>().Where(x => locStrs.Contains(x.LocationCode)).ToList();
                if (stockInfos.Count != locationInfos.Count)
                {
                    string? locStr = locStrs.FirstOrDefault(x => !locationInfos.Select(x => x.LocationCode).ToList().Contains(x));
                    return content.Error($"未找到{locStr}货位数据");
                }
                foreach (var item in stockInfos)
                {
                    Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == item.LocationCode);
                    if (locationInfo == null || (locationInfo.EnableStatus == EnableStatusEnum.Disable.ObjToInt() || locationInfo.EnableStatus != EnableStatusEnum.Normal.ObjToInt()) || item.StockStatus != StockStatusEmun.入库完成.ObjToInt())
                    {
                        return content.Error($"{item.PalletCode}货位或库存状态不满足出库条件");
                    }
                }
                List<Dt_Task> tasks = AreaGetTasks(stockInfos, TaskTypeEnum.AreaRelocation);
                if (tasks == null || tasks.Count <= 0)
                {
                    return content.Error($"生成任务失败");
                }
                stockInfos.ForEach(x =>
                {
                    x.StockStatus = StockStatusEmun.出库锁定.ObjToInt();
                });
                tasks.ForEach(x =>
                {
                    x.OrderNo = "无单据移库";
                });
                locationInfos.ForEach(x =>
                {
                    x.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
                });
                _unitOfWorkManage.BeginTran();
                //更新库存状态
                _stockRepository.UpdateData(stockInfos);
                //新建任务
                BaseDal.AddData(tasks);
                _locationInfoService.UpdateData(locationInfos);
                _unitOfWorkManage.CommitTran();
                content.OK();
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return await Task.FromResult(WebResponseContent.Instance.Error(ex.Message));
            }
            return content;
        }
        public List<Dt_Task> AreaGetTasks(List<Dt_StockInfo> stockInfos, TaskTypeEnum taskType)
        {
            // ä½¿ç”¨ TransactionScope åŒ…裹整个操作,确保所有数据库操作在同一事务中
            using (var scope = new TransactionScope(TransactionScopeOption.Required,
                new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
            {
                try
                {
                    List<Dt_Task> tasks = new List<Dt_Task>();
                    List<Dt_LocationInfo> locationInfos = _locationInfoService.Repository.QueryData(
                        x => stockInfos.Select(x => x.LocationCode).Contains(x.LocationCode));
                    for (int i = 0; i < stockInfos.Count; i++)
                    {
                        Dt_StockInfo stockInfo = stockInfos[i];
                        if (stockInfo != null)
                        {
                            Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == stockInfo.LocationCode);
                            var newLocation = _locationInfoService.AssignLocation(stockInfo.LocationType);
                            if (newLocation == null)
                            {
                                throw new Exception($"在{stockInfo.PalletCode}时没有空闲库位可进行同区域移库,请检查或减少移库料箱");
                            }
                            if (!tasks.Exists(x => x.PalletCode == stockInfo.PalletCode))
                            {
                                Dt_Task task = new()
                                {
                                    CurrentAddress = stockInfo.LocationCode,
                                    Grade = 0,
                                    PalletCode = stockInfo.PalletCode,
                                    NextAddress = "",
                                    Roadway = locationInfo.RoadwayNo,
                                    SourceAddress = stockInfo.LocationCode,
                                    TargetAddress = newLocation.LocationCode,
                                    TaskStatus = TaskStatusEnum.New.ObjToInt(),
                                    TaskType = taskType.ObjToInt(),
                                    PalletType = stockInfo.PalletType,
                                    WarehouseId = stockInfo.WarehouseId,
                                };
                                tasks.Add(task);
                            }
                            newLocation.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
                            _locationInfoService.UpdateData(newLocation);
                        }
                    }
                    // æäº¤äº‹åŠ¡
                    scope.Complete();
                    return tasks;
                }
                catch (Exception ex)
                {
                    // TransactionScope ä¼šè‡ªåŠ¨å›žæ»šäº‹åŠ¡ï¼Œæ— éœ€æ‰‹åŠ¨ Rollback
                    throw new Exception(ex.Message);
                }
            }
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
@@ -132,5 +132,17 @@
        {
            return await Service.TaskCancel(taskCancel);
        }
        /// <summary>
        /// ç›˜ç‚¹åº“存出库
        /// </summary>
        /// <param name="stockViews"></param>
        /// <param name="outStation"></param>
        /// <returns></returns>
        [HttpPost, HttpGet, Route("AreaOutbound"), AllowAnonymous]
        public async Task<WebResponseContent> AreaOutbound([FromBody] List<StockViewDTO> stockViews)
        {
            return await Service.AreaOutbound(stockViews);
        }
    }
}