647556386
2025-12-05 70aa9b5ca249a84e5867912d551cdafbec76e54e
Merge branch 'master' of http://115.159.85.185:8098/r/ZhongRui/ALDbanyunxiangmu
已添加1个文件
已修改26个文件
3286 ■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/outOrderDetail.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/outboundOrder.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/outbound/BatchPickingConfirm.vue 1143 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/17.14.878.3237/CodeChunks.db-shm 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/17.14.878.3237/SemanticSymbols.db-shm 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/ErpApiService.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs 773 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_CheckService/ReCheckOrderService.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Common/TaskEnum/TaskStatusEnum.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Core/BaseRepository/RepositoryBase.cs 269 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITask_HtyService.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_PickingRecord.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/InterfaceLog.cs 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/NoStockOutModel.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs 143 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/WIDESEA_OutboundService.csproj 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 73 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs 459 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/Task_HtyService.cs 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/ESSController.cs 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Inbound/InboundOrderController.cs 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Filter/CustomProfile.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/outOrderDetail.vue
@@ -201,6 +201,12 @@
          width: 90,
        },
        {
          prop: "overOutQuantity",
          title: "挪料数量",
          type: "string",
          width: 90,
        },
        {
          prop: "unit",
          title: "单位",
          type: "string",
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/outboundOrder.js
@@ -386,7 +386,7 @@
      let wheres = [{
        'name': 'orderType',
        'value': '0',
        'value': ['0','116'],
        'displayType': 'text'
      }];
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/BatchPickingConfirm.vue
@@ -92,7 +92,7 @@
    </div>
    <!-- æ‹†åŒ…弹窗 -->
    <div v-if="showCustomSplitDialog" class="custom-dialog-overlay">
    <div v-if="showCustomSplitDialog" class="custom-dialog-overlay" style="z-index: 2000;">
      <div class="custom-dialog-wrapper">
        <div class="custom-dialog">
          <div class="custom-dialog-header">
@@ -154,7 +154,7 @@
    </div>
    <!-- æ’¤é”€æ‹†åŒ…弹窗 -->
    <div v-if="showRevertSplitDialog" class="custom-dialog-overlay">
    <div v-if="showRevertSplitDialog" class="custom-dialog-overlay"  style="z-index: 2001;">
      <div class="custom-dialog-wrapper">
        <div class="custom-dialog">
          <div class="custom-dialog-header">
@@ -214,7 +214,7 @@
    </div>
    <!-- æ‹†åŒ…链信息弹窗 -->
<div v-if="showSplitChainDialog" class="custom-dialog-overlay">
<div v-if="showSplitChainDialog" class="custom-dialog-overlay"  style="z-index: 2002;">
  <div class="custom-dialog-wrapper">
    <div class="custom-dialog" style="width: 750px;">
      <div class="custom-dialog-header">
@@ -338,7 +338,7 @@
</div>
    <!-- æ‰¹é‡å›žåº“弹窗 -->
    <div v-if="showBatchReturnDialog" class="custom-dialog-overlay">
    <div v-if="showBatchReturnDialog" class="custom-dialog-overlay"  style="z-index: 2003;">
      <div class="custom-dialog-wrapper">
        <div class="custom-dialog">
          <div class="custom-dialog-header">
@@ -370,7 +370,7 @@
    </div>
    <!-- å–走空箱弹窗 -->
    <div v-if="showEmptyPalletDialog" class="custom-dialog-overlay">
    <div v-if="showEmptyPalletDialog" class="custom-dialog-overlay"  style="z-index: 2004;">
      <div class="custom-dialog-wrapper">
        <div class="custom-dialog">
          <div class="custom-dialog-header">
@@ -405,11 +405,12 @@
  </div>
</template>
<script>
import http from '@/api/http.js'
import { ref, defineComponent } from "vue";
import { defineComponent } from "vue";
import { ElMessage } from 'element-plus' 
import { useRoute } from 'vue-router'
import printView from "@/extension/outbound/extend/printView.vue"
export default defineComponent({
@@ -417,6 +418,7 @@
  components: {printView},
  data() {
    return {
      // ä¿æŒæ‰€æœ‰åŽŸå§‹æ•°æ®ç»“æž„ä¸å˜...
      scanData: {
        orderNo: '',
        palletCode: '',
@@ -432,21 +434,24 @@
      },
      palletStatus: '未知',
      
      // å¼¹çª—状态
      // å¼¹çª—状态 - å…³é”®ä¿®å¤ï¼šåªå…è®¸ä¸€ä¸ªå¼¹çª—打开
      activeDialog: null, // 'split', 'revert', 'batchReturn', 'emptyPallet', 'splitChain'
      showCustomSplitDialog: false,
      showRevertSplitDialog: false,
      showBatchReturnDialog: false,
      showEmptyPalletDialog: false,
      showSplitChainDialog: false, // æ–°å¢žï¼šæ‹†åŒ…链信息弹窗
      showSplitChainDialog: false,
      
        // æ·»åŠ é˜²é‡å¤ç‚¹å‡»æ ‡å¿—
      isOpeningDialog: false,
      // åŠ è½½çŠ¶æ€
      splitLoading: false,
      revertSplitLoading: false,
      batchReturnLoading: false,
      emptypalletOutLoading: false,
      splitChainLoading: false, // æ–°å¢žï¼šæ‹†åŒ…链加载状态
      splitChainLoading: false,
      
      // è¡¨å•数据
      // è¡¨å•数据...
      splitForm: {
        orderNo: '',
        palletCode: '',
@@ -472,14 +477,13 @@
        palletCode: ''
      },
      
      // æ–°å¢žï¼šæ‹†åŒ…链相关数据
      splitChainInfo: {
        originalBarcode: '',
        totalSplitTimes: 0,
        splitChain: []
      },
      
      // éªŒè¯è§„则
      // éªŒè¯è§„则...
      splitFormRules: {
        originalBarcode: [
          { required: true, message: '请输入原条码', trigger: 'blur' }
@@ -505,8 +509,17 @@
      isProcessing: false
    }
  },
  watch: {
    // å…³é”®ä¿®å¤ï¼šç¡®ä¿åŒä¸€æ—¶é—´åªæœ‰ä¸€ä¸ªå¼¹çª—打开
    activeDialog(newVal, oldVal) {
      this.showCustomSplitDialog = newVal === 'split'
      this.showRevertSplitDialog = newVal === 'revert'
      this.showBatchReturnDialog = newVal === 'batchReturn'
      this.showEmptyPalletDialog = newVal === 'emptyPallet'
      this.showSplitChainDialog = newVal === 'splitChain'
    }
  },
  computed: {
    // æ˜¯å¦å¯ä»¥å–消整个拆包链
    canCancelWholeChain() {
      return this.splitChainInfo.splitChain && 
             this.splitChainInfo.splitChain.some(item => !item.isReverted);
@@ -519,9 +532,13 @@
      this.batchReturnForm.orderNo = this.$route.query.orderNo;
      this.emptypalletOutForm.orderNo = this.$route.query.orderNo;
    }
    this.$nextTick(() => {
      this.$refs.palletInput.focus();
    // ä½¿ç”¨ requestAnimationFrame ç¡®ä¿é¡µé¢å®Œå…¨åŠ è½½
    requestAnimationFrame(() => {
      if (this.$refs.palletInput) {
        this.$refs.palletInput.focus();
      }
    });
  },
  methods: {
    goBack(){
@@ -568,16 +585,445 @@
      }
    },
    // æ‹†åŒ…相关方法
    openSplitDialog() {
   openSplitDialog() {
      console.log('紧急修复版:打开拆包弹窗');
      if (!this.scanData.palletCode) {
        this.$message.warning('请先扫描托盘码');
        return;
      }
      this.showCustomSplitDialog = true;
      this.resetSplitForm();
      this.splitForm.orderNo = this.scanData.orderNo;
      this.splitForm.palletCode = this.scanData.palletCode;
      // 1. å…³é—­æ‰€æœ‰Vue弹窗
      this.closeAllDialogs();
      // 2. å¼ºåˆ¶ä»ŽDOM中移除所有弹窗
      setTimeout(() => {
        const dialogs = document.querySelectorAll('.custom-dialog-overlay');
        dialogs.forEach(dialog => {
          if (dialog.parentNode) {
            dialog.parentNode.removeChild(dialog);
          }
        });
        // å¦‚果已经存在手动弹窗,先移除
        if (this.manualDialog && this.manualDialog.parentNode) {
          this.manualDialog.parentNode.removeChild(this.manualDialog);
        }
        // 3. ç­‰å¾…一帧
        requestAnimationFrame(() => {
          // 4. ç›´æŽ¥åˆ›å»ºæ–°å¼¹çª—,不依赖Vue的响应式系统
          this.createManualSplitDialog();
        });
      }, 10);
    },
    // åˆ›å»ºæ‰‹åŠ¨æ‹†åŒ…å¼¹çª—
    createManualSplitDialog() {
      const newDialog = document.createElement('div');
      newDialog.className = 'custom-dialog-overlay emergency-fix';
      // ç”ŸæˆéšæœºID用于事件绑定
      const dialogId = 'manual-dialog-' + Date.now();
      newDialog.id = dialogId;
      // å­˜å‚¨å¼•用
      this.manualDialog = newDialog;
      // å¼¹çª—内容
      newDialog.innerHTML = `
        <div class="custom-dialog-wrapper">
          <div class="custom-dialog" style="width: 500px;">
            <div class="custom-dialog-header">
              <h3 style="margin: 0; color: #303133;">拆包操作</h3>
              <button class="close-button" onclick="document.getElementById('${dialogId}').remove()" style="
                font-size: 18px;
                color: #909399;
                padding: 0;
                width: 24px;
                height: 24px;
                display: flex;
                align-items: center;
                justify-content: center;
                background: none;
                border: none;
                cursor: pointer;
              ">×</button>
            </div>
            <div class="custom-dialog-body" style="padding: 20px;">
              <div style="margin-bottom: 15px;">
                <div style="display: flex; align-items: center; margin-bottom: 5px;">
                  <span style="width: 100px; text-align: right; padding-right: 12px; color: #606266;">订单编号:</span>
                  <input type="text" value="${this.scanData.orderNo}" disabled style="
                    flex: 1;
                    padding: 8px 12px;
                    border: 1px solid #dcdfe6;
                    border-radius: 4px;
                    background-color: #f5f7fa;
                    color: #909399;
                  ">
                </div>
              </div>
              <div style="margin-bottom: 15px;">
                <div style="display: flex; align-items: center; margin-bottom: 5px;">
                  <span style="width: 100px; text-align: right; padding-right: 12px; color: #606266;">托盘编号:</span>
                  <input type="text" value="${this.scanData.palletCode}" disabled style="
                    flex: 1;
                    padding: 8px 12px;
                    border: 1px solid #dcdfe6;
                    border-radius: 4px;
                    background-color: #f5f7fa;
                    color: #909399;
                  ">
                </div>
              </div>
              <div style="margin-bottom: 15px;">
                <div style="display: flex; align-items: center; margin-bottom: 5px;">
                  <span style="width: 100px; text-align: right; padding-right: 12px; color: #606266;">原条码:</span>
                  <div style="flex: 1; display: flex; align-items: center; gap: 10px;">
                    <input type="text" id="${dialogId}-barcode" placeholder="扫描原条码" style="
                      flex: 1;
                      padding: 8px 12px;
                      border: 1px solid #dcdfe6;
                      border-radius: 4px;
                    ">
                    <button id="${dialogId}-viewChain" style="
                      padding: 8px 16px;
                      background: #409eff;
                      color: white;
                      border: none;
                      border-radius: 4px;
                      cursor: pointer;
                      white-space: nowrap;
                    ">查看拆包链</button>
                  </div>
                </div>
              </div>
              <div style="margin-bottom: 15px;">
                <div style="display: flex; align-items: center; margin-bottom: 5px;">
                  <span style="width: 100px; text-align: right; padding-right: 12px; color: #606266;">物料编码:</span>
                  <input type="text" id="${dialogId}-materiel" disabled style="
                    flex: 1;
                    padding: 8px 12px;
                    border: 1px solid #dcdfe6;
                    border-radius: 4px;
                    background-color: #f5f7fa;
                    color: #909399;
                  ">
                </div>
              </div>
              <div style="margin-bottom: 15px;">
                <div style="display: flex; align-items: center; margin-bottom: 5px;">
                  <span style="width: 100px; text-align: right; padding-right: 12px; color: #606266;">剩余数量:</span>
                  <input type="text" id="${dialogId}-remain" disabled style="
                    flex: 1;
                    padding: 8px 12px;
                    border: 1px solid #dcdfe6;
                    border-radius: 4px;
                    background-color: #f5f7fa;
                    color: #909399;
                  ">
                </div>
              </div>
              <div style="margin-bottom: 15px;">
                <div style="display: flex; align-items: center; margin-bottom: 5px;">
                  <span style="width: 100px; text-align: right; padding-right: 12px; color: #606266;">拆包数量:</span>
                  <div style="flex: 1;">
                    <input type="number" id="${dialogId}-splitQty" value="1" min="0.01" step="0.01" style="
                      width: 100%;
                      padding: 8px 12px;
                      border: 1px solid #dcdfe6;
                      border-radius: 4px;
                    ">
                  </div>
                </div>
              </div>
            </div>
            <div class="custom-dialog-footer" style="
              padding: 10px 20px 20px;
              text-align: right;
              border-top: 1px solid #ebeef5;
            ">
              <button id="${dialogId}-cancel" style="
                padding: 9px 15px;
                background: white;
                color: #606266;
                border: 1px solid #dcdfe6;
                border-radius: 4px;
                cursor: pointer;
                margin-right: 10px;
              ">取消</button>
              <button id="${dialogId}-confirm" style="
                padding: 9px 15px;
                background: #409eff;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
              ">确认拆包</button>
            </div>
          </div>
        </div>
      `;
      // æ·»åŠ æ ·å¼
      newDialog.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: rgba(0,0,0,0.5);
        display: flex;
        align-items: center;
        justify-content: center;
        z-index: 999999;
      `;
      // å¼¹çª—容器样式
      const wrapper = newDialog.querySelector('.custom-dialog-wrapper');
      if (wrapper) {
        wrapper.style.position = 'relative';
        wrapper.style.zIndex = '1000000';
      }
      // å¼¹çª—内容样式
      const dialog = newDialog.querySelector('.custom-dialog');
      if (dialog) {
        dialog.style.background = 'white';
        dialog.style.borderRadius = '4px';
        dialog.style.maxWidth = '90vw';
        dialog.style.maxHeight = '90vh';
        dialog.style.boxShadow = '0 2px 12px 0 rgba(0, 0, 0, 0.1)';
        dialog.style.overflow = 'auto';
      }
      // å¼¹çª—头部样式
      const header = newDialog.querySelector('.custom-dialog-header');
      if (header) {
        header.style.display = 'flex';
        header.style.justifyContent = 'space-between';
        header.style.alignItems = 'center';
        header.style.padding = '20px 20px 10px';
        header.style.borderBottom = '1px solid #ebeef5';
      }
      document.body.appendChild(newDialog);
      console.log('紧急弹窗已创建');
      // ç»‘定事件
      this.bindManualDialogEvents(dialogId);
      // è‡ªåŠ¨èšç„¦åˆ°æ¡ç è¾“å…¥æ¡†
      setTimeout(() => {
        const barcodeInput = document.getElementById(`${dialogId}-barcode`);
        if (barcodeInput) {
          barcodeInput.focus();
          // æ·»åŠ å›žè½¦é”®ç›‘å¬
          barcodeInput.addEventListener('keyup', (event) => {
            if (event.key === 'Enter') {
              this.onManualSplitBarcodeScan(dialogId);
            }
          });
        }
      }, 100);
    },
    // ç»‘定手动弹窗事件
    bindManualDialogEvents(dialogId) {
      const vm = this; // ä¿å­˜Vue实例引用
      // æŸ¥çœ‹æ‹†åŒ…链按钮
      const viewChainBtn = document.getElementById(`${dialogId}-viewChain`);
      if (viewChainBtn) {
        viewChainBtn.onclick = () => {
          const barcodeInput = document.getElementById(`${dialogId}-barcode`);
          if (barcodeInput && barcodeInput.value.trim()) {
            vm.viewSplitChainFromManualDialog(barcodeInput.value.trim(), dialogId);
          } else {
            ElMessage.warning('请先输入条码');
          }
        };
      }
      // å–消按钮
      const cancelBtn = document.getElementById(`${dialogId}-cancel`);
      if (cancelBtn) {
        cancelBtn.onclick = () => {
          const dialog = document.getElementById(dialogId);
          if (dialog && dialog.parentNode) {
            dialog.parentNode.removeChild(dialog);
          }
        };
      }
      // ç¡®è®¤æ‹†åŒ…按钮
      const confirmBtn = document.getElementById(`${dialogId}-confirm`);
      if (confirmBtn) {
        confirmBtn.onclick = () => {
          vm.handleManualSplitPackage(dialogId);
        };
      }
      // æ¡ç è¾“入框变化事件
      const barcodeInput = document.getElementById(`${dialogId}-barcode`);
      if (barcodeInput) {
        // é˜²æŠ–处理
        let timeout;
        barcodeInput.addEventListener('input', () => {
          clearTimeout(timeout);
          timeout = setTimeout(() => {
            if (barcodeInput.value.trim()) {
              vm.onManualSplitBarcodeScan(dialogId);
            }
          }, 500);
        });
      }
    },
    // æ‰‹åŠ¨å¼¹çª—çš„æ¡ç æ‰«æå¤„ç†
    async onManualSplitBarcodeScan(dialogId) {
      const barcodeInput = document.getElementById(`${dialogId}-barcode`);
      if (!barcodeInput || !barcodeInput.value.trim()) return;
      const barcode = barcodeInput.value.trim();
      try {
        const res = await http.post('/api/OutboundBatchPicking/split-package-info', {
          orderNo: this.scanData.orderNo,
          palletCode: this.scanData.palletCode,
          barcode: barcode
        });
        if (res.status) {
          // æ›´æ–°ç‰©æ–™ç¼–码
          const materielInput = document.getElementById(`${dialogId}-materiel`);
          if (materielInput) {
            materielInput.value = res.data.materielCode || '';
          }
          // æ›´æ–°å‰©ä½™æ•°é‡
          const remainInput = document.getElementById(`${dialogId}-remain`);
          if (remainInput) {
            remainInput.value = res.data.remainQuantity || 0;
          }
          // æ›´æ–°æ‹†åŒ…数量(默认为1,不超过剩余数量)
          const splitQtyInput = document.getElementById(`${dialogId}-splitQty`);
          if (splitQtyInput) {
            const maxQty = res.data.remainQuantity || 0;
            splitQtyInput.max = maxQty;
            const currentVal = parseFloat(splitQtyInput.value) || 1;
            if (currentVal > maxQty) {
              splitQtyInput.value = Math.min(1, maxQty);
            }
          }
        } else {
          ElMessage.error(res.message || '获取拆包信息失败');
        }
      } catch (error) {
        console.error('获取拆包信息失败:', error);
        ElMessage.error('获取拆包信息失败');
      }
    },
    // ä»Žæ‰‹åŠ¨å¼¹çª—æŸ¥çœ‹æ‹†åŒ…é“¾
    viewSplitChainFromManualDialog(barcode, dialogId) {
      // å…ˆå…³é—­æ‰‹åŠ¨å¼¹çª—
      const dialog = document.getElementById(dialogId);
      if (dialog && dialog.parentNode) {
        dialog.parentNode.removeChild(dialog);
      }
      // å»¶è¿Ÿä¸€ä¸‹ï¼Œç„¶åŽæ‰“å¼€Vue的拆包链弹窗
      setTimeout(() => {
        this.viewSplitChain(barcode);
      }, 50);
    },
    // å¤„理手动弹窗的拆包操作
    async handleManualSplitPackage(dialogId) {
      const barcodeInput = document.getElementById(`${dialogId}-barcode`);
      const splitQtyInput = document.getElementById(`${dialogId}-splitQty`);
      if (!barcodeInput || !barcodeInput.value.trim()) {
        ElMessage.warning('请输入原条码');
        return;
      }
      if (!splitQtyInput || !splitQtyInput.value || parseFloat(splitQtyInput.value) <= 0) {
        ElMessage.warning('请输入有效的拆包数量');
        return;
      }
      const originalBarcode = barcodeInput.value.trim();
      const splitQuantity = parseFloat(splitQtyInput.value);
      try {
        // æ˜¾ç¤ºåŠ è½½çŠ¶æ€
        const confirmBtn = document.getElementById(`${dialogId}-confirm`);
        if (confirmBtn) {
          confirmBtn.disabled = true;
          confirmBtn.textContent = '处理中...';
        }
        const res = await http.post('/api/OutboundBatchPicking/split-package', {
          orderNo: this.scanData.orderNo,
          palletCode: this.scanData.palletCode,
          originalBarcode: originalBarcode,
          splitQuantity: splitQuantity
        });
        if (res.status) {
          ElMessage.success('拆包成功');
          // å…³é—­æ‰‹åŠ¨å¼¹çª—
          const dialog = document.getElementById(dialogId);
          if (dialog && dialog.parentNode) {
            dialog.parentNode.removeChild(dialog);
          }
          // é‡æ–°åŠ è½½æ•°æ®
          await this.loadPalletData();
        } else {
          ElMessage.error(res.message || '拆包失败');
          // æ¢å¤æŒ‰é’®çŠ¶æ€
          if (confirmBtn) {
            confirmBtn.disabled = false;
            confirmBtn.textContent = '确认拆包';
          }
        }
      } catch (error) {
        console.error('拆包失败:', error);
        ElMessage.error('拆包失败');
        // æ¢å¤æŒ‰é’®çŠ¶æ€
        const confirmBtn = document.getElementById(`${dialogId}-confirm`);
        if (confirmBtn) {
          confirmBtn.disabled = false;
          confirmBtn.textContent = '确认拆包';
        }
      }
    },
      closeAllDialogsImmediately() {
      console.log('立即关闭所有弹窗');
      // ç›´æŽ¥è®¾ç½®ä¸º false,不等待任何异步操作
      this.showCustomSplitDialog = false;
      this.showRevertSplitDialog = false;
      this.showBatchReturnDialog = false;
      this.showEmptyPalletDialog = false;
      this.showSplitChainDialog = false;
      // å¼ºåˆ¶DOM更新
      this.$forceUpdate();
    },
    async onSplitBarcodeScan() {
@@ -620,7 +1066,7 @@
              });
              if (res.status) {
                this.$message.success('拆包成功');
                this.showCustomSplitDialog = false;
                this.closeAllDialogs();
                await this.loadPalletData();
              } else {
                this.$message.error(res.message || '拆包失败');
@@ -634,28 +1080,24 @@
        });
      }
    },
// åœ¨æ‹†åŒ…弹窗中查看拆包链
async viewSplitChainFromSplit(barcode) {
  if (!barcode) {
    this.$message.warning('请先输入条码');
    return;
  }
  // å…ˆå…³é—­æ‹†åŒ…弹窗
  this.closeCustomSplitDialog();
  await this.$nextTick();
  // ç„¶åŽæ‰“开拆包链信息弹窗
  await this.viewSplitChain(barcode);
},
    async viewSplitChainFromSplit(barcode) {
      if (!barcode) {
        this.$message.warning('请先输入条码');
        return;
      }
      this.closeAllDialogs();
      setTimeout(() => {
        this.viewSplitChain(barcode);
      }, 50);
    },
    // æ’¤é”€æ‹†åŒ…
    async onRevertSplitBarcodeScan() {
      if (!this.revertSplitForm.newBarcode) return;
      this.revertSplitForm.newBarcode = this.revertSplitForm.newBarcode.replace(/\n/g, '').trim();
      // æ–°å¢žï¼šæ‰«æåŽè‡ªåŠ¨æ˜¾ç¤ºæ‹†åŒ…é“¾ä¿¡æ¯
      await this.viewSplitChain(this.revertSplitForm.newBarcode);
    },
    async handleRevertSplit() {
@@ -671,7 +1113,7 @@
              });
              if (res.status) {
                this.$message.success('撤销拆包成功');
                this.showRevertSplitDialog = false;
                this.closeAllDialogs();
                await this.loadPalletData();
              } else {
                this.$message.error(res.message || '撤销拆包失败');
@@ -685,216 +1127,140 @@
        });
      }
    },
// æŸ¥æ‰¾å®Œæ•´æ‹†åŒ…链(从根条码开始)
async findRootChain(currentBarcode) {
  this.splitChainLoading = true;
  try {
    const res = await http.post('/api/OutboundBatchPicking/find-root-split-chain', {
      orderNo: this.scanData.orderNo,
      barcode: currentBarcode
    });
    if (res.status) {
      this.splitChainInfo = res.data;
      this.$message.success('已加载完整拆包链');
    } else {
      this.$message.error(res.message || '查找完整拆包链失败');
    }
  } catch (error) {
    this.$message.error('查找完整拆包链失败');
  } finally {
    this.splitChainLoading = false;
  }
},
    // æ‹†åŒ…链相关方法
   // æŸ¥çœ‹æ‹†åŒ…链信息
async viewSplitChain(barcode) {
  if (!barcode) {
    this.$message.warning('请先输入条码');
    return;
  }
  this.splitChainLoading = true;
  try {
    const res = await http.post('/api/OutboundBatchPicking/split-package-chain-info', {
      orderNo: this.scanData.orderNo,
      barcode: barcode
    });
    if (res.status) {
      this.splitChainInfo = res.data;
      // æ˜¾ç¤ºæç¤ºä¿¡æ¯ï¼Œå‘Šè¯‰ç”¨æˆ·è¿™æ˜¯ä»€ä¹ˆç±»åž‹çš„æ‹†åŒ…链
      let chainType = "当前条码的拆包链";
      if (this.splitChainInfo.chainType === 'root') {
        chainType = "完整拆包链(从原始条码开始)";
      } else if (this.splitChainInfo.chainType === 'branch') {
        chainType = "分支拆包链";
    async findRootChain(currentBarcode) {
      this.splitChainLoading = true;
      try {
        const res = await http.post('/api/OutboundBatchPicking/find-root-split-chain', {
          orderNo: this.scanData.orderNo,
          barcode: currentBarcode
        });
        if (res.status) {
          this.splitChainInfo = res.data;
          this.$message.success('已加载完整拆包链');
        } else {
          this.$message.error(res.message || '查找完整拆包链失败');
        }
      } catch (error) {
        this.$message.error('查找完整拆包链失败');
      } finally {
        this.splitChainLoading = false;
      }
    },
    // æŸ¥çœ‹æ‹†åŒ…链信息
    async viewSplitChain(barcode) {
      if (!barcode) {
        this.$message.warning('请先输入条码');
        return;
      }
      
      this.$message.info(`已加载${chainType},共${this.splitChainInfo.totalSplitTimes}次拆包`);
      this.showSplitChainDialog = true;
    } else {
      this.$message.error(res.message || '获取拆包链信息失败');
    }
  } catch (error) {
    this.$message.error('获取拆包链信息失败');
  } finally {
    this.splitChainLoading = false;
  }
},
      this.splitChainLoading = true;
      try {
        const res = await http.post('/api/OutboundBatchPicking/split-package-chain-info', {
          orderNo: this.scanData.orderNo,
          barcode: barcode
        });
        if (res.status) {
          this.splitChainInfo = res.data;
          this.activeDialog = 'splitChain';
        } else {
          this.$message.error(res.message || '获取拆包链信息失败');
        }
      } catch (error) {
        this.$message.error('获取拆包链信息失败');
      } finally {
        this.splitChainLoading = false;
      }
    },
    // å…³é—­æ‹†åŒ…链信息弹窗
    closeSplitChainDialog() {
     closeSplitChainDialog() {
      this.showSplitChainDialog = false;
    },
    // åœ¨æ’¤é”€æ‹†åŒ…弹窗中查看拆包链
async viewSplitChainFromRevert(barcode) {
  if (!barcode) {
    this.$message.warning('请先输入条码');
    return;
  }
  // å…ˆå…³é—­æ’¤é”€æ‹†åŒ…弹窗
  this.closeRevertSplitDialog();
  await this.$nextTick();
  // ç„¶åŽæ‰“开拆包链信息弹窗
  await this.viewSplitChain(barcode);
},
// å¿«é€Ÿé‡æ–°æ‰“开拆包链弹窗
async quickReopenSplitChainDialog(barcode) {
  if (!barcode) return;
  this.showSplitChainDialog = true;
  this.splitChainLoading = true;
  try {
    const res = await http.post('/api/OutboundBatchPicking/split-package-chain-info', {
      orderNo: this.scanData.orderNo,
      barcode: barcode
    });
    if (res.status) {
      this.splitChainInfo = res.data;
    }
  } catch (error) {
    console.error('重新加载拆包链信息失败:', error);
  } finally {
    this.splitChainLoading = false;
  }
},
    // å–消单个拆包记录
async cancelSingleSplit(newBarcode) {
  // å…ˆè®°å½•当前信息,然后关闭弹窗
  const originalBarcode = this.splitChainInfo.originalBarcode;
  this.closeSplitChainDialog();
  await this.$nextTick();
  try {
    await this.$confirm(
      `确定要取消条码 ${newBarcode} çš„æ‹†åŒ…操作吗?`,
      '取消单个拆包',
      {
        confirmButtonText: '确定取消',
        cancelButtonText: '再想想',
        type: 'warning'
      }
    );
    this.revertSplitLoading = true;
    const res = await http.post('/api/OutboundBatchPicking/cancel-split', {
      orderNo: this.scanData.orderNo,
      palletCode: this.scanData.palletCode,
      newBarcode: newBarcode
    });
    if (res.status) {
      this.$message.success('取消拆包成功');
      await this.loadPalletData();
      // é‡æ–°æ‰“开弹窗显示更新后的状态
      await this.viewSplitChain(originalBarcode);
    } else {
      this.$message.error(res.message || '取消拆包失败');
      await this.viewSplitChain(originalBarcode);
    }
  } catch (error) {
    if (error === 'cancel') {
      // ç”¨æˆ·å–消后重新打开弹窗
      await this.viewSplitChain(originalBarcode);
    } else {
      this.$message.error('取消拆包失败');
      await this.viewSplitChain(originalBarcode);
    }
  } finally {
    this.revertSplitLoading = false;
  }
},
// å–消整个拆包链
async cancelWholeSplitChain() {
  // å…ˆè®°å½•当前拆包链信息,然后关闭弹窗
  const originalBarcode = this.splitChainInfo.originalBarcode;
  this.closeSplitChainDialog();
  // ç»™ä¸€ç‚¹æ—¶é—´è®©å¼¹çª—完全关闭
  await this.$nextTick();
  try {
    // çŽ°åœ¨æ˜¾ç¤ºç¡®è®¤å¯¹è¯æ¡†ï¼Œç¡®ä¿å®ƒåœ¨æœ€å‰é¢
    await this.$confirm(
      `确定要取消整个拆包链吗?\n这将取消从条码 ${originalBarcode} å¼€å§‹çš„æ‰€æœ‰æ‹†åŒ…操作。`,
      '取消拆包链确认',
      {
        confirmButtonText: '确定取消',
        cancelButtonText: '再想想',
        type: 'warning',
        center: true,
        closeOnClickModal: false
    // å–消单个拆包记录
    async cancelSingleSplit(newBarcode) {
      const originalBarcode = this.splitChainInfo.originalBarcode;
      try {
        await this.$confirm(
          `确定要取消条码 ${newBarcode} çš„æ‹†åŒ…操作吗?`,
          '取消单个拆包',
          {
            confirmButtonText: '确定取消',
            cancelButtonText: '再想想',
            type: 'warning'
          }
        );
        this.revertSplitLoading = true;
        const res = await http.post('/api/OutboundBatchPicking/cancel-split', {
          orderNo: this.scanData.orderNo,
          palletCode: this.scanData.palletCode,
          newBarcode: newBarcode
        });
        if (res.status) {
          this.$message.success('取消拆包成功');
          await this.loadPalletData();
          this.closeAllDialogs();
          setTimeout(() => {
            this.viewSplitChain(originalBarcode);
          }, 50);
        } else {
          this.$message.error(res.message || '取消拆包失败');
        }
      } catch (error) {
        if (error !== 'cancel') {
          this.$message.error('取消拆包失败');
        }
      } finally {
        this.revertSplitLoading = false;
      }
    );
    // ç”¨æˆ·ç¡®è®¤åŽæ‰§è¡Œå–消操作
    this.revertSplitLoading = true;
    const res = await http.post('/api/OutboundBatchPicking/cancel-split-chain', {
      orderNo: this.scanData.orderNo,
      palletCode: this.scanData.palletCode,
      startBarcode: originalBarcode
    });
    console.log('取消拆包链响应:', res);
    if (res.status) {
      this.$message.success('取消拆包链成功');
      await this.loadPalletData();
      // å¯é€‰ï¼šé‡æ–°æ‰“开拆包链信息弹窗显示更新后的状态
      // await this.viewSplitChain(originalBarcode);
    } else {
      this.$message.error(res.message || '取消拆包链失败');
      // å¤±è´¥åŽé‡æ–°æ‰“开弹窗
      await this.viewSplitChain(originalBarcode);
    }
  } catch (error) {
    // ç”¨æˆ·å–消操作
    if (error === 'cancel') {
      console.log('用户取消了拆包链操作');
      // ç”¨æˆ·å–消后重新打开弹窗
      await this.viewSplitChain(originalBarcode);
    } else {
      console.error('取消拆包链错误:', error);
      this.$message.error('取消拆包链失败: ' + error.message);
      // å‡ºé”™åŽé‡æ–°æ‰“开弹窗
      await this.viewSplitChain(originalBarcode);
    }
  } finally {
    this.revertSplitLoading = false;
  }
},
    },
    // å–消整个拆包链
    async cancelWholeSplitChain() {
      try {
        await this.$confirm(
          `确定要取消整个拆包链吗?\n这将取消从条码 ${this.splitChainInfo.originalBarcode} å¼€å§‹çš„æ‰€æœ‰æ‹†åŒ…操作。`,
          '取消拆包链确认',
          {
            confirmButtonText: '确定取消',
            cancelButtonText: '再想想',
            type: 'warning',
            center: true,
            closeOnClickModal: false
          }
        );
        this.revertSplitLoading = true;
        const res = await http.post('/api/OutboundBatchPicking/cancel-split-chain', {
          orderNo: this.scanData.orderNo,
          palletCode: this.scanData.palletCode,
          startBarcode: this.splitChainInfo.originalBarcode
        });
        if (res.status) {
          this.$message.success('取消拆包链成功');
          this.closeAllDialogs();
          await this.loadPalletData();
        } else {
          this.$message.error(res.message || '取消拆包链失败');
        }
      } catch (error) {
        if (error !== 'cancel') {
          this.$message.error('取消拆包链失败: ' + error.message);
        }
      } finally {
        this.revertSplitLoading = false;
      }
    },
    // æ£€æŸ¥æ¡ç æ˜¯å¦å·²è¢«åˆ†æ‹£
    hasPicked(barcode) {
@@ -908,17 +1274,58 @@
      return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`;
    },
    // å…³é”®ä¿®å¤ï¼šæ–°å¢žå…³é—­æ‰€æœ‰å¼¹çª—的方法
    closeAllDialogs() {
      this.activeDialog = null;
      // ç¡®ä¿æ‰€æœ‰å¼¹çª—状态都被重置
// å…³é—­Vue弹窗
      this.showCustomSplitDialog = false;
      this.showRevertSplitDialog = false;
      this.showBatchReturnDialog = false;
      this.showEmptyPalletDialog = false;
      this.showSplitChainDialog = false;
      // å…³é—­æ‰‹åŠ¨å¼¹çª—
      if (this.manualDialog && this.manualDialog.parentNode) {
        this.manualDialog.parentNode.removeChild(this.manualDialog);
        this.manualDialog = null;
      }
      // ç§»é™¤æ‰€æœ‰ç´§æ€¥å¼¹çª—
      const emergencyDialogs = document.querySelectorAll('.emergency-fix');
      emergencyDialogs.forEach(dialog => {
        if (dialog.parentNode) {
          dialog.parentNode.removeChild(dialog);
        }
      });
    },
    // å›žåº“相关方法
    openBatchReturnDialog() {
      if (!this.scanData.palletCode) {
        this.$message.warning('请先扫描托盘码');
        return;
      }
      this.showBatchReturnDialog = true;
      this.batchReturnForm.orderNo = this.scanData.orderNo;
      this.batchReturnForm.palletCode = this.scanData.palletCode;
      this.batchReturnForm.unpickedCount = this.summary.unpickedCount;
      this.batchReturnForm.unpickedQuantity = this.summary.unpickedQuantity;
      if (this.isOpeningDialog) return;
      this.isOpeningDialog = true;
      setTimeout(() => {
        this.closeAllDialogsImmediately();
        requestAnimationFrame(() => {
          this.showBatchReturnDialog = true;
          this.batchReturnForm.orderNo = this.scanData.orderNo;
          this.batchReturnForm.palletCode = this.scanData.palletCode;
          this.batchReturnForm.unpickedCount = this.summary.unpickedCount;
          this.batchReturnForm.unpickedQuantity = this.summary.unpickedQuantity;
          this.$nextTick(() => {
            this.isOpeningDialog = false;
          });
        });
      }, 0);
    },
    async handleBatchReturnConfirm() {
@@ -930,7 +1337,7 @@
        });
        if (res.status) {
          this.$message.success('回库成功');
          this.showBatchReturnDialog = false;
          this.closeAllDialogs();
          await this.loadPalletData();
        } else {
          this.$message.error(res.message || '回库失败');
@@ -943,10 +1350,24 @@
    },
    // å–空箱方法
    handleEmptyPallet() {
      this.showEmptyPalletDialog = true;
      this.emptypalletOutForm.orderNo = this.scanData.orderNo;
      this.emptypalletOutForm.palletCode = '';
handleEmptyPallet() {
      if (this.isOpeningDialog) return;
      this.isOpeningDialog = true;
      setTimeout(() => {
        this.closeAllDialogsImmediately();
        requestAnimationFrame(() => {
          this.showEmptyPalletDialog = true;
          this.emptypalletOutForm.orderNo = this.scanData.orderNo;
          this.emptypalletOutForm.palletCode = '';
          this.$nextTick(() => {
            this.isOpeningDialog = false;
          });
        });
      }, 0);
    },
    async handleEmptyPalletConfirm() {
@@ -958,7 +1379,7 @@
        });
        if (res.status) {
          this.$message.success('取走空箱成功');
          this.showEmptyPalletDialog = false;
          this.closeAllDialogs();
          await this.loadPalletData();
        } else {
          this.$message.error(res.message || '取走空箱失败');
@@ -974,9 +1395,13 @@
    async loadPalletData() {
      if (!this.scanData.orderNo || !this.scanData.palletCode) return;
      
      await this.loadUnpickedList();
      await this.loadPickedList();
      await this.loadPalletStatus();
      try {
        await this.loadUnpickedList();
        await this.loadPickedList();
        await this.loadPalletStatus();
      } catch (error) {
        console.error('加载托盘数据失败:', error);
      }
    },
    async loadUnpickedList() {
@@ -986,7 +1411,6 @@
          palletCode: this.scanData.palletCode
        });
        if (res.status) {
          //this.unpickedList = res.data || [];
          this.unpickedList = (res.data || []).filter(item => item.canPick === true);
          this.summary.unpickedCount = this.unpickedList.length;
          this.summary.unpickedQuantity = this.unpickedList.reduce((sum, item) => sum + (item.remainQuantity || 0), 0);
@@ -1003,16 +1427,10 @@
          palletCode: this.scanData.palletCode
        });
        if (res.status) {
          this.pickedList = res.data || [];
          this.pickedList = res.data.map(item => {
  // æ–¹å¼1:保留原barcode字段,新增currentBarcode
  return {
    ...item,
    currentBarcode: item.barcode
  };
});
          this.pickedList = res.data.map(item => ({
            ...item,
            currentBarcode: item.barcode
          }));
          this.summary.pickedCount = this.pickedList.length;
        }
      } catch (error) {
@@ -1116,17 +1534,31 @@
      this.resetSplitForm();
    },
    openRevertSplitDialog() {
      this.showRevertSplitDialog = true;
      this.revertSplitForm.newBarcode = '';
openRevertSplitDialog() {
      if (this.isOpeningDialog) return;
      this.isOpeningDialog = true;
      setTimeout(() => {
        this.closeAllDialogsImmediately();
        requestAnimationFrame(() => {
          this.showRevertSplitDialog = true;
          this.revertSplitForm.newBarcode = '';
          this.$nextTick(() => {
            this.isOpeningDialog = false;
          });
        });
      }, 0);
    },
    closeRevertSplitDialog() {
  closeRevertSplitDialog() {
      this.showRevertSplitDialog = false;
      this.revertSplitForm.newBarcode = '';
    },
    closeBatchReturnDialog() {
 closeBatchReturnDialog() {
      this.showBatchReturnDialog = false;
    },
@@ -1135,7 +1567,7 @@
      this.emptypalletOutForm.palletCode = this.emptypalletOutForm.palletCode.replace(/\n/g, '').trim();
    },
    closeEmptyPalletDialog() {
     closeEmptyPalletDialog() {
      this.showEmptyPalletDialog = false;
      this.emptypalletOutForm.palletCode = '';
    },
@@ -1182,19 +1614,7 @@
  color: #909399;
}
/* è‡ªå®šä¹‰å¼¹çª—样式 */
:deep(.el-message-box) {
  z-index: 10010 !important;
}
:deep(.el-overlay) {
  z-index: 10009 !important;
}
:deep(.el-message) {
  z-index: 10011 !important;
}
/* è‡ªå®šä¹‰å¼¹çª—样式 - å…³é”®ä¿®å¤ */
.custom-dialog-overlay {
  position: fixed;
  top: 0;
@@ -1205,7 +1625,130 @@
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000; /* ä¿æŒä¸€ä¸ªåˆç†çš„ z-index */
  z-index: 9999; /* æé«˜z-index确保在最上层 */
}
.custom-dialog-wrapper {
  position: relative;
  z-index: 10000;
}
.custom-dialog {
  background: white;
  border-radius: 4px;
  width: 500px;
  max-width: 90vw;
  max-height: 90vh;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  overflow: auto;
}
.custom-dialog-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20px 20px 10px;
  border-bottom: 1px solid #ebeef5;
}
.custom-dialog-header h3 {
  margin: 0;
  color: #303133;
}
.close-button {
  font-size: 18px;
  color: #909399;
  padding: 0;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.close-button:hover {
  color: #409EFF;
  background-color: transparent;
}
.custom-dialog-body {
  padding: 20px;
}
.custom-dialog-footer {
  padding: 10px 20px 20px;
  text-align: right;
  border-top: 1px solid #ebeef5;
}
.custom-dialog-footer .el-button {
  margin-left: 10px;
}
@media (max-width: 768px) {
  .custom-dialog {
    width: 95vw;
    margin: 10px;
  }
  .scanner-form {
    flex-direction: column;
    align-items: stretch;
  }
  .scanner-form .el-input {
    width: 100%;
  }
}
/* åŽŸæœ‰çš„æ ·å¼ä¿æŒä¸å˜ */
.OutboundPicking-container {
  padding: 20px;
}
.scanner-form {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}
.scanner-form .el-input {
  width: 200px;
}
.summary-info {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
}
.table-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
  padding: 0 10px;
}
.selection-count {
  font-size: 12px;
  color: #909399;
}
/* åŽŸæœ‰çš„è‡ªå®šä¹‰å¼¹çª—æ ·å¼ */
.custom-dialog-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
}
.custom-dialog-wrapper {
@@ -1276,16 +1819,22 @@
    flex-direction: column;
    align-items: stretch;
  }
  /* ç¡®ä¿ç¡®è®¤å¯¹è¯æ¡†åœ¨æœ€å‰é¢ */
.el-message-box__wrapper {
  z-index: 10001 !important;
}
.el-message {
  z-index: 10002 !important;
}
  .scanner-form .el-input {
    width: 100%;
  }
}
/* æ–°å¢žï¼šæ‰‹åŠ¨å¼¹çª—çš„æŒ‰é’®æ‚¬åœæ•ˆæžœ */
:deep(button) {
  transition: all 0.3s;
}
:deep(button:hover) {
  opacity: 0.8;
}
:deep(button:active) {
  opacity: 0.6;
}
</style>
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/17.14.878.3237/CodeChunks.db-shm
Binary files differ
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/17.14.878.3237/SemanticSymbols.db-shm
Binary files differ
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/ErpApiService.cs
@@ -41,7 +41,10 @@
        {
            try
            {
                //erp æµ‹è¯•环境
                var request = new TokenRequest { appId = "BG_SYSTEM", secretKey = "7e9239c1e132462a9cf03bfa342a044aMTcxODE5MzgxODI4Mw" };
                ////erp æ­£å¼çŽ¯å¢ƒ
                //var request = new TokenRequest { appId = "BG_SYSTEM", secretKey = "9a3d0b5a37Bfc6dAM4b34ODb8ebDOSb937106d1b19DS29098" };
                var response = await PostAsync<TokenRequest, TokenResponse>("auth/getAccessToken", request, includeToken: false);
                var _token = response?.data?.access_token;
                return _token ?? "";
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs
@@ -5,6 +5,7 @@
using Org.BouncyCastle.Asn1.Ocsp;
using SqlSugar;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@@ -25,9 +26,10 @@
using WIDESEA_IBasicService;
using WIDESEA_IOutboundService;
using WIDESEA_Model.Models;
using WIDESEA_Model.Models.Outbound;
namespace WIDESEA_BasicService
{
{
    public class InvokeMESService : IInvokeMESService
    {
        private readonly IHttpClientFactory _httpClientFactory;
@@ -44,7 +46,14 @@
        private readonly IOutboundOrderService _outboundOrderService;
        private readonly IOutboundOrderDetailService _outboundOrderDetailService;
        private readonly IOutStockLockInfoService _outStockLockInfoService;
        public InvokeMESService(IHttpClientFactory httpClientFactory, ILogger<InvokeMESService> logger, IRepository<Dt_FeedbackToMes> feedbacktomesRepository, IRepository<Dt_StockInfoDetail> stockInfoDetailRepository, IRepository<Dt_StockInfo> stockInfoRepository, IRepository<Dt_InboundOrder> inboundOrderRepository, IOutboundOrderService outboundOrderService, IOutboundOrderDetailService outboundOrderDetailService, IOutStockLockInfoService outStockLockInfoService, IMaterialUnitService materialUnitService, IRepository<Dt_PickingRecord> pickingRecoreRepository)
        private readonly IRepository<Dt_InterfaceLog> _interfacelogRepository;
        // å­˜å‚¨èµ„源ID及其对应的锁对象。使用 ConcurrentDictionary ç¡®ä¿å¯¹å­—典操作本身的线程安全。
        private static readonly ConcurrentDictionary<string, object> _resourceLocks = new ConcurrentDictionary<string, object>();
        // å…¨å±€é™æ€é”ï¼šç”¨äºŽä¿æŠ¤ _resourceLocks å­—典中 GetOrAdd æˆ– TryRemove æ—¶çš„竞争
        private static readonly object _globalLocker = new object();
        public InvokeMESService(IHttpClientFactory httpClientFactory, ILogger<InvokeMESService> logger, IRepository<Dt_FeedbackToMes> feedbacktomesRepository, IRepository<Dt_StockInfoDetail> stockInfoDetailRepository, IRepository<Dt_StockInfo> stockInfoRepository, IRepository<Dt_InboundOrder> inboundOrderRepository, IOutboundOrderService outboundOrderService, IOutboundOrderDetailService outboundOrderDetailService, IOutStockLockInfoService outStockLockInfoService, IMaterialUnitService materialUnitService, IRepository<Dt_PickingRecord> pickingRecoreRepository, IRepository<Dt_InterfaceLog> interfacelogRepository)
        {
            _httpClientFactory = httpClientFactory;
            _logger = logger;
@@ -57,6 +66,7 @@
            _outStockLockInfoService = outStockLockInfoService;
            _materialUnitService = materialUnitService;
            _pickingRecoreRepository = pickingRecoreRepository;
            _interfacelogRepository = interfacelogRepository;
        }
        /// <summary>
@@ -105,7 +115,7 @@
            _client.DefaultRequestHeaders.Clear();
            _client.DefaultRequestHeaders.Add("Accept", "application/json");
            _logger.LogInformation("InvokeMESService  FeedbackOutbound :  "+ model.orderNo +"  , " + json);
            _logger.LogInformation("InvokeMESService  FeedbackOutbound :  " + model.orderNo + "  , " + json);
            var response = await _client.PostAsync("AldMaterialOutbound/MaterialOutbound", content);
            string body = await response.Content.ReadAsStringAsync();
@@ -224,7 +234,8 @@
            }
        }
        /// <summary>
        /// 
        /// </summary>
@@ -233,141 +244,159 @@
        /// <returns></returns>
        public async Task<WebResponseContent> BatchOrderFeedbackToMes(List<string> orderNos, int inout)
        {
            if (inout == 1)
            // 1. ã€å†…存锁抢占】
            if (MemoryLockManager.TryAcquireLock(orderNos[0]))
            {
                foreach (var orderNo in orderNos)
                try
                {
                    try
                    if (inout == 1)
                    {
                        var stockinfos = _stockInfoRepository.Db.Queryable<Dt_StockInfo>("info").Where(info => info.StockStatus == 6)
                                        .Where(it => SqlFunc.Subqueryable<Dt_StockInfoDetail>().Where(s => s.StockId == it.Id && s.OrderNo == orderNo).Any())
                                        .ToList();
                        var feeds = _feedbacktomesRepository.Db.Queryable<Dt_FeedbackToMes>().Where(x => x.OrderNo == orderNo && x.ReportStatus == 1).Select(o => o.PalletCode).ToList();
                        var unreports = stockinfos.Where(x => !feeds.Contains(x.PalletCode)).ToList();
                        if (unreports != null && !unreports.Any())
                        foreach (var orderNo in orderNos)
                        {
                            return WebResponseContent.Instance.Error("没有需要回传的数据");
                        }
                        foreach (var item in unreports)
                        {
                            var lists = _stockInfoDetailRepository.Db.Queryable<Dt_StockInfoDetail>().Where(x => x.StockId == item.Id).ToList();
                            if (lists.Any())
                            try
                            {
                                var inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>().First(x => x.InboundOrderNo == lists.FirstOrDefault().OrderNo);
                                if (inboundOrder != null)
                                var stockinfos = _stockInfoRepository.Db.Queryable<Dt_StockInfo>("info").Where(info => info.StockStatus == 6)
                                                .Where(it => SqlFunc.Subqueryable<Dt_StockInfoDetail>().Where(s => s.StockId == it.Id && s.OrderNo == orderNo).Any())
                                                .ToList();
                                var feeds = _feedbacktomesRepository.Db.Queryable<Dt_FeedbackToMes>().Where(x => x.OrderNo == orderNo && x.ReportStatus == 1).Select(o => o.PalletCode).ToList();
                                var unreports = stockinfos.Where(x => !feeds.Contains(x.PalletCode)).ToList();
                                if (unreports != null && !unreports.Any())
                                {
                                    if (inboundOrder.OrderType == (int)InOrderTypeEnum.AllocatInbound)//调拨入库
                                    return WebResponseContent.Instance.Error("没有需要回传的数据");
                                }
                                foreach (var item in unreports)
                                {
                                    var lists = _stockInfoDetailRepository.Db.Queryable<Dt_StockInfoDetail>().Where(x => x.StockId == item.Id).ToList();
                                    if (lists.Any())
                                    {
                                        var allocate = SqlSugarHelper.DbWMS.Queryable<Dt_AllocateOrder>().Where(x => x.OrderNo == inboundOrder.InboundOrderNo).First();
                                        var allocatefeedmodel = new AllocateDto
                                        var inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>().First(x => x.InboundOrderNo == lists.FirstOrDefault().OrderNo);
                                        if (inboundOrder != null)
                                        {
                                            ReqCode = Guid.NewGuid().ToString(),
                                            ReqTime = DateTime.Now.ToString(),
                                            BusinessType = "2",
                                            FactoryArea = inboundOrder.FactoryArea,
                                            OperationType = 1,
                                            Operator = inboundOrder.Operator,
                                            OrderNo = inboundOrder.UpperOrderNo,
                                            fromWarehouse = allocate?.FromWarehouse ?? "",
                                            toWarehouse = allocate?.ToWarehouse ?? "",
                                            Details = new List<AllocateDtoDetail>()
                                            if (inboundOrder.OrderType == (int)InOrderTypeEnum.AllocatInbound)//调拨入库
                                            {
                                                var allocate = SqlSugarHelper.DbWMS.Queryable<Dt_AllocateOrder>().Where(x => x.OrderNo == inboundOrder.InboundOrderNo).First();
                                                var allocatefeedmodel = new AllocateDto
                                                {
                                                    ReqCode = Guid.NewGuid().ToString(),
                                                    ReqTime = DateTime.Now.ToString(),
                                                    BusinessType = "2",
                                                    FactoryArea = inboundOrder.FactoryArea,
                                                    OperationType = 1,
                                                    Operator = inboundOrder.Operator,
                                                    OrderNo = inboundOrder.UpperOrderNo,
                                                    fromWarehouse = allocate?.FromWarehouse ?? "",
                                                    toWarehouse = allocate?.ToWarehouse ?? "",
                                                    Details = new List<AllocateDtoDetail>()
                                        };
                                                };
                                        var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                           .Select(group => new AllocateDtoDetail
                                           {
                                               MaterialCode = group.Key.MaterielCode,
                                               LineNo = group.Key.InboundOrderRowNo,
                                               WarehouseCode = group.Key.WarehouseCode,
                                               Qty = group.Sum(x => x.BarcodeQty),
                                               Unit = group.Key.BarcodeUnit,
                                               Barcodes = group.Select(row => new BarcodeInfo
                                               {
                                                   Barcode = row.Barcode,
                                                   Qty = row.BarcodeQty,
                                                   BatchNo = row.BatchNo,
                                                   SupplyCode = row.SupplyCode,
                                                   Unit = row.BarcodeUnit
                                               }).ToList()
                                           }).ToList();
                                        allocatefeedmodel.Details = groupedData;
                                                var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                                   .Select(group => new AllocateDtoDetail
                                                   {
                                                       MaterialCode = group.Key.MaterielCode,
                                                       LineNo = group.Key.InboundOrderRowNo,
                                                       WarehouseCode = group.Key.WarehouseCode,
                                                       Qty = group.Sum(x => x.BarcodeQty),
                                                       Unit = group.Key.BarcodeUnit,
                                                       Barcodes = group.Select(row => new BarcodeInfo
                                                       {
                                                           Barcode = row.Barcode,
                                                           Qty = row.BarcodeQty,
                                                           BatchNo = row.BatchNo,
                                                           SupplyCode = row.SupplyCode,
                                                           Unit = row.BarcodeUnit
                                                       }).ToList()
                                                   }).ToList();
                                                allocatefeedmodel.Details = groupedData;
                                        var result = await FeedbackAllocate(allocatefeedmodel);
                                        if (result != null && result.code == 200)
                                        {
                                            _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand();
                                        }
                                    }
                                    else
                                    {
                                        var feedmodel = new FeedbackInboundRequestModel
                                        {
                                            reqCode = Guid.NewGuid().ToString(),
                                            reqTime = DateTime.Now.ToString(),
                                            business_type = inboundOrder.BusinessType,
                                            factoryArea = inboundOrder.FactoryArea,
                                            operationType = 1,
                                            Operator = inboundOrder.Operator,
                                            orderNo = inboundOrder.UpperOrderNo,
                                            status = inboundOrder.OrderStatus,
                                            details = new List<FeedbackInboundDetailsModel>()
                                                var result = await FeedbackAllocate(allocatefeedmodel);
                                                if (result != null && result.code == 200)
                                                {
                                                    _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand();
                                                }
                                            }
                                            else
                                            {
                                                var feedmodel = new FeedbackInboundRequestModel
                                                {
                                                    reqCode = Guid.NewGuid().ToString(),
                                                    reqTime = DateTime.Now.ToString(),
                                                    business_type = inboundOrder.BusinessType,
                                                    factoryArea = inboundOrder.FactoryArea,
                                                    operationType = 1,
                                                    Operator = inboundOrder.Operator,
                                                    orderNo = inboundOrder.UpperOrderNo,
                                                    status = inboundOrder.OrderStatus,
                                                    details = new List<FeedbackInboundDetailsModel>()
                                        };
                                                };
                                        var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.SupplyCode, item.BatchNo, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                           .Select(group => new FeedbackInboundDetailsModel
                                           {
                                               materialCode = group.Key.MaterielCode,
                                               supplyCode = group.Key.SupplyCode,
                                               batchNo = group.Key.BatchNo,
                                               lineNo = group.Key.InboundOrderRowNo,
                                               qty = group.Sum(x => x.BarcodeQty),
                                               // warehouseCode = group.Key.WarehouseCode=="0"?"1072": group.Key.WarehouseCode,
                                               warehouseCode = group.Key.WarehouseCode,
                                               unit = group.Key.BarcodeUnit,
                                               barcodes = group.Select(row => new FeedbackBarcodesModel
                                               {
                                                   barcode = row.Barcode,
                                                   qty = row.BarcodeQty
                                               }).ToList()
                                           }).ToList();
                                        feedmodel.details = groupedData;
                                        var result = await FeedbackInbound(feedmodel);
                                        if (result != null && result.code == 200)
                                        {
                                            _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand();
                                                var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.SupplyCode, item.BatchNo, item.InboundOrderRowNo, item.BarcodeUnit, item.WarehouseCode })
                                                   .Select(group => new FeedbackInboundDetailsModel
                                                   {
                                                       materialCode = group.Key.MaterielCode,
                                                       supplyCode = group.Key.SupplyCode,
                                                       batchNo = group.Key.BatchNo,
                                                       lineNo = group.Key.InboundOrderRowNo,
                                                       qty = group.Sum(x => x.BarcodeQty),
                                                       // warehouseCode = group.Key.WarehouseCode=="0"?"1072": group.Key.WarehouseCode,
                                                       warehouseCode = group.Key.WarehouseCode,
                                                       unit = group.Key.BarcodeUnit,
                                                       barcodes = group.Select(row => new FeedbackBarcodesModel
                                                       {
                                                           barcode = row.Barcode,
                                                           qty = row.BarcodeQty
                                                       }).ToList()
                                                   }).ToList();
                                                feedmodel.details = groupedData;
                                                var result = await FeedbackInbound(feedmodel);
                                                if (result != null && result.code == 200)
                                                {
                                                    _feedbacktomesRepository.Db.Insertable(new Dt_FeedbackToMes { OrderNo = orderNo, PalletCode = item.PalletCode, ReportStatus = 1 }).ExecuteCommand();
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                _logger.LogInformation("InvokeMESService  BatchOrderFeedbackToMes å›žå†™MES失败:  " + ex.Message);
                                return WebResponseContent.Instance.Error(ex.Message);
                            }
                        }
                    }
                    catch (Exception ex)
                    else if (inout == 2)
                    {
                        _logger.LogInformation("InvokeMESService  BatchOrderFeedbackToMes å›žå†™MES失败:  " + ex.Message);
                        return WebResponseContent.Instance.Error(ex.Message);
                    }
                        foreach (var orderNo in orderNos)
                        {
                            var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().FirstAsync(x => x.OrderNo == orderNo);
                            if (outboundOrder != null && outboundOrder.IsBatch == 0)
                            {
                                var result = await HandleOutboundOrderToMESCompletion(outboundOrder, orderNo);
                                return result;
                            }
                            else if (outboundOrder != null && outboundOrder.IsBatch == 1)
                            {
                                var result = await HandleOutboundOrderBatchToMESCompletion(outboundOrder, orderNo);
                                return result;
                            }
                        }
                    }
                }
                finally
                {
                    // 2. ã€é‡Šæ”¾å†…存锁】无论成功失败,必须释放
                    MemoryLockManager.ReleaseLock(orderNos[0]);
                }
            }
            else if (inout == 2)
            else
            {
                foreach (var orderNo in orderNos)
                {
                    var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().FirstAsync(x => x.OrderNo == orderNo);
                    if (outboundOrder != null && outboundOrder.IsBatch == 0)
                    {
                        var result = await HandleOutboundOrderToMESCompletion(outboundOrder, orderNo);
                        return result;
                    }
                    else if (outboundOrder != null && outboundOrder.IsBatch == 1)
                    {
                        var result = await HandleOutboundOrderBatchToMESCompletion(outboundOrder, orderNo);
                        return result;
                    }
                }
                // æŠ¢é”å¤±è´¥ï¼šè¯´æ˜Žæœ‰å¦ä¸€ä¸ªçº¿ç¨‹ï¼ˆWCS回调或人工操作)正在处理
                return WebResponseContent.Instance.Error("WMS正在处理此回传任务,请勿重复操作。");
            }
            return WebResponseContent.Instance.OK();
        }
@@ -404,218 +433,66 @@
                    return WebResponseContent.Instance.Error("没有需要回传的分拣记录");
                var documentNo = UniqueValueGenerator.Generate();
                var feedModel = new FeedbackOutboundRequestModel
                var groups = pickingRecords.GroupBy(x => x.FeedBackMesDocumentNo).ToList();
                foreach (var group in groups)
                {
                    reqCode = Guid.NewGuid().ToString(),
                    reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                    business_type = outboundOrder.BusinessType,
                    factoryArea = outboundOrder.FactoryArea,
                    operationType = 1,
                    Operator = outboundOrder.Operator!=""? outboundOrder.Operator:App.User.UserName,
                    orderNo = outboundOrder.UpperOrderNo,
                    documentsNO = documentNo,
                    status = outboundOrder.OrderStatus,
                    details = new List<FeedbackOutboundDetailsModel>()
                };
                var detailIds = new List<int>();
                // å¡«å……明细和条码信息
                foreach (var detail in orderDetails)
                {
                    // æŸ¥è¯¢è¯¥æ˜Žç»†å¯¹åº”的锁定条码记录
                    var detailPicks = pickingRecords.Where(x => x.OrderNo == orderNo
                                   && detail.Id == x.OrderDetailId).ToList();
                    if (!detailPicks.Any())
                    {
                        continue;
                    }
                    var detailModel = new FeedbackOutboundDetailsModel
                    {
                        materialCode = detail.MaterielCode,
                        lineNo = detail.lineNo,
                        warehouseCode = detail.WarehouseCode,
                        qty = detail.BarcodeQty,
                        currentDeliveryQty = 0,
                        unit = detail.BarcodeUnit,
                        barcodes = new List<WIDESEA_DTO.Outbound.BarcodesModel>()
                    };
                    // å¡«å……条码信息(含单位转换)
                    foreach (var item in detailPicks)
                    List<Dt_PickingRecord> records = group.ToList(); // è¯¥åˆ†ç»„下的所有记录
                    if (string.IsNullOrEmpty(group.Key))
                    {
                        if (item.PickQuantity <= 0)
                        var emptydocumentNo = UniqueValueGenerator.Generate();
                        records.ForEach(x => { x.FeedBackMesDocumentNo = emptydocumentNo; });
                        var result = await _pickingRecoreRepository.Db.Updateable(records).ExecuteCommandAsync();
                        var interfacelog = new Dt_InterfaceLog
                        {
                            continue;
                        }
                        var barModel = new WIDESEA_DTO.Outbound.BarcodesModel
                        {
                            barcode = item.Barcode,
                            supplyCode = item.SupplyCode,
                            batchNo = item.BatchNo,
                            unit = item.BarcodeUnit,
                            qty = item.PickQuantity
                            Content = JsonConvert.SerializeObject(records),
                            DocumentNo = emptydocumentNo,
                            OrderNo = orderNo,
                            OrderType = "2",
                        };
                        _interfacelogRepository.AddData(interfacelog);
                        // å•位不一致时转换
                        if (detail.BarcodeUnit != detail.Unit)
                        if (result > 0)
                        {
                            var convertResult = await _materialUnitService.ConvertAsync(
                                item.MaterielCode, item.PickQuantity, detail.Unit, detail.BarcodeUnit);
                            barModel.unit = convertResult.Unit;
                            barModel.qty = convertResult.Quantity;
                        }
                        else
                        {
                            barModel.qty = item.PickQuantity;
                        }
                        detailModel.currentDeliveryQty += barModel.qty;
                        detailModel.barcodes.Add(barModel);
                    }
                    detailIds.Add(detail.Id);
                    feedModel.details.Add(detailModel);
                }
                            (bool _flowControl, WebResponseContent _value) = await FeedBackBatchToMes(outboundOrder, orderNo, orderDetails, pickingRecords, emptydocumentNo);
                feedModel.details = feedModel.details.GroupBy(item => new { item.materialCode, item.lineNo, item.warehouseCode, item.unit,item.qty  }).Select(group => new FeedbackOutboundDetailsModel
                {
                    materialCode = group.Key.materialCode,
                    lineNo = group.Key.lineNo,
                    warehouseCode = group.Key.warehouseCode,
                    qty = group.Key.qty,
                    currentDeliveryQty = group.Sum(x => x.currentDeliveryQty),
                    unit = group.Key.unit,
                    barcodes = group.SelectMany(x => x.barcodes.GroupBy(o => new { o.barcode, o.supplyCode, o.batchNo, o.unit }).Select(row => new WIDESEA_DTO.Outbound.BarcodesModel
                    {
                        barcode = row.Key.barcode,
                        supplyCode = row.Key.supplyCode,
                        batchNo = row.Key.batchNo,
                        unit = row.Key.unit,
                        qty = row.Sum(y => y.qty)
                    })).ToList()
                }).ToList();
                            return _value;
                var allCompleted = true;
                // ç­›é€‰å¾…回传的明细(ReturnToMESStatus=0)
                var pendingDetails = orderDetails.Where(x => x.ReturnToMESStatus == 0).ToList();
                foreach (var detail in pendingDetails)
                {
                    if (detail.OverOutQuantity < detail.NeedOutQuantity)
                    {
                        allCompleted = false;
                    }
                }
                // å­˜åœ¨å›žä¼ å¤±è´¥çš„æ˜Žç»†ï¼ˆReturnToMESStatus=2),标记未完成
                if (orderDetails.Any(x => x.ReturnToMESStatus == 2))
                {
                    allCompleted = false;
                }
                //  æ›´æ–°è®¢å•状态
                int newStatus = allCompleted ? (int)OutOrderStatusEnum.出库完成 : (int)OutOrderStatusEnum.出库中;
                if (outboundOrder.OrderStatus != newStatus)
                {
                    int updateCount = await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(x => x.OrderStatus == newStatus)
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                    if (updateCount <= 0)
                    {
                        _logger.LogWarning($"更新出库单状态失败 - OrderNo: {orderNo}, ç›®æ ‡çŠ¶æ€: {newStatus}");
                    }
                }
                // è°ƒç”¨MES回传接口
                var mesResult = await FeedbackOutbound(feedModel);
                if (mesResult == null || mesResult.code != 200)
                {
                    // æ›´æ–°æ˜Žç»†ä¸ºå›žä¼ å¤±è´¥ï¼ˆReturnToMESStatus=2)
                    await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                        .SetColumns(it => new Dt_OutboundOrderDetail
                        {
                            ReturnToMESStatus = 2,
                            documentsNO = documentNo,
                        })
                        .Where(x => detailIds.Contains(x.Id))
                        .ExecuteCommandAsync();
                    return WebResponseContent.Instance.Error($"回传MES失败");
                }
                foreach (var record in pickingRecords.Where(x => detailIds.Contains(x.OrderDetailId)).ToList())
                {
                    record.ReturnToMESStatus = 1;
                }
                var updates = pickingRecords.Where(x => detailIds.Contains(x.OrderDetailId)).ToList();
                updates.ForEach(x =>
                {
                    x.ReturnToMESStatus = 1;
                });
                await _pickingRecoreRepository.Db.Updateable(updates).ExecuteCommandAsync();
                if (allCompleted)
                {
                    //MES回传成功:更新明细为回传成功状态
                    await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                        .SetColumns(it => new Dt_OutboundOrderDetail
                        {
                            ReturnToMESStatus = 1,
                            documentsNO = documentNo,
                        })
                        .Where(x => detailIds.Contains(x.Id))
                        .ExecuteCommandAsync();
                }
                // æ ¡éªŒæ˜¯å¦æ‰€æœ‰æ˜Žç»†éƒ½å®Œæˆï¼Œæ›´æ–°è®¢å•最终状态
                if (allCompleted && newStatus == (int)OutOrderStatusEnum.出库完成)
                {
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(x => new Dt_OutboundOrder
                        {
                            ReturnToMESStatus = 1,
                            OrderStatus = newStatus
                        })
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                }
                else
                {
                    // äºŒæ¬¡æ ¡éªŒæ˜¯å¦æ‰€æœ‰æœªå›žä¼ æ˜Žç»†éƒ½å·²å®Œæˆ
                    var dbOrderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                        .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id)
                        .Where((o, item) => item.OrderNo == orderNo && item.ReturnToMESStatus != 1)
                        .Select((o, item) => o)
                        .ToListAsync();
                    var secAllCompleted = true;
                    foreach (var detail in dbOrderDetails.Where(x => x.ReturnToMESStatus == 0).ToList())
                    {
                        if (detail.OverOutQuantity < detail.NeedOutQuantity)
                        {
                            secAllCompleted = false;
                            break;
                        }
                    }
                    if (secAllCompleted)
                    else
                    {
                        await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                            .SetColumns(it => new Dt_OutboundOrder
                        var ilog = _interfacelogRepository.QueryFirst(x => x.DocumentNo == group.Key);
                        if (ilog == null)
                        {
                            var interfacelog = new Dt_InterfaceLog
                            {
                                ReturnToMESStatus = 1,
                                OrderStatus = OutOrderStatusEnum.出库完成.ObjToInt(),
                            })
                            .Where(x => x.OrderNo == orderNo)
                            .ExecuteCommandAsync();
                                Content = JsonConvert.SerializeObject(records),
                                DocumentNo = group.Key,
                                OrderNo = orderNo,
                                OrderType = "2",
                            };
                            _interfacelogRepository.AddData(interfacelog);
                        }
                        (bool _flowControl, WebResponseContent _value) = await FeedBackBatchToMes(outboundOrder, orderNo, orderDetails, pickingRecords, group.Key);
                        return _value;
                    }
                }
                //var documentNo = UniqueValueGenerator.Generate();
                //(bool flowControl, WebResponseContent value) = await FeedBackBatchToMes(outboundOrder, orderNo, orderDetails, pickingRecords, documentNo);
                //if (!flowControl)
                //{
                //    return value;
                //}
                // å›žä¼ æˆåŠŸçš„æœ€ç»ˆè¿”å›ž
                response = WebResponseContent.Instance.OK($"回传MES成功,单据号:{documentNo}");
                response = WebResponseContent.Instance.OK($"回传MES成功,单据号:{orderNo}");
            }
            catch (Exception ex)
            {
@@ -628,6 +505,223 @@
            }
            return response;
        }
        private async Task<(bool flowControl, WebResponseContent value)> FeedBackBatchToMes(Dt_OutboundOrder outboundOrder, string orderNo, List<Dt_OutboundOrderDetail> orderDetails, List<Dt_PickingRecord> pickingRecords, string documentNo)
        {
            var feedModel = new FeedbackOutboundRequestModel
            {
                reqCode = Guid.NewGuid().ToString(),
                reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                business_type = outboundOrder.BusinessType,
                factoryArea = outboundOrder.FactoryArea,
                operationType = 1,
                Operator = outboundOrder.Operator != "" ? outboundOrder.Operator : App.User.UserName,
                orderNo = outboundOrder.UpperOrderNo,
                documentsNO = documentNo,
                status = outboundOrder.OrderStatus,
                details = new List<FeedbackOutboundDetailsModel>()
            };
            var detailIds = new List<int>();
            // å¡«å……明细和条码信息
            foreach (var detail in orderDetails)
            {
                // æŸ¥è¯¢è¯¥æ˜Žç»†å¯¹åº”的锁定条码记录
                var detailPicks = pickingRecords.Where(x => x.OrderNo == orderNo
                               && detail.Id == x.OrderDetailId).ToList();
                if (!detailPicks.Any())
                {
                    continue;
                }
                var detailModel = new FeedbackOutboundDetailsModel
                {
                    materialCode = detail.MaterielCode,
                    lineNo = detail.lineNo,
                    warehouseCode = detail.WarehouseCode,
                    qty = detail.BarcodeQty,
                    currentDeliveryQty = 0,
                    unit = detail.BarcodeUnit,
                    barcodes = new List<WIDESEA_DTO.Outbound.BarcodesModel>()
                };
                // å¡«å……条码信息(含单位转换)
                foreach (var item in detailPicks)
                {
                    if (item.PickQuantity <= 0)
                    {
                        continue;
                    }
                    var barModel = new WIDESEA_DTO.Outbound.BarcodesModel
                    {
                        barcode = item.Barcode,
                        supplyCode = item.SupplyCode,
                        batchNo = item.BatchNo,
                        unit = item.BarcodeUnit,
                        qty = item.PickQuantity
                    };
                    // å•位不一致时转换
                    if (detail.BarcodeUnit != detail.Unit)
                    {
                        var convertResult = await _materialUnitService.ConvertAsync(
                            item.MaterielCode, item.PickQuantity, detail.Unit, detail.BarcodeUnit);
                        barModel.unit = convertResult.Unit;
                        barModel.qty = convertResult.Quantity;
                    }
                    else
                    {
                        barModel.qty = item.PickQuantity;
                    }
                    detailModel.currentDeliveryQty += barModel.qty;
                    detailModel.barcodes.Add(barModel);
                }
                detailIds.Add(detail.Id);
                feedModel.details.Add(detailModel);
            }
            feedModel.details = feedModel.details.GroupBy(item => new { item.materialCode, item.lineNo, item.warehouseCode, item.unit, item.qty }).Select(group => new FeedbackOutboundDetailsModel
            {
                materialCode = group.Key.materialCode,
                lineNo = group.Key.lineNo,
                warehouseCode = group.Key.warehouseCode,
                qty = group.Key.qty,
                currentDeliveryQty = group.Sum(x => x.currentDeliveryQty),
                unit = group.Key.unit,
                barcodes = group.SelectMany(x => x.barcodes.GroupBy(o => new { o.barcode, o.supplyCode, o.batchNo, o.unit }).Select(row => new WIDESEA_DTO.Outbound.BarcodesModel
                {
                    barcode = row.Key.barcode,
                    supplyCode = row.Key.supplyCode,
                    batchNo = row.Key.batchNo,
                    unit = row.Key.unit,
                    qty = row.Sum(y => y.qty)
                })).ToList()
            }).ToList();
            var allCompleted = true;
            // ç­›é€‰å¾…回传的明细(ReturnToMESStatus=0)
            var pendingDetails = orderDetails.Where(x => x.ReturnToMESStatus == 0).ToList();
            foreach (var detail in pendingDetails)
            {
                if (detail.OverOutQuantity < detail.NeedOutQuantity)
                {
                    allCompleted = false;
                }
            }
            // å­˜åœ¨å›žä¼ å¤±è´¥çš„æ˜Žç»†ï¼ˆReturnToMESStatus=2),标记未完成
            if (orderDetails.Any(x => x.ReturnToMESStatus == 2))
            {
                allCompleted = false;
            }
            //  æ›´æ–°è®¢å•状态
            int newStatus = allCompleted ? (int)OutOrderStatusEnum.出库完成 : (int)OutOrderStatusEnum.出库中;
            if (outboundOrder.OrderStatus != newStatus)
            {
                int updateCount = await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                    .SetColumns(x => x.OrderStatus == newStatus)
                    .Where(x => x.OrderNo == orderNo)
                    .ExecuteCommandAsync();
                if (updateCount <= 0)
                {
                    _logger.LogWarning($"更新出库单状态失败 - OrderNo: {orderNo}, ç›®æ ‡çŠ¶æ€: {newStatus}");
                }
            }
            // è°ƒç”¨MES回传接口
            var mesResult = await FeedbackOutbound(feedModel);
            if (mesResult == null || mesResult.code != 200)
            {
                // æ›´æ–°æ˜Žç»†ä¸ºå›žä¼ å¤±è´¥ï¼ˆReturnToMESStatus=2)
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(it => new Dt_OutboundOrderDetail
                    {
                        ReturnToMESStatus = 2,
                        documentsNO = documentNo,
                    })
                    .Where(x => detailIds.Contains(x.Id))
                    .ExecuteCommandAsync();
                return (flowControl: false, value: WebResponseContent.Instance.Error($"回传MES失败"));
            }
            foreach (var record in pickingRecords.Where(x => detailIds.Contains(x.OrderDetailId)).ToList())
            {
                record.ReturnToMESStatus = 1;
            }
            var updates = pickingRecords.Where(x => detailIds.Contains(x.OrderDetailId)).ToList();
            updates.ForEach(x =>
            {
                x.ReturnToMESStatus = 1;
            });
            await _pickingRecoreRepository.Db.Updateable(updates).ExecuteCommandAsync();
            await _interfacelogRepository.Db.Updateable<Dt_InterfaceLog>()
                                             .SetColumns(x => x.ReturnToMESStatus == 1)
                                             .Where(x => x.OrderNo == orderNo)
                                             .ExecuteCommandAsync();
            if (allCompleted)
            {
                //MES回传成功:更新明细为回传成功状态
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(it => new Dt_OutboundOrderDetail
                    {
                        ReturnToMESStatus = 1,
                        documentsNO = documentNo,
                    })
                    .Where(x => detailIds.Contains(x.Id))
                    .ExecuteCommandAsync();
            }
            // æ ¡éªŒæ˜¯å¦æ‰€æœ‰æ˜Žç»†éƒ½å®Œæˆï¼Œæ›´æ–°è®¢å•最终状态
            if (allCompleted && newStatus == (int)OutOrderStatusEnum.出库完成)
            {
                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                    .SetColumns(x => new Dt_OutboundOrder
                    {
                        ReturnToMESStatus = 1,
                        OrderStatus = newStatus
                    })
                    .Where(x => x.OrderNo == orderNo)
                    .ExecuteCommandAsync();
            }
            else
            {
                // äºŒæ¬¡æ ¡éªŒæ˜¯å¦æ‰€æœ‰æœªå›žä¼ æ˜Žç»†éƒ½å·²å®Œæˆ
                var dbOrderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id)
                    .Where((o, item) => item.OrderNo == orderNo && item.ReturnToMESStatus != 1)
                    .Select((o, item) => o)
                    .ToListAsync();
                var secAllCompleted = true;
                foreach (var detail in dbOrderDetails.Where(x => x.ReturnToMESStatus == 0).ToList())
                {
                    if (detail.OverOutQuantity < detail.NeedOutQuantity)
                    {
                        secAllCompleted = false;
                        break;
                    }
                }
                if (secAllCompleted)
                {
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(it => new Dt_OutboundOrder
                        {
                            ReturnToMESStatus = 1,
                            OrderStatus = OutOrderStatusEnum.出库完成.ObjToInt(),
                        })
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                }
            }
            return (flowControl: true, value: null);
        }
        private async Task<WebResponseContent> HandleOutboundOrderToMESCompletion(Dt_OutboundOrder outboundOrder, string orderNo)
@@ -777,7 +871,7 @@
                return WebResponseContent.Instance.Error("处理回传MES时发生异常,请联系管理员");
            }
        }
    }
    }
    public static class UniqueValueGenerator
    {
@@ -796,10 +890,10 @@
            // æ‹¼æŽ¥ï¼šè®¡æ•°å™¨è¡¥0为3位(避免位数不一致)
            return $"{datePart}{ticksPart}";
        }
        public static string GenerateCount()
        {
            var now = DateTime.Now;
            var now = DateTime.Now;
            string datePart = now.ToString("yyyyMMddHHmmss");
@@ -810,4 +904,59 @@
            return $"{datePart}{counterPart:D3}";
        }
    }
    public static class MemoryLockManager
    {
        // å­˜å‚¨èµ„源ID及其对应的锁对象。使用 ConcurrentDictionary ç¡®ä¿å¯¹å­—典操作本身的线程安全。
        private static readonly ConcurrentDictionary<string, object> _resourceLocks = new ConcurrentDictionary<string, object>();
        // å…¨å±€é™æ€é”ï¼šç”¨äºŽä¿æŠ¤ _resourceLocks å­—典中 GetOrAdd æˆ– TryRemove æ—¶çš„竞争
        private static readonly object _globalLocker = new object();
        /// <summary>
        /// å°è¯•锁定一个资源ID。
        /// </summary>
        /// <param name="resourceId">要锁定的资源ID(例如 InboundRecord ID)</param>
        /// <returns>是否成功获取锁</returns>
        public static bool TryAcquireLock(string resourceId)
        {
            object lockObject = null;
            // æ ¸å¿ƒæ€è·¯ï¼šä¸ºæ¯ä¸ªèµ„源创建一个唯一的锁对象
            lock (_globalLocker)
            {
                // å¦‚果资源ID不在字典中,则添加一个新的锁对象
                // å¦åˆ™ï¼Œä½¿ç”¨å·²å­˜åœ¨çš„锁对象
                lockObject = _resourceLocks.GetOrAdd(resourceId, new object());
            }
            // å°è¯•获取资源特定的锁
            // ä½¿ç”¨ Monitor.TryEnter é¿å…é˜»å¡žï¼Œå¹¶å®žçŽ°éžé˜»å¡žçš„æŠ¢é”
            return Monitor.TryEnter(lockObject);
        }
        /// <summary>
        /// é‡Šæ”¾èµ„源ID的锁定。
        /// </summary>
        /// <param name="resourceId">要释放的资源ID</param>
        public static void ReleaseLock(string resourceId)
        {
            if (_resourceLocks.TryGetValue(resourceId, out object lockObject))
            {
                // ç¡®ä¿é‡Šæ”¾çš„æ˜¯å½“前线程持有的锁
                if (Monitor.IsEntered(lockObject))
                {
                    Monitor.Exit(lockObject);
                    // é‡Šæ”¾é”åŽï¼Œå°è¯•从字典中移除这个锁对象,清理内存。
                    // å¿…须在 Monitor.Exit ä¹‹åŽæ‰§è¡Œã€‚
                    lock (_globalLocker)
                    {
                        _resourceLocks.TryRemove(resourceId, out _);
                    }
                }
            }
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
@@ -139,15 +139,26 @@
                }
                List<string> lockLocationCodes = locationCaches.Select(x => x.LocationCode).ToList();
                //Dictionary<string, SqlSugar.OrderByType> orderBy = new Dictionary<string, OrderByType>()
                //{
                //    { nameof(Dt_LocationInfo.RoadwayNo),OrderByType.Asc },
                //    { nameof(Dt_LocationInfo.Layer),OrderByType.Asc },
                //    { nameof(Dt_LocationInfo.Column),OrderByType.Asc },
                //    { nameof(Dt_LocationInfo.Depth),OrderByType.Desc },
                //    { nameof(Dt_LocationInfo.Row),OrderByType.Asc }
                //};
                Dictionary<string, SqlSugar.OrderByType> orderBy = new Dictionary<string, OrderByType>()
                {
                    { nameof(Dt_LocationInfo.RoadwayNo),OrderByType.Asc },
                    //{ nameof(Dt_LocationInfo.RoadwayNo),OrderByType.Asc },
                    { nameof(Dt_LocationInfo.Layer),OrderByType.Asc },
                    { nameof(Dt_LocationInfo.Row),OrderByType.Asc },
                    { nameof(Dt_LocationInfo.Column),OrderByType.Asc },
                    { nameof(Dt_LocationInfo.Depth),OrderByType.Desc },
                    { nameof(Dt_LocationInfo.Row),OrderByType.Asc }
                };
                var first = BaseDal.QueryFirst(x => x.LocationType == locationType && x.LocationStatus == LocationStatusEnum.Free.ObjToInt() && x.EnableStatus != EnableStatusEnum.Disable.ObjToInt() && !lockLocationCodes.Contains(x.LocationCode), orderBy);//查询空货位信息并排除5分钟内分配的货位,根据层、列、深度、行排序
                if (first != null)
@@ -157,7 +168,7 @@
                    {
                        LocationStatus = (int)LocationStatusEnum.InStockLock,
                    }).Where(x => x.Id == first.Id).ExecuteCommand();
                }
                }
                return first;
            }
@@ -217,7 +228,7 @@
        {
            return Repository.QueryData(x => locationCodes.Contains(x.LocationCode));
        }
        public List<LocationTypeDto> GetLocationTypes()
        {
            return _locationTypeRepository.Db.Queryable<Dt_LocationType>().Select(x =>
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_CheckService/ReCheckOrderService.cs
@@ -92,6 +92,11 @@
                {
                    return WebResponseContent.Instance.Error($"未找到单信息");
                }
                var isupdate = false;
                if (recheckOrder.SignSeq == model.SignSeq)
                {
                    isupdate = true;
                }
                recheckOrder.OrderNo = model.OrderNo;
                recheckOrder.MaterielCode = model.MaterielCode;
                recheckOrder.BatchNo = model.BatchNo;
@@ -107,14 +112,15 @@
                BaseDal.UpdateData(recheckOrder);
                _outboundService.OutboundOrderService.Db.Deleteable<Dt_OutboundOrder>().Where(x => x.OrderNo == model.OrderNo).ExecuteCommand();
                _outboundService.OutboundOrderDetailService.Db.Deleteable<Dt_OutboundOrderDetail>()
                 .Where(p => SqlFunc.Subqueryable<Dt_OutboundOrder>().Where(s => s.Id == p.OrderId && s.OrderNo == model.OrderNo).Any()).ExecuteCommand();
                if (isupdate)
                {
                    _outboundService.OutboundOrderService.Db.Deleteable<Dt_OutboundOrder>().Where(x => x.OrderNo == model.OrderNo).ExecuteCommand();
                    _outboundService.OutboundOrderDetailService.Db.Deleteable<Dt_OutboundOrderDetail>()
                     .Where(p => SqlFunc.Subqueryable<Dt_OutboundOrder>().Where(s => s.Id == p.OrderId && s.OrderNo == model.OrderNo).Any()).ExecuteCommand();
                var outboundOrders = ConvertToOutboundOrders(model);
                await _outboundService.OutboundOrderService.ReceiveOutboundOrder(outboundOrders, 1);
                    var outboundOrders = ConvertToOutboundOrders(model);
                    await _outboundService.OutboundOrderService.ReceiveOutboundOrder(outboundOrders, 1);
                }
                return WebResponseContent.Instance.OK();
            }
            catch (Exception ex)
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Common/TaskEnum/TaskStatusEnum.cs
@@ -66,6 +66,8 @@
        [Description("AGV执行中")]
        AGV_Executing = 310,
        [Description("AGV取货中")]
        AGV_Pull = 315,
        /// <summary>
        /// AGV待继续执行
        /// </summary>
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Core/BaseRepository/RepositoryBase.cs
@@ -901,7 +901,7 @@
                            else if (propertyInfo.Name == nameof(BaseEntity.ModifyDate))
                            {
                                propertyInfo.SetValue(obj, DateTime.Now);
                            }
                            }
                            else
                            {
                                propertyInfo.SetValue(obj, property.GetValue(entity));
@@ -913,62 +913,245 @@
                }
            }
            return DeleteData(entity);
        }
        //public bool DeleteAndMoveIntoHty(TEntity entity, OperateTypeEnum operateType)
        //{
        //    // æ ¸å¿ƒé€»è¾‘:用事务保证原子性,异常捕获避免流程中断,日志辅助排查
        //    bool isSuccess = false;
        //    string entityTypeName = entity?.GetType().Name ?? "未知实体";
        //    try
        //    {
        //        // å‰ç½®æ ¡éªŒï¼šå®žä½“不能为空
        //        if (entity == null)
        //        {
        //            return false;
        //        }
        //        Type entityType = entity.GetType();
        //        Assembly assembly = entityType.Assembly;
        //        string htyTypeName = $"{entityType.FullName}_Hty";
        //        Type? htyType = assembly.GetType(htyTypeName);
        //        // 1. æ£€æŸ¥åŽ†å²è¡¨ç±»åž‹æ˜¯å¦å­˜åœ¨
        //        if (htyType == null)
        //        {
        //            return false;
        //        }
        //        // 2. åˆ›å»ºåŽ†å²è¡¨å®žä¾‹ï¼ˆå¤„ç†æ— å‚æž„é€ å‡½æ•°ä¸å­˜åœ¨çš„æƒ…å†µï¼‰
        //        object? htyObj;
        //        try
        //        {
        //            htyObj = Activator.CreateInstance(htyType);
        //        }
        //        catch (Exception ex)
        //        {
        //            // _logger.LogError(ex, "DeleteAndMoveIntoHty:创建历史表实例 {HtyTypeName} å¤±è´¥", htyTypeName);
        //            return false;
        //        }
        //        if (htyObj == null)
        //        {
        //            // _logger.LogWarning("DeleteAndMoveIntoHty:历史表实例 {HtyTypeName} åˆ›å»ºç»“果为null", htyTypeName);
        //            return false;
        //        }
        //        // 3. èŽ·å–æ ¸å¿ƒå±žæ€§ï¼ˆæŒ‡å®šBindingFlags确保获取公共实例属性)
        //        BindingFlags propFlags = BindingFlags.Public | BindingFlags.Instance;
        //        PropertyInfo? keyPro = typeof(TEntity).GetKeyProperty(); // è‡ªå®šä¹‰æ–¹æ³•需确保返回非空,此处增加判空
        //        PropertyInfo? operateTypePro = htyType.GetProperty(nameof(IBaseHistoryEntity.OperateType), propFlags);
        //        PropertyInfo? sourceIdPro = htyType.GetProperty(nameof(IBaseHistoryEntity.SourceId), propFlags);
        //        // æ ¡éªŒæ ¸å¿ƒå±žæ€§æ˜¯å¦å­˜åœ¨
        //        if (keyPro == null)
        //        {
        //            //_logger.LogError("DeleteAndMoveIntoHty:实体 {EntityType} æœªæ‰¾åˆ°ä¸»é”®å±žæ€§", entityType.FullName);
        //            return false;
        //        }
        //        if (operateTypePro == null)
        //        {
        //            //_logger.LogError("DeleteAndMoveIntoHty:历史表 {HtyTypeName} æœªæ‰¾åˆ°OperateType属性", htyTypeName);
        //            return false;
        //        }
        //        if (sourceIdPro == null)
        //        {
        //            // _logger.LogError("DeleteAndMoveIntoHty:历史表 {HtyTypeName} æœªæ‰¾åˆ°SourceId属性", htyTypeName);
        //            return false;
        //        }
        //        // 4. èµ‹å€¼æ ¸å¿ƒå±žæ€§ï¼ˆæ ¡éªŒç±»åž‹åŒ¹é…ï¼Œé¿å…SetValue抛异常)
        //        try
        //        {
        //            // å¤„理OperateType类型匹配:若历史表属性是枚举类型,直接传枚举而非字符串
        //            if (operateTypePro.PropertyType == typeof(OperateTypeEnum))
        //            {
        //                operateTypePro.SetValue(htyObj, operateType);
        //            }
        //            else if (operateTypePro.PropertyType == typeof(string))
        //            {
        //                operateTypePro.SetValue(htyObj, operateType.ToString());
        //            }
        //            else
        //            {
        //                //_logger.LogError("DeleteAndMoveIntoHty:历史表 {HtyTypeName} çš„OperateType属性类型 {PropType} ä¸åŒ¹é…ï¼ˆä»…支持枚举/字符串)", htyTypeName, operateTypePro.PropertyType.FullName);
        //                return false;
        //            }
        //            // èµ‹å€¼SourceId(校验类型匹配)
        //            object sourceIdValue = keyPro.GetValue(entity)!;
        //            if (sourceIdPro.PropertyType != sourceIdValue.GetType())
        //            {
        //                sourceIdValue = Convert.ChangeType(sourceIdValue, sourceIdPro.PropertyType); // ç±»åž‹è½¬æ¢
        //            }
        //            sourceIdPro.SetValue(htyObj, sourceIdValue);
        //        }
        //        catch (Exception ex)
        //        {
        //            // _logger.LogError(ex, "DeleteAndMoveIntoHty:历史表 {HtyTypeName} æ ¸å¿ƒå±žæ€§èµ‹å€¼å¤±è´¥", htyTypeName);
        //            return false;
        //        }
        //        // 5. èµ‹å€¼å…¶ä»–属性(排除核心属性)
        //        List<PropertyInfo> htyProperties = htyType.GetProperties(propFlags)
        //            .Where(x => x.Name != operateTypePro.Name
        //                     && x.Name != sourceIdPro.Name
        //                     && x.Name != keyPro.Name)
        //            .ToList();
        //        foreach (PropertyInfo htyProp in htyProperties)
        //        {
        //            PropertyInfo? entityProp = entityType.GetProperty(htyProp.Name, propFlags);
        //            if (entityProp == null) continue; // å®žä½“无该属性则跳过
        //            try
        //            {
        //                object propValue;
        //                // å¤„理修改人:避免App.User空引用
        //                if (htyProp.Name == nameof(BaseEntity.Modifier))
        //                {
        //                    propValue = App.User?.UserId > 0 ? App.User?.UserName : "System";
        //                }
        //                // å¤„理修改时间
        //                else if (htyProp.Name == nameof(BaseEntity.ModifyDate))
        //                {
        //                    propValue = DateTime.Now;
        //                }
        //                // å…¶ä»–属性从原实体取值
        //                else
        //                {
        //                    propValue = entityProp.GetValue(entity) ?? DBNull.Value; // å¤„理null值
        //                }
        //                // ç±»åž‹è½¬æ¢åŽèµ‹å€¼ï¼ˆé¿å…ç±»åž‹ä¸åŒ¹é…ï¼‰
        //                if (propValue != DBNull.Value && propValue != null)
        //                {
        //                    propValue = Convert.ChangeType(propValue, htyProp.PropertyType);
        //                }
        //                htyProp.SetValue(htyObj, propValue);
        //            }
        //            catch (Exception ex)
        //            {
        //                // _logger.LogWarning(ex, "DeleteAndMoveIntoHty:历史表 {HtyTypeName} å±žæ€§ {PropName} èµ‹å€¼å¤±è´¥ï¼Œè·³è¿‡è¯¥å±žæ€§", htyTypeName, htyProp.Name);
        //            }
        //        }
        //        try
        //        {
        //            // æ‰§è¡Œæ’入历史表
        //            int insertRows = _db.InsertableByObject(htyObj).AS(entityType.Name + "_Hty").ExecuteCommand();
        //            if (insertRows <= 0)
        //            {
        //                // _logger.LogError("DeleteAndMoveIntoHty:历史表 {HtyTypeName} æ’入失败(影响行数0)", htyTypeName);
        //                _db.InsertableByObject(htyObj).AS(entityType.Name + "_Hty").ExecuteCommand();
        //                return false;
        //            }
        //            // æ’入成功后执行删除
        //            bool deleteSuccess = DeleteData(entity);
        //            if (!deleteSuccess)
        //            {
        //                //_logger.LogError("DeleteAndMoveIntoHty:实体 {EntityType} åˆ é™¤å¤±è´¥", entityType.FullName);
        //                DeleteData(entity);
        //                return false;
        //            }
        //            // æäº¤äº‹åŠ¡
        //            isSuccess = true;
        //            //_logger.LogInformation("DeleteAndMoveIntoHty:实体 {EntityType} å·²æˆåŠŸç§»å…¥åŽ†å²è¡¨å¹¶åˆ é™¤åŽŸæ•°æ®", entityType.FullName);
        //        }
        //        catch (Exception ex)
        //        {
        //            // _logger.LogError(ex, "DeleteAndMoveIntoHty:事务执行失败(插入历史表/删除原数据)", entityType.FullName);
        //            return false;
        //        }
        //    }
        //    catch (Exception ex)
        //    {
        //        //  _logger.LogError(ex, "DeleteAndMoveIntoHty:处理实体 {EntityTypeName} æ—¶å‘生未捕获异常", entityTypeName);
        //        return false;
        //    }
        //    return isSuccess;
        //}
        public bool DeleteAndMoveIntoHty(List<TEntity> entities, OperateTypeEnum operateType)
{
    Type type = typeof(TEntity);
    Assembly assembly = type.Assembly;
    Type? htyType = assembly.GetType(type.FullName + "_Hty");
    if (htyType != null)
    {
        object? obj2 = Activator.CreateInstance(htyType);
        PropertyInfo keyPro = typeof(TEntity).GetKeyProperty();
        PropertyInfo? operateTypePro = htyType.GetProperty(nameof(IBaseHistoryEntity.OperateType));
        PropertyInfo? sourceIdPro = htyType.GetProperty(nameof(IBaseHistoryEntity.SourceId));
        if (obj2 != null && keyPro != null && operateTypePro != null && sourceIdPro != null)
        {
            Type type = typeof(TEntity);
            Assembly assembly = type.Assembly;
            Type? htyType = assembly.GetType(type.FullName + "_Hty");
            if (htyType != null)
            List<PropertyInfo> propertyInfos = htyType.GetProperties().Where(x => x.Name != operateTypePro.Name && x.Name != sourceIdPro.Name && x.Name != keyPro.Name).ToList();
            List<object> list = new List<object>();
            foreach (var item in entities)
            {
                object? obj2 = Activator.CreateInstance(htyType);
                PropertyInfo keyPro = typeof(TEntity).GetKeyProperty();
                PropertyInfo? operateTypePro = htyType.GetProperty(nameof(IBaseHistoryEntity.OperateType));
                PropertyInfo? sourceIdPro = htyType.GetProperty(nameof(IBaseHistoryEntity.SourceId));
                if (obj2 != null && keyPro != null && operateTypePro != null && sourceIdPro != null)
                object? obj = Activator.CreateInstance(htyType);
                if (obj != null)
                {
                    List<PropertyInfo> propertyInfos = htyType.GetProperties().Where(x => x.Name != operateTypePro.Name && x.Name != sourceIdPro.Name && x.Name != keyPro.Name).ToList();
                    List<object> list = new List<object>();
                    foreach (var item in entities)
                    operateTypePro.SetValue(obj, operateType.ToString());
                    sourceIdPro.SetValue(obj, keyPro.GetValue(item));
                    for (int i = 0; i < propertyInfos.Count; i++)
                    {
                        object? obj = Activator.CreateInstance(htyType);
                        if (obj != null)
                        {
                            operateTypePro.SetValue(obj, operateType.ToString());
                            sourceIdPro.SetValue(obj, keyPro.GetValue(item));
                            for (int i = 0; i < propertyInfos.Count; i++)
                            {
                                PropertyInfo propertyInfo = propertyInfos[i];
                                PropertyInfo? property = type.GetProperty(propertyInfo.Name);
                        PropertyInfo propertyInfo = propertyInfos[i];
                        PropertyInfo? property = type.GetProperty(propertyInfo.Name);
                                if (property != null)
                                {
                                    if (propertyInfo.Name == nameof(BaseEntity.Modifier))
                                    {
                                        propertyInfo.SetValue(obj, App.User.UserId > 0 ? App.User.UserName : "System");
                                    }
                                    else if (propertyInfo.Name == nameof(BaseEntity.ModifyDate))
                                    {
                                        propertyInfo.SetValue(obj, DateTime.Now);
                                    }
                                    else
                                    {
                                        propertyInfo.SetValue(obj, property.GetValue(item));
                                    }
                                }
                        if (property != null)
                        {
                            if (propertyInfo.Name == nameof(BaseEntity.Modifier))
                            {
                                propertyInfo.SetValue(obj, App.User.UserId > 0 ? App.User.UserName : "System");
                            }
                            list.Add(obj);
                            else if (propertyInfo.Name == nameof(BaseEntity.ModifyDate))
                            {
                                propertyInfo.SetValue(obj, DateTime.Now);
                            }
                            else
                            {
                                propertyInfo.SetValue(obj, property.GetValue(item));
                            }
                        }
                    }
                    if (list.Count > 0)
                        _db.InsertableByObject(list).AS(type.Name + "_Hty").ExecuteCommand();
                    list.Add(obj);
                }
            }
            return DeleteData(entities);
            if (list.Count > 0)
                _db.InsertableByObject(list).AS(type.Name + "_Hty").ExecuteCommand();
        }
    }
    return DeleteData(entities);
}
        //List<TResult> QueryMuch<T, T2, T3, TResult>(
        //    Expression<Func<T, T2, T3, object[]>> joinExpression,
        //    Expression<Func<T, T2, T3, TResult>> selectExpression,
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs
@@ -23,6 +23,7 @@
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Common.CommonEnum;
using WIDESEA_Common.TaskEnum;
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
@@ -44,6 +45,8 @@
        Task<WebResponseContent> TaskCompleted(string taskNum);
        Task TaskStatusChange(string taskNum, TaskStatusEnum taskStatusEnum);
        Task<WebResponseContent> GenerateOutboundTasksAsync(int[] keys, string outStation);
        Task<WebResponseContent> GenerateOutboundTask(int orderDetailId, List<StockSelectViewDTO> stockSelectViews,string station=null);
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITask_HtyService.cs
@@ -15,4 +15,5 @@
public interface ITask_HtyService : IService<Dt_Task_Hty>
{
    bool DeleteAndMoveIntoHty(Dt_Task task, OperateTypeEnum operateType);
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_PickingRecord.cs
@@ -8,11 +8,11 @@
namespace WIDESEA_Model.Models
{
    /// <summary>
    /// æ‹£é€‰è®°å½•表
    /// </summary>
    [SugarTable(nameof(Dt_PickingRecord), "拣选记录表")]
    public class Dt_PickingRecord : BaseEntity
@@ -20,7 +20,7 @@
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public int Id { get; set; }
        public int TaskNo { get; set; }
        public int TaskNo { get; set; }
        public string OrderNo { get; set; }
        public int OrderDetailId { get; set; }
@@ -93,6 +93,8 @@
        public decimal BarcodeMoveQty { get; set; }
        public int ReturnToMESStatus { get; set; }
        public string FeedBackMesDocumentNo { get; set; }
    }
@@ -114,7 +116,7 @@
        /// å›žåº“数量
        /// </summary>
        public decimal ReturnQty { get; set; }
        public DateTime ReturnTime { get; set; }
        /// <summary>
        /// 0-待回库 1-已回库
@@ -126,7 +128,7 @@
    /// æ‹†åŒ…记录表
    /// </summary>
    [SugarTable("Dt_SplitPackageRecord")]
    public class Dt_SplitPackageRecord: BaseEntity
    public class Dt_SplitPackageRecord : BaseEntity
    {
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public int Id { get; set; }
@@ -140,8 +142,8 @@
        public int OutStockLockInfoId { get; set; } // å…³è”的出库锁定信息
        public string OriginalBarcode { get; set; } // åŽŸæ¡ç 
        public string NewBarcode { get; set; } // æ–°æ¡ç 
        public string FactoryArea { get; set; }
        public string FactoryArea { get; set; }
        /// <summary>
        /// æ‹†åˆ†æ•°é‡ï¼ˆæ–°æ¡ç æ•°é‡ï¼‰
        /// </summary>
@@ -162,7 +164,7 @@
        [SugarColumn(IsNullable = true)]
        public decimal? OriginalStockQuantity { get; set; }
       public decimal StockBeforeSplit { get; set; }
        public decimal StockBeforeSplit { get; set; }
        public decimal AssignBeforeSplit { get; set; }
    }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/InterfaceLog.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
using Newtonsoft.Json;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Core.DB.Models;
namespace WIDESEA_Model.Models.Outbound
{
    /// <summary>
    /// æŽ¥å£æ—¥å¿—记录表
    /// </summary>
    [SugarTable("Dt_InterfaceLog")]
    public class Dt_InterfaceLog : BaseEntity
    {
        /// <summary>
        /// ä¸»é”®ID(自增)
        /// </summary>
        [SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)]
        public long Id { get; set; }
        /// <summary>
        /// è®¢å•号
        /// </summary>
        [SugarColumn(ColumnName = "OrderNo", Length = 100, IsNullable = false)]
        [Required(ErrorMessage = "订单号不能为空")]
        public string OrderNo { get; set; }
        /// <summary>
        /// æ–‡æ¡£å·
        /// </summary>
        [SugarColumn(ColumnName = "DocumentNo", Length = 100, IsNullable = true)]
        public string? DocumentNo { get; set; }
        /// <summary>
        /// è®¢å•类型
        /// </summary>
        [SugarColumn(ColumnName = "OrderType", Length = 50, IsNullable = true)]
        [Required(ErrorMessage = "订单类型不能为空")]
        public string OrderType { get; set; }
        /// <summary>
        /// JSON内容
        /// </summary>
        [SugarColumn(ColumnName = "Content", IsNullable = true, ColumnDataType = "nvarchar(max)")]
        [Required(ErrorMessage = "内容不能为空")]
        public string Content { get; set; }
        public int ReturnToMESStatus { get; set; }
        /// <summary>
        /// è½¯åˆ é™¤æ ‡å¿—
        /// </summary>
        [SugarColumn(ColumnName = "IsDeleted", IsNullable = false)]
        public bool IsDeleted { get; set; } = false;
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/NoStockOutModel.cs
@@ -1,5 +1,8 @@
using System;
using Newtonsoft.Json;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -43,4 +46,5 @@
        public List<string> BarCodeSubmit { get; set; }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs
@@ -21,6 +21,7 @@
using WIDESEA_IBasicService;
using WIDESEA_IOutboundService;
using WIDESEA_IStockService;
using WIDESEA_ITaskInfoService;
using WIDESEA_Model.Models;
using WIDESEA_Model.Models.Basic;
using WIDESEA_Model.Models.Outbound;
@@ -49,6 +50,7 @@
        private readonly IDailySequenceService _dailySequenceService;
        private readonly IAllocateService _allocateService;
        private readonly IRepository<Dt_OutboundBatch> _outboundBatchRepository;
        private readonly ITask_HtyService _task_HtyService;
        private readonly ILogger<OutboundPickingService> _logger;
        private Dictionary<string, string> stations = new Dictionary<string, string>
@@ -68,7 +70,7 @@
        public OutboundBatchPickingService(IRepository<Dt_PickingRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService,
            IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService,
            IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService,
            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_OutboundBatch> outboundBatchRepository) : base(BaseDal)
            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_OutboundBatch> outboundBatchRepository, ITask_HtyService task_HtyService) : base(BaseDal)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _stockInfoService = stockInfoService;
@@ -86,6 +88,7 @@
            _dailySequenceService = dailySequenceService;
            _allocateService = allocateService;
            _outboundBatchRepository = outboundBatchRepository;
            _task_HtyService = task_HtyService;
        }
        // <summary>
@@ -1798,7 +1801,7 @@
                // æ›´æ–°é”å®šè®°å½•状态为已回库
                lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
                lockInfo.Operator = App.User.UserName;
                lockInfo.Operator = App.User.UserName;
                await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
@@ -1854,7 +1857,7 @@
                    // æ›´æ–°é”å®šè®°å½•状态为已回库
                    lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
                    lockInfo.Operator = App.User.UserName;
                    lockInfo.Operator = App.User.UserName;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
@@ -1945,9 +1948,17 @@
                    PalletType = stockInfo.PalletType,
                    WarehouseId = currentTask.WarehouseId
                };
                try
                {
                    await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
                await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
                }
                catch (Exception ex)
                {
                    _logger.LogInformation($"创建回库任务失败 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
                    throw new Exception($"创建回库任务失败 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
                }
                // å‘送ESS命令
                await SendESSCommands(palletCode, currentTask.TargetAddress, returnTask);
@@ -2223,7 +2234,7 @@
                    // åˆ†é…æ•°é‡åœ¨è®¢å•明细层面统一处理
                    var originalStatus = lockInfo.Status;
                    lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
                    lockInfo.Operator = App.User.UserName;
                    lockInfo.Operator = App.User.UserName;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
@@ -2550,7 +2561,7 @@
        /// <summary>
        /// å¤„理未分拣的锁定记录回库
        /// </summary>
        /// <summary>
        /// å¤„理托盘上的库存货物回库
@@ -3478,11 +3489,16 @@
            };
            // ä¿å­˜å›žåº“任务
            await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
            var insertcount = await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
            if (insertcount <= 0)
            {
                throw new Exception("创建任务失败!");
            }
            var targetAddress = originalTask.TargetAddress;
            // åˆ é™¤åŽŸå§‹å‡ºåº“ä»»åŠ¡
            _taskRepository.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.自动完成);
            //_taskRepository.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.自动完成);
            _task_HtyService.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.人工删除);
            await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync();
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs
@@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging;
using Autofac.Core;
using Microsoft.Extensions.Logging;
using SqlSugar;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.OrderEnum;
@@ -123,6 +124,10 @@
                // æ›´æ–°å‡ºåº“单明细状态
                UpdateOrderDetailStatus(item.Details, allocatedQuantity, needQuantity);
                // æ›´æ–°åº“存和托盘状态
               UpdateNormalStocksAndPalletsStatus(autoAssignStocks, stockAllocations);
            }
            if (outStocks.Any())
@@ -133,7 +138,27 @@
            return (outStocks, outboundOrderDetails, outStockLockInfos, locationInfos);
        }
        /// <summary>
        /// æ›´æ–°åº“存和托盘状态
        /// </summary>
        private void UpdateNormalStocksAndPalletsStatus(List<Dt_StockInfo> stocks, Dictionary<int, decimal> stockAllocations)
        {
            foreach (var stock in stocks)
            {
                var stockDetail = stock.Details.First();
                if (stockAllocations.TryGetValue(stockDetail.Id, out decimal allocatedQty) && allocatedQty > 0)
                {
                      _stockService.StockInfoService.Db.Updateable<Dt_StockInfo>()
                        .SetColumns(x => new Dt_StockInfo
                        {
                            StockStatus = (int)StockStatusEmun.出库锁定,
                        })
                        .Where(x => x.Id == stock.Id)
                        .ExecuteCommand();
                }
            }
        }
        /// <summary>
        /// æ£€æŸ¥è®¢å•是否允许重新分配
        /// </summary>
@@ -331,6 +356,9 @@
                // æ›´æ–°å‡ºåº“单明细状态
                UpdateOrderDetailStatus(item.Details, allocatedQuantity, needQuantity);
                // æ›´æ–°åº“存和托盘状态为出库锁定
                await UpdateStocksAndPalletsStatus(autoAssignStocks, stockAllocations);
            }
            if (outStocks.Any())
@@ -343,7 +371,27 @@
            return (outStocks, groupDetails.SelectMany(x => x.Details).ToList(), outStockLockInfos, locationInfos);
        }
        /// <summary>
        /// æ›´æ–°åº“存和托盘状态
        /// </summary>
        private async Task UpdateStocksAndPalletsStatus(List<Dt_StockInfo> stocks, Dictionary<int, decimal> stockAllocations)
        {
            foreach (var stock in stocks)
            {
                var stockDetail = stock.Details.First();
                if (stockAllocations.TryGetValue(stockDetail.Id, out decimal allocatedQty) && allocatedQty > 0)
                {
                    await _stockService.StockInfoService.Db.Updateable<Dt_StockInfo>()
                        .SetColumns(x => new Dt_StockInfo
                        {
                            StockStatus = (int)StockStatusEmun.出库锁定,
                        })
                        .Where(x => x.Id == stock.Id)
                        .ExecuteCommandAsync();
                }
            }
        }
        /// <summary>
        /// æŒ‰å…ˆè¿›å…ˆå‡ºåŽŸåˆ™åˆ†é…é”å®šæ•°é‡
        /// </summary>
@@ -588,24 +636,25 @@
                if (userSelection == null) continue;
                // è®¡ç®—该托盘实际可用数量
                var availableQuantity = CalculateAvailableQuantity(stock, outboundOrderDetail.MaterielCode,
                    outboundOrderDetail.BatchNo, outboundOrderDetail.SupplyCode);
                var availableQuantity = CalculateAvailableQuantityByBarcode(stock, outboundOrderDetail.MaterielCode,
           outboundOrderDetail.BatchNo, outboundOrderDetail.SupplyCode, userSelection.Barcode);
                // ç¡®å®šåˆ†é…æ•°é‡ï¼šå–用户选择数量、可用数量和剩余需求的最小值
                var assignQuantity = Math.Min(Math.Min(userSelection.UseableQuantity, availableQuantity),remainingNeedQuantity);
                var assignQuantity = Math.Min(Math.Min(userSelection.UseableQuantity, availableQuantity), remainingNeedQuantity);
                if (assignQuantity <= 0) continue;
                // æ‰§è¡Œåˆ†é…
                var actualAssigned = AssignStockQuantity(stock, outboundOrderDetail, assignQuantity);
                // æ‰§è¡Œåˆ†é…ï¼Œä½¿ç”¨ç”¨æˆ·é€‰æ‹©çš„æ¡ç 
                var actualAssigned = AssignStockQuantity(stock, outboundOrderDetail, assignQuantity, userSelection.Barcode);
                if (actualAssigned > 0)
                {
                    outStocks.Add(stock);
                    totalAssignedFromUserSelection += actualAssigned;
                    remainingNeedQuantity -= actualAssigned;
                    // åˆ›å»ºé”å®šè®°å½•
                    var lockInfo = CreateOutStockLockInfo(outboundOrder, outboundOrderDetail, stock, actualAssigned);
                    // åˆ›å»ºé”å®šè®°å½•,使用用户选择的条码
                    var lockInfo = CreateOutStockLockInfo(outboundOrder, outboundOrderDetail, stock, actualAssigned, userSelection.Barcode);
                    outStockLockInfos.Add(lockInfo);
                }
            }
@@ -664,7 +713,7 @@
            }
            // æ›´æ–°é”å®šæ•°é‡
            outboundOrderDetail.LockQuantity += totalAssignedFromUserSelection + totalAssignedFromUserSelection;
            outboundOrderDetail.LockQuantity += totalAssignedFromUserSelection;
            // æ›´æ–°çŠ¶æ€
            UpdateOrderDetailStatus(outboundOrderDetail, remainingNeedQuantity);
@@ -673,7 +722,25 @@
            return (outStocks, outboundOrderDetail, outStockLockInfos, locationInfos);
        }
        private decimal CalculateAvailableQuantityByBarcode(Dt_StockInfo stock, string materielCode, string batchNo, string supplyCode, string barcode)
        {
            var query = stock.Details.AsQueryable()
                .Where(d => d.MaterielCode == materielCode &&
                           (d.StockQuantity - d.OutboundQuantity) > 0 &&
                           d.Barcode == barcode);
            if (!string.IsNullOrEmpty(batchNo))
            {
                query = query.Where(x => x.BatchNo == batchNo);
            }
            if (!string.IsNullOrEmpty(supplyCode))
            {
                query = query.Where(d => d.SupplyCode == supplyCode);
            }
            return query.Sum(d => d.StockQuantity - d.OutboundQuantity);
        }
        // è¾…助方法
        private decimal CalculateAvailableQuantity(Dt_StockInfo stock, string materielCode, string batchNo, string supplyCode)
        {
@@ -686,9 +753,45 @@
            return relevantDetails.Sum(d => d.StockQuantity - d.OutboundQuantity);
        }
        private decimal AssignStockQuantity(Dt_StockInfo stock, Dt_OutboundOrderDetail detail, decimal assignQuantity)
        private decimal AssignStockQuantity(Dt_StockInfo stock, Dt_OutboundOrderDetail detail, decimal assignQuantity, string barcode)
        {
            decimal remainingAssign = assignQuantity;
            // æŒ‰å…ˆè¿›å…ˆå‡ºåˆ†é…æŒ‡å®šæ¡ç çš„库存明细
            var query = stock.Details.AsQueryable()
                .Where(d => d.MaterielCode == detail.MaterielCode &&
                           (d.StockQuantity - d.OutboundQuantity) > 0 &&
                           d.Barcode == barcode); // åªåˆ†é…æŒ‡å®šæ¡ç 
            if (!string.IsNullOrEmpty(detail.BatchNo))
            {
                query = query.Where(x => x.BatchNo == detail.BatchNo);
            }
            // å¦‚果出库单有供应商要求,按供应商过滤
            if (!string.IsNullOrEmpty(detail.SupplyCode))
            {
                query = query.Where(d => d.SupplyCode == detail.SupplyCode);
            }
            var sortedDetails = query.ToList().OrderBy(d => d.CreateDate);
            foreach (var stockDetail in sortedDetails)
            {
                if (remainingAssign <= 0) break;
                var available = stockDetail.StockQuantity - stockDetail.OutboundQuantity;
                var assign = Math.Min(available, remainingAssign);
                stockDetail.OutboundQuantity += assign;
                remainingAssign -= assign;
            }
            return assignQuantity - remainingAssign; // è¿”回实际分配数量
        }
        private (decimal assignedQuantity, string barcode) AssignStockQuantity(Dt_StockInfo stock, Dt_OutboundOrderDetail detail, decimal assignQuantity)
        {
            decimal remainingAssign = assignQuantity;
            string assignedBarcode = string.Empty;
            // æŒ‰å…ˆè¿›å…ˆå‡ºåˆ†é…åº“存明细
            var query = stock.Details.AsQueryable()
@@ -717,18 +820,20 @@
                stockDetail.OutboundQuantity += assign;
                remainingAssign -= assign;
                if (string.IsNullOrEmpty(assignedBarcode) && assign > 0)
                {
                    assignedBarcode = stockDetail.Barcode;
                }
            }
            return assignQuantity - remainingAssign; // è¿”回实际分配数量
            return (assignQuantity - remainingAssign, assignedBarcode);
        }
        private Dt_OutStockLockInfo CreateOutStockLockInfo(Dt_OutboundOrder outboundOrder, Dt_OutboundOrderDetail detail,
            Dt_StockInfo stock, decimal quantity)
            Dt_StockInfo stock, decimal quantity,string barcode="")
        {
            var barcode = stock.Details
                .Where(d => !string.IsNullOrEmpty(d.Barcode))
                .Select(d => d.Barcode)
                .FirstOrDefault();
            return _outStockLockInfoService.GetOutStockLockInfo(outboundOrder, detail, stock, quantity, barcode);
        }
@@ -792,8 +897,8 @@
                    return (false, $"托盘[{selection.PalletCode}]不存在");
                }
                var available = CalculateAvailableQuantity(stock, outboundOrderDetail.MaterielCode,
                    outboundOrderDetail.BatchNo, outboundOrderDetail.SupplyCode);
                var available = CalculateAvailableQuantityByBarcode(stock, outboundOrderDetail.MaterielCode,
                    outboundOrderDetail.BatchNo, outboundOrderDetail.SupplyCode,selection.Barcode);
                if (available <= 0)
                {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs
@@ -334,6 +334,7 @@
            }
            var data = BaseDal.Db.Queryable<Dt_OutboundOrder>()
                .WhereIF(!wheres.IsNullOrEmpty(), wheres)
                .Where(x => x.OrderType == 0 || x.OrderType == 116)
                .OrderBy(orderByModels)
                .ToPageList(options.Page, options.Rows, ref totalCount);
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs
@@ -32,6 +32,7 @@
using WIDESEA_IInboundService;
using WIDESEA_IOutboundService;
using WIDESEA_IStockService;
using WIDESEA_ITaskInfoService;
using WIDESEA_Model.Models;
using WIDESEA_Model.Models.Basic;
using WIDESEA_Model.Models.Check;
@@ -63,6 +64,7 @@
        private readonly IInboundOrderDetailService _inboundOrderDetailService;
        private readonly IRepository<Dt_WarehouseArea> _warehouseAreaRepository;
        private readonly IReCheckOrderService _reCheckOrderService;
        private readonly ITask_HtyService _task_HtyService;
        private readonly ILogger<OutboundPickingService> _logger;
        private Dictionary<string, string> stations = new Dictionary<string, string>
@@ -82,7 +84,7 @@
        public OutboundPickingService(IRepository<Dt_PickingRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService,
            IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService,
            IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService,
            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_InboundOrder> inboundOrderRepository, IInboundOrderDetailService inboundOrderDetailService, IRepository<Dt_WarehouseArea> warehouseAreaRepository, IReCheckOrderService reCheckOrderService) : base(BaseDal)
            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_InboundOrder> inboundOrderRepository, IInboundOrderDetailService inboundOrderDetailService, IRepository<Dt_WarehouseArea> warehouseAreaRepository, IReCheckOrderService reCheckOrderService, ITask_HtyService task_HtyService) : base(BaseDal)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _stockInfoService = stockInfoService;
@@ -103,6 +105,7 @@
            _inboundOrderDetailService = inboundOrderDetailService;
            _warehouseAreaRepository = warehouseAreaRepository;
            _reCheckOrderService = reCheckOrderService;
            _task_HtyService = task_HtyService;
        }
@@ -1124,7 +1127,7 @@
        {
            // èŽ·å–æœ€æ–°çš„è®¢å•æ˜Žç»†æ•°æ®ï¼ˆå¸¦é”ï¼‰
            var currentOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .With(SqlWith.RowLock)
                //.With(SqlWith.RowLock)
                .FirstAsync(x => x.Id == orderDetailId);
            decimal newOverOutQuantity = currentOrderDetail.OverOutQuantity - cancelQty;
@@ -1684,14 +1687,29 @@
                WarehouseId = originalTask.WarehouseId
            };
            // ä¿å­˜å›žåº“任务
            await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
            try
            {
                // ä¿å­˜å›žåº“任务
                var insertcount = await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
                if (insertcount <= 0)
                {
                    throw new Exception("创建任务失败!");
                }
            }
            catch (Exception ex)
            {
                _logger.LogInformation($"CreateReturnTaskAndHandleESS åˆ›å»ºä»»åŠ¡å¤±è´¥: {orderNo} ï¼Œ {palletCode}");
                throw new Exception("创建任务失败!");
            }
            var targetAddress = originalTask.TargetAddress;
            // åˆ é™¤åŽŸå§‹å‡ºåº“ä»»åŠ¡
            _taskRepository.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.自动完成);
            await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync();
            // _taskRepository.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.自动完成);
            var result = _task_HtyService.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.人工删除);
            if (!result)
            {
                await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync();
            }
            // ç»™ ESS å‘送流动信号和创建任务
@@ -2093,8 +2111,7 @@
            {
                // 1. åˆ é™¤åº“存数量为0的明细记录
                var deleteDetailCount = await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
                    .Where(x => x.StockId == stockId && x.StockQuantity == 0 && (x.Status == StockStatusEmun.出库完成.ObjToInt() || x.Status ==
                                          StockStatusEmun.入库完成.ObjToInt()))
                    .Where(x => x.StockId == stockId && x.StockQuantity == 0)
                    .ExecuteCommandAsync();
                await _stockInfoService.Db.Deleteable<Dt_StockInfo>()
@@ -2753,15 +2770,15 @@
                        TaskNum = 0,
                        Status = (int)OutLockStockStatusEnum.拣选完成,
                        Unit = outboundOrderDetail.Unit,
                        SupplyCode = outboundOrderDetail.SupplyCode?? "无",
                        SupplyCode = outboundOrderDetail.SupplyCode ?? "无",
                        OrderType = outboundOrder.OrderType,
                        CurrentBarcode = inboundOrderDetail.Barcode,
                        IsSplitted = 1,
                        Operator = App.User.UserName,
                        lineNo= outboundOrderDetail.lineNo,
                        lineNo = outboundOrderDetail.lineNo,
                        WarehouseCode = outboundOrderDetail.WarehouseCode ?? "无",
                        BarcodeQty=outboundOrderDetail.NoStockOutQty,
                        BarcodeUnit =outboundOrderDetail.BarcodeUnit,
                        BarcodeQty = outboundOrderDetail.NoStockOutQty,
                        BarcodeUnit = outboundOrderDetail.BarcodeUnit,
                        BatchNo = outboundOrderDetail.BatchNo
                    };
                    _outStockLockInfoService.AddData(newLockInfo);
@@ -2843,18 +2860,18 @@
                var documentNo = UniqueValueGenerator.Generate();
                var outfeedmodel = new FeedbackOutboundRequestModel
                    {
                        reqCode = Guid.NewGuid().ToString(),
                        reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                        business_type = outboundOrder.BusinessType,
                        factoryArea = outboundOrder.FactoryArea,
                        operationType = 1,
                        Operator = App.User.UserName,
                        orderNo = outboundOrder.UpperOrderNo,
                        documentsNO = documentNo,
                        status = outboundOrder.OrderStatus,
                        details = new List<FeedbackOutboundDetailsModel>()
                    };
                {
                    reqCode = Guid.NewGuid().ToString(),
                    reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                    business_type = outboundOrder.BusinessType,
                    factoryArea = outboundOrder.FactoryArea,
                    operationType = 1,
                    Operator = App.User.UserName,
                    orderNo = outboundOrder.UpperOrderNo,
                    documentsNO = documentNo,
                    status = outboundOrder.OrderStatus,
                    details = new List<FeedbackOutboundDetailsModel>()
                };
                foreach (var detail in outboundOrder.Details)
                {
                    // èŽ·å–è¯¥æ˜Žç»†å¯¹åº”çš„æ¡ç ä¿¡æ¯ï¼ˆä»Žé”å®šè®°å½•ï¼‰
@@ -2864,34 +2881,34 @@
                                        (x.Status == (int)OutLockStockStatusEnum.拣选完成 || x.Status == (int)OutLockStockStatusEnum.已回库))
                        .ToListAsync();
                        var groupdata = detailLocks.GroupBy(item => new { item.MaterielCode, item.lineNo, item.BarcodeUnit, item.WarehouseCode })
                              .Select(group => new FeedbackOutboundDetailsModel
                              {
                    var groupdata = detailLocks.GroupBy(item => new { item.MaterielCode, item.lineNo, item.BarcodeUnit, item.WarehouseCode })
                          .Select(group => new FeedbackOutboundDetailsModel
                          {
                                  materialCode = group.Key.MaterielCode,
                                  lineNo = group.Key.lineNo,
                                  warehouseCode = group.Key.WarehouseCode,
                                  qty = group.Sum(x => x.PickedQty),
                                  currentDeliveryQty = group.Sum(x => x.PickedQty),
                                  unit = group.Key.BarcodeUnit,
                                  barcodes = group.Select(lockInfo => new WIDESEA_DTO.Outbound.BarcodesModel
                                  {
                                      barcode = lockInfo.CurrentBarcode,
                                      supplyCode = lockInfo.SupplyCode,
                                      batchNo = lockInfo.BatchNo,
                                      unit = lockInfo.BarcodeUnit,
                                      qty = lockInfo.PickedQty
                                  }).ToList()
                              }).ToList();
                        outfeedmodel.details.AddRange(groupdata);
                    }
                    var result = await _invokeMESService.FeedbackOutbound(outfeedmodel);
                    if (result != null && result.code == 200)
                    {
                        await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                            .SetColumns(x => x.ReturnToMESStatus == 1)
                            .Where(x => x.OrderId == outboundOrder.Id)
                            .ExecuteCommandAsync();
                              materialCode = group.Key.MaterielCode,
                              lineNo = group.Key.lineNo,
                              warehouseCode = group.Key.WarehouseCode,
                              qty = group.Sum(x => x.PickedQty),
                              currentDeliveryQty = group.Sum(x => x.PickedQty),
                              unit = group.Key.BarcodeUnit,
                              barcodes = group.Select(lockInfo => new WIDESEA_DTO.Outbound.BarcodesModel
                              {
                                  barcode = lockInfo.CurrentBarcode,
                                  supplyCode = lockInfo.SupplyCode,
                                  batchNo = lockInfo.BatchNo,
                                  unit = lockInfo.BarcodeUnit,
                                  qty = lockInfo.PickedQty
                              }).ToList()
                          }).ToList();
                    outfeedmodel.details.AddRange(groupdata);
                }
                var result = await _invokeMESService.FeedbackOutbound(outfeedmodel);
                if (result != null && result.code == 200)
                {
                    await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                        .SetColumns(x => x.ReturnToMESStatus == 1)
                        .Where(x => x.OrderId == outboundOrder.Id)
                        .ExecuteCommandAsync();
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(x => x.ReturnToMESStatus == 1)
@@ -2978,6 +2995,8 @@
                    {
                        return content.Error("只有拿到重检结果才能入库!");
                    }
                }
                //  Dt_InboundOrder inboundOrder = GetInboundOrder(materielGroupDTO.OrderNo);
@@ -3003,7 +3022,8 @@
                    stockInfo.Details = new List<Dt_StockInfoDetail>();
                }
                var inboindId = 0; Dt_InboundOrder dt_InboundOrder = null;
                var dbinbound = _inboundOrderRepository.QueryData(x => x.InboundOrderNo == dbinboundOrderDetails.First().OrderNo).First();
                var orderno = dbinboundOrderDetails.First().OrderNo;
                var dbinbound = _inboundOrderRepository.QueryData(x => x.InboundOrderNo == orderno).FirstOrDefault();
                if (dbinbound == null)
                {
                    dt_InboundOrder = new Dt_InboundOrder
@@ -3046,7 +3066,7 @@
                        FactoryArea = item.FactoryArea,
                        Status = 0,
                        OrderNo = item.OrderNo,
                        BusinessType = InOrderTypeEnum.InternalAllocat.ObjToInt().ToString()
                        BusinessType = materielGroupDTO.orderTypes.ToString()
                    });
                    item.WarehouseCode = item.WarehouseCode;
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/WIDESEA_OutboundService.csproj
@@ -15,6 +15,7 @@
    <ProjectReference Include="..\WIDESEA_IOutboundService\WIDESEA_IOutboundService.csproj" />
    <ProjectReference Include="..\WIDESEA_IRecordService\WIDESEA_IRecordService.csproj" />
    <ProjectReference Include="..\WIDESEA_IStockService\WIDESEA_IStockService.csproj" />
    <ProjectReference Include="..\WIDESEA_ITaskInfoService\WIDESEA_ITaskInfoService.csproj" />
  </ItemGroup>
</Project>
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -84,6 +84,7 @@
        private readonly IRecordService _recordService;
        private readonly IAllocateService _allocateService;
        private readonly IInvokeMESService _invokeMESService;
        private readonly ITask_HtyService _task_HtyService;
        public IRepository<Dt_Task> Repository => BaseDal;
        private Dictionary<string, SqlSugar.OrderByType> _taskOrderBy = new()
@@ -103,7 +104,7 @@
        public List<int> TaskOutboundTypes => typeof(TaskTypeEnum).GetEnumIndexList();
        public TaskService(IRepository<Dt_Task> BaseDal, IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_StockInfo> stockRepository, ILocationInfoService locationInfoService, IInboundOrderService inboundOrderService, ILocationStatusChangeRecordService locationStatusChangeRecordService, IESSApiService eSSApiService, ILogger<TaskService> logger, IStockService stockService, IRecordService recordService, IInboundOrderDetailService inboundOrderDetailService, IOutboundOrderService outboundOrderService, IOutboundOrderDetailService outboundOrderDetailService, IInvokeMESService invokeMESService, IOutStockLockInfoService outStockLockInfoService, IAllocateService allocateService, IRepository<Dt_OutboundBatch> outboundBatchRepository, IRepository<Dt_ReCheckOrder> reCheckOrderRepository, IRepository<Dt_AllocateOrderDetail> allocateOrderDetailRepository, IRepository<Dt_AllocateOrder> allocateOrderRepository, IMaterialUnitService materialUnitService) : base(BaseDal)
        public TaskService(IRepository<Dt_Task> BaseDal, IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_StockInfo> stockRepository, ILocationInfoService locationInfoService, IInboundOrderService inboundOrderService, ILocationStatusChangeRecordService locationStatusChangeRecordService, IESSApiService eSSApiService, ILogger<TaskService> logger, IStockService stockService, IRecordService recordService, IInboundOrderDetailService inboundOrderDetailService, IOutboundOrderService outboundOrderService, IOutboundOrderDetailService outboundOrderDetailService, IInvokeMESService invokeMESService, IOutStockLockInfoService outStockLockInfoService, IAllocateService allocateService, IRepository<Dt_OutboundBatch> outboundBatchRepository, IRepository<Dt_ReCheckOrder> reCheckOrderRepository, IRepository<Dt_AllocateOrderDetail> allocateOrderDetailRepository, IRepository<Dt_AllocateOrder> allocateOrderRepository, IMaterialUnitService materialUnitService, ITask_HtyService task_HtyService) : base(BaseDal)
        {
            _mapper = mapper;
            _unitOfWorkManage = unitOfWorkManage;
@@ -126,8 +127,21 @@
            _allocateOrderDetailRepository = allocateOrderDetailRepository;
            _allocateOrderRepository = allocateOrderRepository;
            _materialUnitService = materialUnitService;
            _task_HtyService = task_HtyService;
        }
        public async Task TaskStatusChange(string taskNum,TaskStatusEnum taskStatusEnum)
        {
            if (int.TryParse(taskNum, out var newTaskNum))
            {
                await Db.Updateable<Dt_Task>().SetColumns(it => new Dt_Task {
                        TaskStatus = taskStatusEnum.ObjToInt()
                    })
                    .Where(it => it.TaskNum == newTaskNum)
                    .ExecuteCommandAsync();
            }
        }
        /// <summary>
        /// 
@@ -276,8 +290,12 @@
            task.TaskStatus = TaskStatusEnum.Finish.ObjToInt();
            BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
            //  BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
            var result = _task_HtyService.DeleteAndMoveIntoHty(task, OperateTypeEnum.人工删除);
            if (!result)
            {
                await Db.Deleteable(task).ExecuteCommandAsync();
            }
            _locationStatusChangeRecordService.AddLocationStatusChangeRecord(locationInfo, beforeStatus, StockChangeType.Inbound.ObjToInt(), "", task.TaskNum);
            _recordService.StockQuantityChangeRecordService.AddStockChangeRecord(stockInfo, stockInfo.Details, beforeQuantity, stockInfo.Details.Sum(x => x.StockQuantity) + beforeQuantity, WIDESEA_Common.StockEnum.StockChangeType.MaterielGroup);
@@ -325,8 +343,8 @@
                               }).ToList();
                            allocatefeedmodel.Details = groupedData;
                            var result = await _invokeMESService.FeedbackAllocate(allocatefeedmodel);
                            if (result != null && result.code == 200)
                            var feedbackresult = await _invokeMESService.FeedbackAllocate(allocatefeedmodel);
                            if (feedbackresult != null && feedbackresult.code == 200)
                            {
                                _inboundOrderService.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 1 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
@@ -381,8 +399,8 @@
                               }).ToList();
                            allocatefeedmodel.Details = groupedData;
                            var result = await _invokeMESService.FeedbackAllocate(allocatefeedmodel);
                            if (result != null && result.code == 200)
                            var feedbackresult = await _invokeMESService.FeedbackAllocate(allocatefeedmodel);
                            if (feedbackresult != null && feedbackresult.code == 200)
                            {
                                _inboundOrderService.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 1 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
@@ -429,8 +447,8 @@
                               }).ToList();
                            feedmodel.details = groupedData;
                            var result = await _invokeMESService.FeedbackInbound(feedmodel);
                            if (result != null && result.code == 200)
                            var feedbackresult = await _invokeMESService.FeedbackInbound(feedmodel);
                            if (feedbackresult != null && feedbackresult.code == 200)
                            {
                                _inboundOrderService.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 1 })
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
@@ -536,8 +554,12 @@
                var outboundOrder = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().First(x => x.OrderNo == task.OrderNo);
                task.TaskStatus = TaskStatusEnum.Finish.ObjToInt();
                BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? WIDESEA_Core.Enums.OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
                // BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? WIDESEA_Core.Enums.OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
                var result = _task_HtyService.DeleteAndMoveIntoHty(task, OperateTypeEnum.人工删除);
                if (!result)
                {
                    await Db.Deleteable(task).ExecuteCommandAsync();
                }
                _locationStatusChangeRecordService.AddLocationStatusChangeRecord(locationInfo, beforelocationStatus, StockChangeType.Inbound.ObjToInt(), "", task.TaskNum);
@@ -650,9 +672,13 @@
                task.TaskStatus = TaskStatusEnum.Finish.ObjToInt();
                // åˆ é™¤ä»»åŠ¡è®°å½•
                BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
                BaseDal.DeleteData(task);
                //BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
                //BaseDal.DeleteData(task);
                var result = _task_HtyService.DeleteAndMoveIntoHty(task, OperateTypeEnum.人工删除);
                if (!result)
                {
                    await Db.Deleteable(task).ExecuteCommandAsync();
                }
                // è®°å½•货位状态变更
                _locationStatusChangeRecordService.AddLocationStatusChangeRecord(
                    locationInfo,
@@ -1105,7 +1131,7 @@
                        //不用回传
                    }
                    else
                    {
                    {
                        if (outboundOrder != null && outboundOrder.IsBatch == 0)
                        {
                            var feedmodel = new FeedbackOutboundRequestModel
@@ -1187,7 +1213,7 @@
                        }
                        else if (outboundOrder != null && outboundOrder.IsBatch == 1)
                        {
                           await  _invokeMESService.BatchOrderFeedbackToMes(new List<string>(){outboundOrder.OrderNo },2);
                            await _invokeMESService.BatchOrderFeedbackToMes(new List<string>() { outboundOrder.OrderNo }, 2);
                        }
                    }
@@ -1227,9 +1253,18 @@
                task.TaskStatus = TaskStatusEnum.Finish.ObjToInt();
                BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
                _stockService.StockInfoService.Repository.DeleteAndMoveIntoHty(stockInfo, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
                //_stockRepository.Db.Deleteable(stockInfo).ExecuteCommand();
                // BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
                var result = _task_HtyService.DeleteAndMoveIntoHty(task, OperateTypeEnum.人工删除);
                if (!result)
                {
                    await Db.Deleteable(task).ExecuteCommandAsync();
                }
                var stockresult = _stockService.StockInfoService.Repository.DeleteAndMoveIntoHty(stockInfo, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
                if (!stockresult)
                {
                    _stockRepository.Db.Deleteable(stockInfo).ExecuteCommand();
                }
                _stockService.StockInfoService.DeleteData(stockInfo);
                _locationStatusChangeRecordService.AddLocationStatusChangeRecord(locationInfo, beforeStatus, StockChangeType.Outbound.ObjToInt(), stockInfo.Details.FirstOrDefault()?.OrderNo ?? "", task.TaskNum);
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs
@@ -2,6 +2,7 @@
using Newtonsoft.Json;
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -148,42 +149,73 @@
            List<Dt_LocationInfo>? locationInfos = null;
            CleanupPreviousInvalidLocks(outboundOrderDetails);
            (List<Dt_StockInfo>, List<Dt_OutboundOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) result = _outboundOrderDetailService.AssignStockOutbound(outboundOrderDetails);
            if (result.Item1 != null && result.Item1.Count > 0)
            // å¼€å¯äº‹åŠ¡ï¼Œä½¿ç”¨æ•°æ®åº“è¡Œçº§é”
            using (var transaction = _outboundOrderDetailService.Db.Ado.UseTran())
            {
                Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId);
                TaskTypeEnum typeEnum = outboundOrder.OrderType switch
                try
                {
                    (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound,
                    (int)OutOrderTypeEnum.Allocate => TaskTypeEnum.OutAllocate,
                    (int)OutOrderTypeEnum.Quality => TaskTypeEnum.OutQuality,
                    _ => TaskTypeEnum.Outbound
                };
                tasks = GetTasks(result.Item1, typeEnum, outStation);
                tasks.ForEach(x =>
                {
                    x.OrderNo = outboundOrder.OrderNo;
                });
                result.Item2.ForEach(x =>
                {
                    x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
                });
                result.Item3.ForEach(x =>
                {
                    x.Status = OutLockStockStatusEnum.出库中.ObjToInt();
                });
                    // ä½¿ç”¨æ‚²è§‚锁锁定订单明细
                    var lockedOrderDetails = new List<Dt_OutboundOrderDetail>();
                    foreach (var key in keys)
                    {
                        var detail = _outboundOrderDetailService.Db.Ado.SqlQuerySingle<Dt_OutboundOrderDetail>(
                            "SELECT * FROM Dt_OutboundOrderDetail WITH (UPDLOCK) WHERE Id = @Id",
                            new { Id = key });
                stockInfos = result.Item1;
                orderDetails = result.Item2;
                outStockLockInfos = result.Item3;
                locationInfos = result.Item4;
                        if (detail != null)
                        {
                            lockedOrderDetails.Add(detail);
                        }
                    }
                    if (!lockedOrderDetails.Any())
                    {
                        throw new Exception("未找到出库单明细信息");
                    }
                    (List<Dt_StockInfo>, List<Dt_OutboundOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) result = _outboundOrderDetailService.AssignStockOutbound(outboundOrderDetails);
                    if (result.Item1 != null && result.Item1.Count > 0)
                    {
                        Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId);
                        TaskTypeEnum typeEnum = outboundOrder.OrderType switch
                        {
                            (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound,
                            (int)OutOrderTypeEnum.Allocate => TaskTypeEnum.OutAllocate,
                            (int)OutOrderTypeEnum.Quality => TaskTypeEnum.OutQuality,
                            _ => TaskTypeEnum.Outbound
                        };
                        tasks = GetTasks(result.Item1, typeEnum, outStation);
                        tasks.ForEach(x =>
                        {
                            x.OrderNo = outboundOrder.OrderNo;
                        });
                        result.Item2.ForEach(x =>
                        {
                            x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
                        });
                        result.Item3.ForEach(x =>
                        {
                            x.Status = OutLockStockStatusEnum.出库中.ObjToInt();
                        });
                        stockInfos = result.Item1;
                        orderDetails = result.Item2;
                        outStockLockInfos = result.Item3;
                        locationInfos = result.Item4;
                        transaction.CommitTran();
                    }
                    else
                    {
                        transaction.RollbackTran();
                        throw new Exception("无库存");
                    }
                }
                catch (Exception)
                {
                    transaction.RollbackTran();
                    throw;
                }
                return (tasks, stockInfos, orderDetails, outStockLockInfos, locationInfos);
            }
            else
            {
                throw new Exception("无库存");
            }
            return (tasks, stockInfos, orderDetails, outStockLockInfos, locationInfos);
        }
        /// <summary>
        /// æ¸…理之前的无效锁定记录
@@ -355,7 +387,6 @@
                            // TaskNum = BaseDal.GetTaskNum(nameof(SequenceEnum.SeqTaskNum)),
                            PalletType = stockInfo.PalletType,
                            WarehouseId = stockInfo.WarehouseId,
                        };
                        //if (taskType != TaskTypeEnum.OutEmpty)
                        //{
@@ -371,52 +402,67 @@
            return tasks;
        }
        public List<Dt_Task> GetTasks(List<Dt_StockInfo> stockInfos, TaskTypeEnum taskType)
        #region å†…存锁管理器
        private static readonly ConcurrentDictionary<string, SemaphoreSlim> _normalmaterialLocks =
            new ConcurrentDictionary<string, SemaphoreSlim>();
        private static readonly ConcurrentDictionary<string, DateTime> _normallockLastUsed =
            new ConcurrentDictionary<string, DateTime>();
        private static readonly object _normalcleanupLock = new object();
        private static DateTime _normallastCleanupTime = DateTime.MinValue;
        /// <summary>
        /// èŽ·å–ç‰©æ–™çº§å†…å­˜é”
        /// </summary>
        private SemaphoreSlim GetNormalMaterialSemaphore(string materialCode, string batchNo, string supplyCode)
        {
            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++)
            // åˆ›å»ºé”é”®ï¼šç‰©æ–™+批次+供应商
            string lockKey = $"MaterialLock_{materialCode}_{batchNo}_{supplyCode}";
            // æ¸…理长时间不用的锁(每小时清理一次)
            var now = DateTime.Now;
            if ((now - _normallastCleanupTime).TotalHours >= 1)
            {
                Dt_StockInfo stockInfo = stockInfos[i];
                if (stockInfo != null)
                lock (_normalcleanupLock)
                {
                    Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == stockInfo.LocationCode);
                    if (!tasks.Exists(x => x.PalletCode == stockInfo.PalletCode))
                    if ((now - _normallastCleanupTime).TotalHours >= 1)
                    {
                        Dt_Task task = new()
                        {
                            CurrentAddress = stockInfo.LocationCode,
                            Grade = 0,
                            PalletCode = stockInfo.PalletCode,
                            NextAddress = "",
                            Roadway = locationInfo.RoadwayNo,
                            SourceAddress = stockInfo.LocationCode,
                            TargetAddress = "",
                            TaskStatus = TaskStatusEnum.New.ObjToInt(),
                            TaskType = taskType.ObjToInt(),
                            //TaskNum = BaseDal.GetTaskNum(nameof(SequenceEnum.SeqTaskNum)),
                            PalletType = stockInfo.PalletType,
                            WarehouseId = stockInfo.WarehouseId,
                        var keysToRemove = _normallockLastUsed
                            .Where(kvp => (now - kvp.Value).TotalHours > 2)
                            .Select(kvp => kvp.Key)
                            .ToList();
                        };
                        //if (taskType != TaskTypeEnum.OutEmpty)
                        //{
                        //    task.MaterielCode = stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).FirstOrDefault()?.MaterielCode;
                        //    task.Quantity = (float)stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).Sum(x => x.StockQuantity);
                        //    task.BatchNo = stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).FirstOrDefault()?.BatchNo;
                        //}
                        //if (stockInfo.StockLength > 0)
                        //{
                        //    task.TaskLength = stockInfo.StockLength;
                        //}
                        tasks.Add(task);
                        foreach (var key in keysToRemove)
                        {
                            if (_normalmaterialLocks.TryRemove(key, out var _semaphore))
                            {
                                _semaphore.Dispose();
                            }
                            _normallockLastUsed.TryRemove(key, out _);
                        }
                        _normallastCleanupTime = now;
                    }
                }
            }
            return tasks;
            // èŽ·å–æˆ–åˆ›å»ºä¿¡å·é‡
            var semaphore = _normalmaterialLocks.GetOrAdd(lockKey, _ => new SemaphoreSlim(1, 1));
            _normallockLastUsed[lockKey] = now;
            return semaphore;
        }
        /// <summary>
        /// æ›´æ–°å†…存锁最后使用时间
        /// </summary>
        private void UpdateNormalMaterialLockUsedTime(string materialCode, string batchNo, string supplyCode)
        {
            string lockKey = $"MaterialLock_{materialCode}_{batchNo}_{supplyCode}";
            _normallockLastUsed[lockKey] = DateTime.Now;
        }
        #endregion
        /// <summary>
        /// ç”Ÿæˆå‡ºåº“任务
        /// </summary>
@@ -432,31 +478,90 @@
                List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>();
                List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
                List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
                (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = OutboundTaskDataHandle(keys, outStation);
                if (result.Item2 != null && result.Item2.Count > 0)
                // å…ˆèŽ·å–æ‰€æœ‰è®¢å•æ˜Žç»†ï¼Œç¡®å®šéœ€è¦é”å®šçš„ç‰©æ–™
                var orderDetails = _outboundOrderDetailService.Repository.QueryData(x => keys.Contains(x.Id));
                if (orderDetails == null || orderDetails.Count == 0)
                {
                    stockInfos.AddRange(result.Item2);
                }
                if (result.Item3 != null && result.Item3.Count > 0)
                {
                    outboundOrderDetails.AddRange(result.Item3);
                }
                if (result.Item4 != null && result.Item4.Count > 0)
                {
                    outStockLockInfos.AddRange(result.Item4);
                }
                if (result.Item5 != null && result.Item5.Count > 0)
                {
                    locationInfos.AddRange(result.Item5);
                }
                if (result.Item1 != null && result.Item1.Count > 0)
                {
                    tasks.AddRange(result.Item1);
                    return WebResponseContent.Instance.Error("未找到出库单明细信息");
                }
                WebResponseContent content = await GenerateOutboundTaskDataUpdateAsync(tasks, stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos);
                return content;
                // èŽ·å–æ‰€æœ‰éœ€è¦é”å®šçš„ç‰©æ–™åˆ†ç»„
                var materialGroups = orderDetails
                    .GroupBy(x => new { x.MaterielCode, x.BatchNo, x.SupplyCode })
                    .Select(g => new
                    {
                        g.Key.MaterielCode,
                        g.Key.BatchNo,
                        g.Key.SupplyCode,
                        Count = g.Count()
                    })
                    .ToList();
                // æŒ‰é¡ºåºèŽ·å–æ‰€æœ‰ç‰©æ–™çš„å†…å­˜é”ï¼ˆæŒ‰ç‰©æ–™ä»£ç æŽ’åºä»¥é¿å…æ­»é”ï¼‰
                var semaphores = new List<SemaphoreSlim>();
                var acquiredLocks = new List<(string MaterialCode, string BatchNo, string SupplyCode)>();
                try
                {
                    foreach (var group in materialGroups.OrderBy(g => g.MaterielCode).ThenBy(g => g.BatchNo))
                    {
                        var semaphore = GetMaterialSemaphore(group.MaterielCode, group.BatchNo, group.SupplyCode);
                        // ç­‰å¾…获取锁,最多等待30秒
                        bool lockAcquired = await semaphore.WaitAsync(TimeSpan.FromSeconds(30));
                        if (!lockAcquired)
                        {
                            // å¦‚果获取锁失败,释放已获取的所有锁
                            foreach (var acquiredSemaphore in semaphores)
                            {
                                acquiredSemaphore.Release();
                            }
                            return WebResponseContent.Instance.Error($"物料[{group.MaterielCode}]批次[{group.BatchNo}]分配繁忙,请稍后重试");
                        }
                        semaphores.Add(semaphore);
                        acquiredLocks.Add((group.MaterielCode, group.BatchNo, group.SupplyCode));
                    }
                    (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = OutboundTaskDataHandle(keys, outStation);
                    if (result.Item2 != null && result.Item2.Count > 0)
                    {
                        stockInfos.AddRange(result.Item2);
                    }
                    if (result.Item3 != null && result.Item3.Count > 0)
                    {
                        outboundOrderDetails.AddRange(result.Item3);
                    }
                    if (result.Item4 != null && result.Item4.Count > 0)
                    {
                        outStockLockInfos.AddRange(result.Item4);
                    }
                    if (result.Item5 != null && result.Item5.Count > 0)
                    {
                        locationInfos.AddRange(result.Item5);
                    }
                    if (result.Item1 != null && result.Item1.Count > 0)
                    {
                        tasks.AddRange(result.Item1);
                    }
                    WebResponseContent content = await GenerateOutboundTaskDataUpdateAsync(tasks, stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos);
                    return content;
                }
                finally
                {
                    // é‡Šæ”¾æ‰€æœ‰å†…存锁并更新使用时间
                    foreach (var semaphore in semaphores)
                    {
                        semaphore.Release();
                    }
                    foreach (var lockInfo in acquiredLocks)
                    {
                        UpdateMaterialLockUsedTime(lockInfo.MaterialCode, lockInfo.BatchNo, lockInfo.SupplyCode);
                    }
                }
            }
            catch (Exception ex)
            {
@@ -491,17 +596,17 @@
                {
                    return WebResponseContent.Instance.Error("找不到出库单据");
                }
                var orderdetail = outboundOrder.Details.Where(outItem => allocorder.Details .Any(allocItem => allocItem.MaterielCode == outItem.MaterielCode  && allocItem.LineNo == outItem.lineNo
                 && allocItem.BarcodeQty==outItem.BarcodeQty && allocItem.WarehouseCode==outItem.WarehouseCode && allocItem.BarcodeUnit==outItem.BarcodeUnit) ) .First();
                var orderdetail = outboundOrder.Details.Where(outItem => allocorder.Details.Any(allocItem => allocItem.MaterielCode == outItem.MaterielCode && allocItem.LineNo == outItem.lineNo
                 && allocItem.BarcodeQty == outItem.BarcodeQty && allocItem.WarehouseCode == outItem.WarehouseCode && allocItem.BarcodeUnit == outItem.BarcodeUnit)).First();
                if (orderdetail == null)
                {
                    return WebResponseContent.Instance.Error("找不到出库明细单据");
                }
                (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = OutboundTaskDataHandle(outboundOrder.Details.First().Id, stockSelectViews,station);
                (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = OutboundTaskDataHandle(outboundOrder.Details.First().Id, stockSelectViews, station);
                WebResponseContent content =await GenerateOutboundTaskDataUpdate(result.Item1, result.Item2, result.Item3, result.Item4, result.Item5);
                WebResponseContent content = await GenerateOutboundTaskDataUpdate(result.Item1, result.Item2, result.Item3, result.Item4, result.Item5);
                return content;
            }
@@ -516,7 +621,7 @@
        /// <param name="orderDetailId"></param>
        /// <param name="stockSelectViews"></param>
        /// <returns></returns>
        public async Task<WebResponseContent> GenerateOutboundTask(int orderDetailId, List<StockSelectViewDTO> stockSelectViews,string station=null)
        public async Task<WebResponseContent> GenerateOutboundTask(int orderDetailId, List<StockSelectViewDTO> stockSelectViews, string station = null)
        {
            try
            {
@@ -526,9 +631,9 @@
                {
                    return WebResponseContent.Instance.Error("找不到单据");
                }
                (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = OutboundTaskDataHandle(outboundOrder.Details.First().Id, stockSelectViews,station);
                (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = OutboundTaskDataHandle(outboundOrder.Details.First().Id, stockSelectViews, station);
                WebResponseContent content =await GenerateOutboundTaskDataUpdate(result.Item1, result.Item2, result.Item3, result.Item4, result.Item5);
                WebResponseContent content = await GenerateOutboundTaskDataUpdate(result.Item1, result.Item2, result.Item3, result.Item4, result.Item5);
                return content;
            }
@@ -545,7 +650,7 @@
        /// <param name="stockSelectViews"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) OutboundTaskDataHandle(int orderDetailId, List<StockSelectViewDTO> stockSelectViews,string station=null)
        public (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) OutboundTaskDataHandle(int orderDetailId, List<StockSelectViewDTO> stockSelectViews, string station = null)
        {
            List<Dt_Task> tasks = new List<Dt_Task>();
            Dt_OutboundOrderDetail outboundOrderDetail = _outboundOrderDetailService.Repository.QueryFirst(x => x.Id == orderDetailId);
@@ -563,12 +668,13 @@
            Dt_OutboundOrderDetail? orderDetail = null;
            List<Dt_OutStockLockInfo>? outStockLockInfos = null;
            List<Dt_LocationInfo>? locationInfos = null;
            if (outboundOrderDetail.OrderDetailStatus == OrderDetailStatusEnum.New.ObjToInt())
            if (outboundOrderDetail.OrderDetailStatus == OrderDetailStatusEnum.New.ObjToInt() ||
                outboundOrderDetail.OrderDetailStatus == OrderDetailStatusEnum.Outbound.ObjToInt())
            {
                (List<Dt_StockInfo>, Dt_OutboundOrderDetail, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) result = _outboundOrderDetailService.AssignStockOutbound(outboundOrderDetail, stockSelectViews);
                if (result.Item1 != null && result.Item1.Count > 0)
                {
                    Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetail.OrderId);
                    Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetail.OrderId);
                    TaskTypeEnum typeEnum = outboundOrder.OrderType switch
                    {
                        (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound,
@@ -576,7 +682,7 @@
                        (int)OutOrderTypeEnum.Quality => TaskTypeEnum.OutQuality,
                        _ => TaskTypeEnum.Outbound
                    };
                    tasks = GetTasks(result.Item1, typeEnum,station);
                    tasks = GetTasks(result.Item1, typeEnum, station);
                    result.Item2.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
                    result.Item3.ForEach(x =>
                    {
@@ -599,7 +705,7 @@
                if (stockLockInfos != null && stockLockInfos.Count > 0)
                {
                    List<Dt_StockInfo> stocks = _stockService.StockInfoService.GetStockInfosByPalletCodes(stockLockInfos.Select(x => x.PalletCode).Distinct().ToList());
                    tasks = GetTasks(stocks, TaskTypeEnum.Outbound);
                    tasks = GetTasks(stocks, TaskTypeEnum.Outbound,station);
                }
            }
@@ -615,7 +721,7 @@
        /// <param name="outStockLockInfos"></param>
        /// <param name="locationInfos"></param>
        /// <returns></returns>
        public async Task< WebResponseContent> GenerateOutboundTaskDataUpdate(List<Dt_Task> tasks, List<Dt_StockInfo>? stockInfos = null, List<Dt_OutboundOrderDetail>? outboundOrderDetails = null, List<Dt_OutStockLockInfo>? outStockLockInfos = null, List<Dt_LocationInfo>? locationInfos = null)
        public async Task<WebResponseContent> GenerateOutboundTaskDataUpdate(List<Dt_Task> tasks, List<Dt_StockInfo>? stockInfos = null, List<Dt_OutboundOrderDetail>? outboundOrderDetails = null, List<Dt_OutStockLockInfo>? outStockLockInfos = null, List<Dt_LocationInfo>? locationInfos = null)
        {
            try
            {
@@ -699,7 +805,7 @@
                {
                    return WebResponseContent.Instance.Error("下发机器人任务失败!");
                }
            }
            catch (Exception ex)
            {
@@ -711,7 +817,65 @@
        #region åˆ†æ‰¹åˆ†é…åº“å­˜
        #region å†…存锁管理器
        private static readonly ConcurrentDictionary<string, SemaphoreSlim> _materialLocks =
            new ConcurrentDictionary<string, SemaphoreSlim>();
        private static readonly ConcurrentDictionary<string, DateTime> _lockLastUsed =
            new ConcurrentDictionary<string, DateTime>();
        private static readonly object _cleanupLock = new object();
        private static DateTime _lastCleanupTime = DateTime.MinValue;
        /// <summary>
        /// èŽ·å–ç‰©æ–™çº§å†…å­˜é”
        /// </summary>
        private SemaphoreSlim GetMaterialSemaphore(string materialCode, string batchNo, string supplyCode)
        {
            // åˆ›å»ºé”é”®ï¼šç‰©æ–™+批次+供应商
            string lockKey = $"MaterialLock_{materialCode}_{batchNo}_{supplyCode}";
            // æ¸…理长时间不用的锁(每小时清理一次)
            var now = DateTime.Now;
            if ((now - _lastCleanupTime).TotalHours >= 1)
            {
                lock (_cleanupLock)
                {
                    if ((now - _lastCleanupTime).TotalHours >= 1)
                    {
                        var keysToRemove = _lockLastUsed
                            .Where(kvp => (now - kvp.Value).TotalHours > 2)
                            .Select(kvp => kvp.Key)
                            .ToList();
                        foreach (var key in keysToRemove)
                        {
                            if (_materialLocks.TryRemove(key, out var _semaphore))
                            {
                                _semaphore.Dispose();
                            }
                            _lockLastUsed.TryRemove(key, out _);
                        }
                        _lastCleanupTime = now;
                    }
                }
            }
            // èŽ·å–æˆ–åˆ›å»ºä¿¡å·é‡
            var semaphore = _materialLocks.GetOrAdd(lockKey, _ => new SemaphoreSlim(1, 1));
            _lockLastUsed[lockKey] = now;
            return semaphore;
        }
        /// <summary>
        /// é‡Šæ”¾å†…存锁并更新最后使用时间
        /// </summary>
        private void UpdateMaterialLockUsedTime(string materialCode, string batchNo, string supplyCode)
        {
            string lockKey = $"MaterialLock_{materialCode}_{batchNo}_{supplyCode}";
            _lockLastUsed[lockKey] = DateTime.Now;
        }
        #endregion
        /// <summary>
        /// åˆ†æ‰¹åˆ†é…åº“å­˜
        /// </summary>
@@ -719,37 +883,62 @@
        {
            try
            {
                List<Dt_Task> tasks = new List<Dt_Task>();
                List<Dt_StockInfo> stockInfos = new List<Dt_StockInfo>();
                List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>();
                List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
                List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
                // å…ˆèŽ·å–è®¢å•æ˜Žç»†ä¿¡æ¯ï¼Œç¡®å®šç‰©æ–™
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == orderDetailId);
                (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = await BatchAllocateStockDataHandle(orderDetailId, batchQuantity, outStation);
                if (orderDetail == null)
                    return WebResponseContent.Instance.Error("未找到订单明细信息");
                if (result.Item2 != null && result.Item2.Count > 0)
                {
                    stockInfos.AddRange(result.Item2);
                }
                if (result.Item3 != null && result.Item3.Count > 0)
                {
                    outboundOrderDetails.AddRange(result.Item3);
                }
                if (result.Item4 != null && result.Item4.Count > 0)
                {
                    outStockLockInfos.AddRange(result.Item4);
                }
                if (result.Item5 != null && result.Item5.Count > 0)
                {
                    locationInfos.AddRange(result.Item5);
                }
                if (result.Item1 != null && result.Item1.Count > 0)
                {
                    tasks.AddRange(result.Item1);
                }
                // èŽ·å–ç‰©æ–™çº§å†…å­˜é”
                var semaphore = GetMaterialSemaphore(orderDetailId + orderDetail.MaterielCode, orderDetail.BatchNo, orderDetail.SupplyCode);
                WebResponseContent content = await GenerateOutboundTaskDataUpdateAsync(tasks, stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos);
                return content;
                // ç­‰å¾…获取内存锁,最多等待30秒
                bool memoryLockAcquired = await semaphore.WaitAsync(TimeSpan.FromSeconds(30));
                if (!memoryLockAcquired)
                    return WebResponseContent.Instance.Error("系统繁忙,请稍后重试");
                try
                {
                    List<Dt_Task> tasks = new List<Dt_Task>();
                    List<Dt_StockInfo> stockInfos = new List<Dt_StockInfo>();
                    List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>();
                    List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
                    List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
                    (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = await BatchAllocateStockDataHandle(orderDetailId, batchQuantity, outStation);
                    if (result.Item2 != null && result.Item2.Count > 0)
                    {
                        stockInfos.AddRange(result.Item2);
                    }
                    if (result.Item3 != null && result.Item3.Count > 0)
                    {
                        outboundOrderDetails.AddRange(result.Item3);
                    }
                    if (result.Item4 != null && result.Item4.Count > 0)
                    {
                        outStockLockInfos.AddRange(result.Item4);
                    }
                    if (result.Item5 != null && result.Item5.Count > 0)
                    {
                        locationInfos.AddRange(result.Item5);
                    }
                    if (result.Item1 != null && result.Item1.Count > 0)
                    {
                        tasks.AddRange(result.Item1);
                    }
                    WebResponseContent content = await GenerateOutboundTaskDataUpdateAsync(tasks, stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos);
                    return content;
                }
                finally
                {
                    // é‡Šæ”¾å†…存锁
                    semaphore.Release();
                    UpdateMaterialLockUsedTime(orderDetail.MaterielCode, orderDetail.BatchNo, orderDetail.SupplyCode);
                }
            }
            catch (Exception ex)
            {
@@ -768,7 +957,7 @@
            List<Dt_Task> tasks = new List<Dt_Task>();
            // èŽ·å–è®¢å•æ˜Žç»†
            var outboundOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
            var outboundOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().With("UPDLOCK")
                .FirstAsync(x => x.Id == orderDetailId);
            if (outboundOrderDetail == null)
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/Task_HtyService.cs
@@ -1,5 +1,10 @@
using WIDESEA_Core.BaseRepository;
using AutoMapper;
using Microsoft.Extensions.Logging;
using System.Transactions;
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_Core.Enums;
using WIDESEA_ITaskInfoService;
using WIDESEA_Model.Models;
@@ -7,7 +12,58 @@
public class Task_HtyService : ServiceBase<Dt_Task_Hty, IRepository<Dt_Task_Hty>>, ITask_HtyService
{
    public Task_HtyService(IRepository<Dt_Task_Hty> BaseDal) : base(BaseDal)
    private readonly ILogger<Task_HtyService> _logger;
    private readonly IMapper _mapper; // AutoMapper实例
    public Task_HtyService(IRepository<Dt_Task_Hty> BaseDal, IMapper mapper, ILogger<Task_HtyService> logger) : base(BaseDal)
    {
        _mapper = mapper;
        _logger = logger;
    }
    public bool DeleteAndMoveIntoHty(Dt_Task task, OperateTypeEnum operateType)
    {
       // using var transaction = Db.Ado.UseTran();
        try
        {
           var historyEntity = _mapper.Map<Dt_Task_Hty>(task);
            // 2. å¡«å……历史实体核心字段
            historyEntity.SourceId = task.TaskId;
            historyEntity.OperateType =  App.User?.UserName != null ? OperateTypeEnum.自动完成.ToString() : OperateTypeEnum.人工完成.ToString();
            historyEntity.Creater = App.User?.UserName != null ? App.User.UserName : "System";
            // è¦†ç›–修改人/修改时间(优先级高于映射)
           // historyEntity.Modifier = App.User?.UserId > 0 ? App.User?.UserName : "System";
           // historyEntity.ModifyDate = DateTime.Now;
            // 3. æ’入历史表(类型安全,无反射拼接表名)
            int insertCount = Db.Insertable(historyEntity).ExecuteCommand();
            if (insertCount <= 0)
            {
                _logger.LogError($"任务历史表Dt_Task_Hty [{task.TaskNum}]插入失败,影响行数为0");
               // transaction.RollbackTran();
                return false;
            }
            // 4. åˆ é™¤åŽŸå®žä½“ï¼ˆç±»åž‹å®‰å…¨ï¼‰
            int deleteCount = Db.Deleteable(task).ExecuteCommand();
            if (deleteCount <= 0)
            {
                _logger.LogError("任务业务实体[{0}]删除失败,影响行数为0", task.TaskNum);
              //  transaction.RollbackTran();
                return false;
            }
            //transaction.CommitTran();
            _logger.LogInformation("任务实体[{0}]已成功移入历史表[{1}]并删除原数据", task.TaskNum);
            return true;
        }
        catch (Exception ex)
        {
           // transaction.RollbackTran();
            _logger.LogError(ex, "任务删除实体[{0}]并移入历史表失败", task.TaskNum);
            return false;
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/ESSController.cs
@@ -287,8 +287,7 @@
        /// </summary>
        private async Task HandleTaskSuspended(StatusCallbackRequest request)
        {
            _logger.LogWarning("任务挂起: TaskCode={TaskCode}, ç³»ç»Ÿä»»åŠ¡ç ={SysTaskCode}, åŽŸå› ={Message}",
                request.TaskCode, request.SysTaskCode, request.Message);
            _logger.LogWarning("任务挂起: TaskCode={TaskCode}, ç³»ç»Ÿä»»åŠ¡ç ={SysTaskCode}, åŽŸå› ={Message}",request.TaskCode, request.SysTaskCode, request.Message);
            // è¿™é‡Œæ·»åŠ æ‚¨çš„æŒ‚èµ·å¤„ç†é€»è¾‘
            await Task.CompletedTask;
@@ -299,8 +298,7 @@
        /// </summary>
        private async Task HandleTaskAllocated(StatusCallbackRequest request)
        {
            _logger.LogInformation("任务分配: TaskCode={TaskCode}, Robot={Robot}",
                request.TaskCode, request.RobotCode);
            _logger.LogInformation("任务分配: TaskCode={TaskCode}, Robot={Robot}",request.TaskCode, request.RobotCode);
            // è¿™é‡Œæ·»åŠ æ‚¨çš„ä»»åŠ¡åˆ†é…å¤„ç†é€»è¾‘
            await Task.CompletedTask;
@@ -313,8 +311,9 @@
        {
            if (request.Status == WIDESEA_DTO.Basic.TaskStatus.success)
            {
                _logger.LogInformation("取箱完成: Container={Container}, Location={Location}",
                    request.ContainerCode, request.LocationCode);
                _logger.LogInformation("取箱完成: Container={Container}, Location={Location}", request.ContainerCode, request.LocationCode);
                await _taskService.TaskStatusChange(request.TaskCode, WIDESEA_Common.TaskEnum.TaskStatusEnum.AGV_Pull);
            }
            else
            {
@@ -333,8 +332,8 @@
        {
            if (request.Status == WIDESEA_DTO.Basic.TaskStatus.success)
            {
                _logger.LogInformation("放箱完成: Container={Container}, Location={Location}",
                    request.ContainerCode, request.LocationCode);
                _logger.LogInformation("放箱完成: Container={Container}, Location={Location}", request.ContainerCode, request.LocationCode);
                await _taskService.TaskStatusChange(request.TaskCode, WIDESEA_Common.TaskEnum.TaskStatusEnum.AGV_Puting);
            }
            else
            {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Inbound/InboundOrderController.cs
@@ -13,6 +13,8 @@
using WIDESEA_Core;
using WIDESEA_Core.Attributes;
using WIDESEA_Core.BaseController;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.Enums;
using WIDESEA_DTO.Allocate;
using WIDESEA_DTO.Inbound;
using WIDESEA_DTO.Mes;
@@ -20,6 +22,7 @@
using WIDESEA_IInboundService;
using WIDESEA_InboundService;
using WIDESEA_IOutboundService;
using WIDESEA_ITaskInfoService;
using WIDESEA_Model.Models;
using WIDESEA_OutboundService;
@@ -42,8 +45,10 @@
        private readonly IMaterialUnitService _materialUnitService;
        private readonly IOutStockLockInfoService _outStockLockInfoService;
        private readonly IOutboundOrderDetailService _outboundOrderDetailService;
        private readonly IRepository<Dt_Task> _taskRepository;
        private readonly ITask_HtyService _task_HtyService;
        private readonly ILogger<InboundOrderController> _logger;
        public InboundOrderController(IInboundOrderService service, WIDESEA_IBasicService.IErpApiService erpApiService, WIDESEA_IBasicService.IInvokeMESService invokeMESService, IESSApiService eSSApiService, IDailySequenceService dailySequenceService, ILocationInfoService locationInfoService, ILogger<InboundOrderController> logger, IMaterialUnitService materialUnitService, IInboundService inboundService, IOutStockLockInfoService outStockLockInfoService, IOutboundOrderDetailService outboundOrderDetailService) : base(service)
        public InboundOrderController(IInboundOrderService service, WIDESEA_IBasicService.IErpApiService erpApiService, WIDESEA_IBasicService.IInvokeMESService invokeMESService, IESSApiService eSSApiService, IDailySequenceService dailySequenceService, ILocationInfoService locationInfoService, ILogger<InboundOrderController> logger, IMaterialUnitService materialUnitService, IInboundService inboundService, IOutStockLockInfoService outStockLockInfoService, IOutboundOrderDetailService outboundOrderDetailService, IRepository<Dt_Task> taskRepository, ITask_HtyService task_HtyService) : base(service)
        {
            this.erpApiService = erpApiService;
            _invokeMESService = invokeMESService;
@@ -55,22 +60,32 @@
            _inboundService = inboundService;
            _outStockLockInfoService = outStockLockInfoService;
            _outboundOrderDetailService = outboundOrderDetailService;
            _taskRepository = taskRepository;
            _task_HtyService = task_HtyService;
        }
        [HttpPost, Route("Test"), AllowAnonymous, MethodParamsValidate]
        public async Task<WebResponseContent> Test()
        {
            var originalTask = _taskRepository.Db.Queryable<Dt_Task>().First();
            var result = _task_HtyService.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.人工删除);
            if (result)
            {
                var sddd = "trueee";
            }
            //var originalTask = _taskRepository.Db.Queryable<Dt_Task>().First();
            //_taskRepository.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.自动完成);
            // Service.Db.Deleteable<Dt_InboundOrder>().Where(x=>x.UpperOrderNo== "12020251100040").ExecuteCommand();
            //_inboundService.InboundOrderDetailService.Db.Deleteable<Dt_InboundOrderDetail>()
            // .Where(p => SqlFunc.Subqueryable<Dt_InboundOrder>().Where(s => s.Id == p.OrderId && s.UpperOrderNo == "12020251100040").Any()).ExecuteCommand();
            var purchaseToStockResult = await _materialUnitService.ConvertPurchaseToStockAsync("100513-00303", 1);
            //var purchaseToStockResult = await _materialUnitService.ConvertPurchaseToStockAsync("100513-00303", 1);
            var pdddurchaseToStockResult = await _materialUnitService.ConvertFromToStockAsync("100513-00303", "W013", 1);
            //var pdddurchaseToStockResult = await _materialUnitService.ConvertFromToStockAsync("100513-00303", "W013", 1);
            //var sddd = _locationInfoService.AssignLocation();
            //var sddd = _locationInfoService.AssignLocation(1);
            //var code = sddd.LocationCode;
            //var ssss=await _dailySequenceService.GetNextSequenceAsync();
            //var  ddddssss = "WSLOT" + DateTime.Now.ToString("yyyyMMddHHmmss") + ssss.ToString().PadLeft(5, '0');
@@ -104,7 +119,7 @@
            //await  erpApiService.GetMaterialInfoAsync(new WIDESEA_DTO.Basic.MaterialRequest());
            return WebResponseContent.Instance.OK();
            return WebResponseContent.Instance.OK(result?1:0 );
        }
        /// <summary>
@@ -115,12 +130,12 @@
        [HttpPost, Route("ReceiveInboundOrder"), AllowAnonymous, MethodParamsValidate]
        public async Task<WebResponseContent> ReceiveInboundOrder([FromBody] InboundRequestModel model)
        {
            if(model.inBounds == null || !model.inBounds.Any())
            if (model.inBounds == null || !model.inBounds.Any())
            {
                return WebResponseContent.Instance.Error("入库单不能为空");
            }
            _logger.LogInformation("InboundOrderController ReceiveInboundOrder:  " +  JsonConvert.SerializeObject(model));
            _logger.LogInformation("InboundOrderController ReceiveInboundOrder:  " + JsonConvert.SerializeObject(model));
            List<Dt_InboundOrder> inboundOrders = new List<Dt_InboundOrder>();
            foreach (var item in model.inBounds)
@@ -178,7 +193,7 @@
                inboundOrders.Add(dt_InboundOrder);
            }
            var content =await Service.ReceiveInboundOrder(inboundOrders, model.operationType);
            var content = await Service.ReceiveInboundOrder(inboundOrders, model.operationType);
            if (content.Status) return WebResponseContent.Instance.OK(200);
            else return WebResponseContent.Instance.Error(content.Message);
@@ -224,9 +239,9 @@
        }
        [HttpPost, Route("BatchOrderFeedbackToMes"), AllowAnonymous]
        public async Task<WebResponseContent> BatchOrderFeedbackToMes([FromBody]  BatchOrderFeedbackToMesDto request)
        public async Task<WebResponseContent> BatchOrderFeedbackToMes([FromBody] BatchOrderFeedbackToMesDto request)
        {
           return await _invokeMESService.BatchOrderFeedbackToMes(request.orderNos, request.inout);
            return await _invokeMESService.BatchOrderFeedbackToMes(request.orderNos, request.inout);
        }
    }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Filter/CustomProfile.cs
@@ -7,6 +7,7 @@
using System.Threading.Tasks;
using WIDESEA_Common.OrderEnum;
using WIDESEA_Core.Helper;
using WIDESEA_Core.HttpContextUser;
using WIDESEA_DTO;
using WIDESEA_DTO.Inbound;
using WIDESEA_DTO.Outbound;
@@ -33,6 +34,10 @@
            CreateMap<Dt_ReceiveOrderDetail, Dt_CheckOrder>().ForMember(a => a.ReceivedQuantity, b => b.MapFrom(x => x.ReceivedQuantity)).ForMember(a => a.MaterielCode, b => b.MapFrom(x => x.MaterielCode)).ForMember(a => a.CheckOrderStatus, b => b.MapFrom(x => CheckOrderStatusEnum.NotCheck.ObjToInt()));
            CreateMap<Dt_Task, WMSTaskDTO>();
            CreateMap<Dt_Task, Dt_Task_Hty>()
         .ForMember(dest => dest.SourceId, opt => opt.Ignore()) // æ‰‹åŠ¨å¡«å……ï¼Œå¿½ç•¥æ˜ å°„
         .ForMember(dest => dest.OperateType, opt => opt.Ignore()); // æ‰‹åŠ¨å¡«å……ï¼Œå¿½ç•¥æ˜ å°„
        }
    }
}