Bladeren bron

得润宝

cqp 12 uur geleden
bovenliggende
commit
1d2abc2c83

+ 287 - 99
app/Console/Commands/U8Settle.php

@@ -2,11 +2,13 @@
 
 namespace App\Console\Commands;
 
-use App\Model\DDEmployee;
-use App\Model\Record;
-use App\Service\U8DatabaseServerService;
+use App\Jobs\ProcessWMSDataJob;
+use App\Model\SyncTempRecord;
+use App\Service\U8ThirtyPartyDatabaseServerService;
 use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Redis;
 
 class U8Settle extends Command
 {
@@ -34,112 +36,298 @@ class U8Settle extends Command
         parent::__construct();
     }
 
-    /**
-     * Execute the console command.
-     *
-     * @return mixed
-     */
     public function handle()
     {
-        echo '任务--------start---------------';
-        $this->syncApprovedRecords();
-        echo '任务结束--------end---------------';
+        try {
+            $this->settle();
+        }catch (\Exception $exception){
+            Log::channel('apiLog')->info('异常', ['msg' => $exception->getMessage() . '|' . $exception->getLine()]);
+        }
     }
 
-    public function syncApprovedRecords()
-    {
-        try {
-            $databases = [
-                "UFDATA_200_2021",
-                "UFDATA_002_2021",
-                "UFDATA_999_2021",
-            ];
-
-            //初始化连接池
-            $connections = [];
-            $error = true;
-            foreach ($databases as $db) {
-                if(! $error) continue;
-                $service = new U8DatabaseServerService(['zt_database' => $db]);
-                if ($service->error) {
-                    Log::channel('apiLog')->info('SQLServer连接失败', [
-                        "database" => $db,
-                        "error"    => $service->error,
-                    ]);
-                    $error = false;
-                    $connections[$db] = null; // 标记连接失败
-                } else {
-                    $connections[$db] = $service;
-                }
-            }
-            if(! $error ) return;
-
-            $time = date("Y-m-d H:i:s");
-            $time1 = date("Y-m-d 00:00:00");
-            // 分批同步数据
-            Record::where("del_time", 2)
-                ->select("id","type","database","order_number","userid")
-                ->orderBy("id","desc")
-                ->chunkById(10, function ($data) use($connections,$time,$time1){
-                    $data = $data->toArray();
-
-                    $name = DDEmployee::whereIn('userid', array_column($data,'userid'))
-                        ->pluck('name','userid')
-                        ->toArray();
-
-                    $id = [];
-                    foreach ($data as $record) {
-                        $database = $record['database'];
-                        $service  = $connections[$database] ?? null;
-                        if (! $service) continue;
-                        $type = $record['type'];
-                        $order_number = $record['order_number'];
-
-                        $system_name = "system";
-                        if(! empty($name[$record['userid']])) $system_name = $name[$record['userid']];
-                        if($type == 1){
-                            $service->db->table("PO_Pomain")
-                                ->where("cPOID", $order_number)
-                                ->update([
-                                    "cVerifier" => $system_name,
-                                    "iverifystateex"   => 1,
-                                    "cState" => 1,
-                                    "cAuditTime" => $time . ".000",
-                                    "cAuditDate" => $time1 . ".000",
-                                ]);
-                        }elseif($type == 2){
-                            $service->db->table("PU_AppVouch")
-                                ->where("cCode", $order_number)
-                                ->update([
-                                    "cVerifier" => $system_name,
-                                    "cAuditTime" => $time . ".000",
-                                    "cAuditDate" => $time1 . ".000",
-                                ]);
-                        }else{
-                            $service->db->table("AP_ApplyPayVouch")
-                                ->where("cVouchID", $order_number)
-                                ->update([
-                                    "cCheckMan" => $system_name,
-                                    "dverifysystime" => $time . ".000",
-                                    "dverifydate" => $time1 . ".000",
-                                ]);
-                        }
+    public function settle(){
+        $service = new U8ThirtyPartyDatabaseServerService();
+        $minPeriods = $this->getYjData();
+        if(empty($minPeriods['pu_date']) || empty($minPeriods['st_date']) || empty($minPeriods['sa_date'])) {
+            Log::channel('apiLog')->info('月结数据为空', ['msg' => $minPeriods]);
+            return;
+        }
+
+        //单据-----------------
+        $this->orderInsert($service, $minPeriods);
+    }
+
+    public function orderInsert($service, $minPeriods){
+        $pu = $minPeriods['pu_date'] . ' 00:00:00.000';
+        $st = $minPeriods['st_date'] . ' 00:00:00.000';
+        $sa = $minPeriods['sa_date'] . ' 00:00:00.000';
+
+        $dateMap = [
+            SyncTempRecord::type_one   => $pu,
+            SyncTempRecord::type_two   => $st,
+            SyncTempRecord::type_three => $st,
+            SyncTempRecord::type_four  => $sa,
+            SyncTempRecord::type_five  => $st,
+            SyncTempRecord::type_six  => $sa,
+            SyncTempRecord::type_seven  => $st,
+            SyncTempRecord::type_eight  => $st,
+        ];
+
+        // 配置不同单据的表名和字段
+        $tasks = [
+            SyncTempRecord::type_one => [// 采购到货 0 |退货 1
+                'main' => 'PU_ArrivalVouch',
+                'detail' => 'PU_ArrivalVouchs',
+                'main_key' => 'ID',
+                'key' => 'ID',
+                'whereRaw' => "(dDate >= '{$pu}')",
+                'main_field' => ['ID as id','cCode as no','dDate as order_date', 'cMakeTime as crt_time', 'cModifyTime as upd_time', 'iBillType as type', 'cverifier as reviewer'],
+                'son_field' => [
+                    'detail.ID as id',
+//                    'detail.ivouchrowno as lineNum',
+//                    'detail.cWhCode as warehouseCode',
+                    'detail.cInvCode as materialCode',
+                    'detail.iQuantity as planQty',
+//                    'detail.cordercode as from_order',
+                ],
+                'limit' => 30,
+            ],
+            SyncTempRecord::type_two => [// 领料申请单 材料出库----
+                'main' => 'MaterialAppVouch',
+                'detail' => 'MaterialAppVouchs',
+                'main_key' => 'ID',
+                'key' => 'ID',
+                'whereRaw' => "(dDate >= '{$st}')",
+                'main_field' => ['ID as id','cCode as no', 'dDate as order_date', 'dnmaketime as crt_time', 'dnmodifytime as upd_time','iverifystate as state', 'cHandler as reviewer'],
+                'son_field' => [
+                    'detail.ID as id',
+//                    'detail.ivouchrowno as lineNum',
+//                    'detail.cWhCode as warehouseCode',
+                    'detail.cInvCode as materialCode',
+                    'detail.iQuantity as planQty',
+                ],
+                'limit' => 30,
+            ],
+            SyncTempRecord::type_three => [// 产品报检单 产成品入库流程----- 没有仓库
+                'main' => 'QMINSPECTVOUCHER',
+                'detail' => 'QMINSPECTVOUCHERS',
+                'main_key' => 'ID',
+                'key' => 'ID',
+                'whereRaw' => "(DDATE >= '{$st}' and CVOUCHTYPE = 'QM02')",
+                'main_field' => ['ID as id','CINSPECTCODE as no','DDATE as order_date','DMAKETIME as crt_time', 'DMODIFYTIME as upd_time','CVERIFIER as reviewer'],
+                'son_field' => [
+                    'detail.ID as id',
+                    'detail.CINVCODE as materialCode',
+                    'detail.FQUANTITY as planQty',
+                    'detail.CDEFINE32 as lottar1',
+                ],
+                'limit' => 30,
+            ],
+            SyncTempRecord::type_four => [// 销售订单 销售出库流程 ------- 没有仓库
+                'main' => 'SO_SOMain',
+                'detail' => 'SO_SODetails',
+                'main_key' => 'ID',
+                'key' => 'ID',
+                'whereRaw' => "(dDate >= '{$sa}')",
+                'main_field' => ['ID as id','cSOCode as no','dDate as order_date','dcreatesystime as crt_time', 'dmodifysystime as upd_time','cVerifier as reviewer'],
+                'son_field' => [
+                    'detail.ID as id',
+                    'detail.cInvCode as materialCode',
+                    'detail.iQuantity as planQty',
+                ],
+                'limit' => 30,
+            ],
+            SyncTempRecord::type_five => [// 其他入  其他入库流程
+                'main' => 'RdRecord08',
+                'detail' => 'RdRecords08',
+                'main_key' => 'ID',
+                'key' => 'ID',
+                'whereRaw' => "(dDate >= '{$st}')",
+                'main_field' => ['ID as id','cCode as no','dDate as order_date','dnmaketime as crt_time', 'dnmodifytime as upd_time','cWhCode as warehouseCode','cHandler as reviewer'],
+                'son_field' => [
+                    'detail.ID as id',
+                    'detail.cInvCode as materialCode',
+                    'detail.iQuantity as planQty',
+                ],
+                'limit' => 30,
+            ],
+            SyncTempRecord::type_six => [// 销售退货单  其他入库流程
+                'main' => 'DispatchList',
+                'detail' => 'DispatchLists',
+                'main_key' => 'DLID',
+                'key' => 'DLID',
+                'whereRaw' => "(dDate >= '{$st}' and bReturnFlag = 1)",
+                'main_field' => ['DLID as id','cDLCode as no', 'dDate as order_date','dcreatesystime as crt_time', 'dmodifysystime as upd_time','cVerifier as reviewer'],
+                'son_field' => [
+                    'detail.DLID as id',
+                    'detail.cInvCode as materialCode',
+                    'detail.cWhCode as warehouseCode',
+                    'detail.iQuantity as planQty',
+                ],
+                'limit' => 30,
+            ],
+            SyncTempRecord::type_seven => [// 其他出  其他入库流程
+                'main' => 'RdRecord09',
+                'detail' => 'RdRecords09',
+                'main_key' => 'ID',
+                'key' => 'ID',
+                'whereRaw' => "(dDate >= '{$st}')",
+                'main_field' => ['ID as id','cCode as no','dDate as order_date','dnmaketime as crt_time', 'dnmodifytime as upd_time','cWhCode as warehouseCode','cHandler as reviewer'],
+                'son_field' => [
+                    'detail.ID as id',
+                    'detail.cInvCode as materialCode',
+                    'detail.iQuantity as planQty',
+                ],
+                'limit' => 30,
+            ],
+            SyncTempRecord::type_eight => [
+                //检验单
+                //cvouchtype=null=>采购到货单 1
+                //cvouchtype=QM04=>产品检验单 2
+                //cvouchtype=QM14=>退货检验单 3
+                'main' => 'QMCHECKVOUCHER',
+                'detail' => 'QMCHECKVOUCHERS',
+                'main_key' => 'ID',
+                'key' => '',
+                'whereRaw' => "(DDATE >= '{$st}' and (CVOUCHTYPE = null OR CVOUCHTYPE = 'QM04' OR CVOUCHTYPE = 'QM14'))",
+                'main_field' => ['ID as id','CCHECKCODE as no','DDATE as order_date','DMAKETIME as crt_time', 'DMODIFYTIME as upd_time','CVERIFIER as reviewer', 'CVOUCHTYPE as type', 'CINVCODE as materialCode', 'CBATCH as lot', 'FREGQUANTITY as hg_quantity', 'FDISQUANTITY as hg_not_quantity', 'FCONQUANTIY as rb_quantity'],
+                'son_field' => [],
+                'limit' => 30,
+            ],
+        ];
+
+        $time = time();
+
+        foreach ($tasks as $name => $config) {
+            // 用于记录本次在 U8 查到的所有单号,用来比对删除
+            $currentU8Nos = [];
+            $lastId = 0;
+            $orderDateLimit = $dateMap[$name]; // 当前类型的起始日期限制
+
+            while (true) {
+                list($status, $items) = $service->getPendingBills($config, $lastId);
+                if (!$status || empty($items)) break;
+
+                $nos = collect($items)->pluck('no')->toArray();
+                $currentU8Nos = array_merge($currentU8Nos, $nos); // 记录当前存在的单号
+
+                //获取这 30 条的快照
+                $snapshots = DB::table('sync_snapshot')
+                    ->where('type', $name)
+                    ->whereIn('u8_no', $nos)
+                    ->get()
+                    ->keyBy('u8_no');
 
-                        $id[] = $record['id'];
+                foreach ($items as $item) {
+                    if ($name == SyncTempRecord::type_eight){
+                        $type_v = 0;
+                        if($item['type'] == null){
+                            $type_v = 1;
+                        }elseif ($item['type'] == 'QM04'){
+                            $type_v = 2;
+                        }elseif ($item['type'] == 'QM14'){
+                            $type_v = 3;
+                        }
+                        $item['type'] = $type_v;
                     }
 
-                    // 更新本地数据
-                    if(! empty($id)) Record::whereIn("id", $id)->update(['del_time' => 3]);
-                });
+                    $no = $item['no'];
+                    $u8Id = $item['id']; // 抓取主表 ID
+                    $u8Upd = $item['upd_time'] ?: $item['crt_time'];
+                    $snapshot = $snapshots->get($no);
+                    $type_2 = isset($item['type']) ? $item['type'] : 0;
+                    $bool = $item['reviewer'] ? true : false;
+                    if(! $bool) continue;// 没审核跳过
+
+                    //退货单没有来源单据的不推送
+//                    if($name == SyncTempRecord::type_one && $item['type_2'] == 1 && empty($item['details'][0]['from_order'])) continue;
 
-            // ✅ 处理完所有 chunk 后统一关闭连接
-            foreach ($connections as $conn) {
-                if ($conn instanceof U8DatabaseServerService) {
-                    $conn->close();
+                    $opType = null;
+                    if (!$snapshot) {
+                        $opType = SyncTempRecord::opt_zero;
+                    } elseif ($u8Upd > $snapshot->last_upd_time) {
+                        $opType = SyncTempRecord::opt_one;
+                    }
+
+                    if ($opType !== null) {
+                        // 写入任务流水
+                        $this->createSyncTask($name, $no, $u8Id, $item, $opType, $u8Upd, $time, $type_2);
+                    }
                 }
+
+                //分页最大id
+                $lastId = collect($items)->max('id');
             }
-        } catch (\Throwable $e) {
-            Log::channel('apiLog')->info('U8数据更新异常', ['msg' => $e->getMessage()]);
+
+            $currentU8NosLookup = array_flip($currentU8Nos);
+
+            // 使用 chunk 配合日期过滤,只查快照中大于等于月结日期的单据
+            DB::table('sync_snapshot')
+                ->where('type', $name)
+                ->where('order_date', '>=', $orderDateLimit) // 关键:缩小快照查询范围
+                ->orderBy('u8_id')
+                ->chunk(100, function ($snapshots) use ($currentU8NosLookup, $name, $time) {
+                    foreach ($snapshots as $oldSnapshot) {
+                        // 如果快照里的单号不在本次 U8 扫描结果里
+                        if (!isset($currentU8NosLookup[$oldSnapshot->u8_no])) {
+                            $this->createSyncTask($name, $oldSnapshot->u8_no, $oldSnapshot->u8_id, $oldSnapshot->payload, SyncTempRecord::opt_two, "", $time);
+                        }
+                    }
+                });
+
+            unset($currentU8Nos); // 释放内存
         }
     }
+
+    private function createSyncTask($type, $no, $u8Id, $payload, $opType, $u8Upd = "", $time, $type_2 = 0) {
+        $record = SyncTempRecord::create([
+            'type'    => $type,
+            'type_2'  => $type_2,
+            'u8_no'   => $no,
+            'u8_id'   => $u8Id,
+            'payload' => json_encode($payload),
+            'u8_upd'  => $u8Upd,
+            'op_type' => $opType,
+            'crt_time'=> $time,
+        ]);
+
+        // 只分发 ID 给队列,Job 内部再根据此 ID 取 u8_id 和 payload
+        ProcessWMSDataJob::dispatch(['id' => $record->id])->onQueue("sync_wms_order");
+    }
+
+    private function getYjData(){
+        //结账
+        //采购到货单 | 退货单  bflag_PU  采购
+        //领料申请单|产成品入库单| bflag_ST 出和入
+        //销售发货单 销售退货 bflag_SA 销售
+        $result = DB::connection('u8_third_sqlserver')
+            ->table('gl_mend')
+            ->lock('WITH(NOLOCK)')
+            ->selectRaw("
+                MIN(CASE WHEN bflag_PU = 0 AND iyear >= 2023 THEN iYPeriod END) as min_pu,
+                MIN(CASE WHEN bflag_ST = 0 THEN iYPeriod END) as min_st,
+                MIN(CASE WHEN bflag_SA = 0 THEN iYPeriod END) as min_sa
+            ")
+            ->whereRaw("RIGHT(CAST(iYPeriod AS VARCHAR), 2) <> '00'")
+            ->first();
+        // 转换成数组方便取值
+        $minPeriods = (array)$result;
+        $formatDate = function($period) {
+            if (!$period) return null; // 如果没有未结账月份,返回null
+
+            $period = (string)$period; // 确保是字符串
+            $year = substr($period, 0, 4);
+            $month = substr($period, 4, 2);
+
+            return "{$year}-{$month}-01";
+        };
+        // 2. 执行转换
+        $formattedPeriods = [
+            'pu_date' => $formatDate($minPeriods['min_pu'] ?? null),
+            'st_date' => $formatDate($minPeriods['min_st'] ?? null),
+            'sa_date' => $formatDate($minPeriods['min_sa'] ?? null),
+        ];
+
+        return $formattedPeriods;
+    }
 }

+ 253 - 0
app/Console/Commands/U8SettleInventory.php

@@ -0,0 +1,253 @@
+<?php
+
+namespace App\Console\Commands;
+use App\Model\SyncTempRecordProduct;
+use App\Service\U8ThirtyPartyDatabaseServerService;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Cache;
+
+class U8SettleInventory extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'command:u8_settle_inventory';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Command description';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    public function handle()
+    {
+        try {
+            $time = time();
+            //生成需要增删改的存货数据
+            $this->settle($time);
+            echo '第一步完成------' . PHP_EOL;
+            //处理存货数据
+            $this->productInsert($time);
+            echo '第二步完成------' . PHP_EOL;
+        }catch (\Exception $exception){
+            Log::channel('apiLog')->info('存货异常', ['msg' => $exception->getMessage()]);
+        }
+    }
+
+    public function settle($time){
+        $service = new U8ThirtyPartyDatabaseServerService();
+        $tasks = [
+            'product' => [
+                'whereRaw' => "",
+                'limit' => 100,
+            ]
+        ];
+
+        foreach ($tasks as $name => $config) {
+            $currentU8Nos = [];
+            $lastCode = ""; // 分页依据:存货编码
+
+            while (true) {
+                list($status, $items) = $service->getInventoryData($config, $lastCode);
+                if (!$status || empty($items)) break;
+
+                $nos = collect($items)->pluck('product_code')->toArray();
+                $currentU8Nos = array_merge($currentU8Nos, $nos);
+
+                // 获取本地快照(注意:快照表的 ufts 字段需要是字符串类型)
+                $snapshots = DB::table('sync_snapshot_product')
+                    ->whereIn('code', $nos)
+                    ->get()
+                    ->keyBy('code');
+
+                foreach ($items as $item) {
+                    $no = $item['product_code'];
+                    $u8Ufts = $item['ufts_str']; // 拿到的是 '0x000000000005AD12' 这种
+                    $snapshot = $snapshots->get($no);
+
+                    $opType = null;
+                    if (!$snapshot) {
+                        $opType = SyncTempRecordProduct::opt_zero;
+                    } elseif (strtolower($u8Ufts) !== strtolower($snapshot->ufts)) {
+                        $opType = SyncTempRecordProduct::opt_one;
+                    }
+//                    if (!$snapshot) {
+//                        // 本地无记录 -> 新增
+//                        $opType = SyncTempRecordProduct::opt_zero;
+//                    } elseif ($u8Ufts !== $snapshot->ufts) {
+//                        // 本地 ufts 字符串与 U8 不一致 -> 修改
+//                        $opType = SyncTempRecordProduct::opt_one;
+//                    }
+
+                    if ($opType !== null) {
+                        $this->createSyncTask($no, $item, $opType, $u8Ufts, $time);
+                    }
+                }
+
+                // 分页标识:取本批次最后一个编码
+                $lastCode = end($items)['product_code'];
+            }
+
+            // --- 第二步:处理删除 ---
+            DB::table('sync_snapshot_product')->orderBy('code')->chunk(1000, function ($snapshots) use ($currentU8Nos, $time) {
+                $localNos = $snapshots->pluck('code')->toArray();
+                $deletedNos = array_diff($localNos, $currentU8Nos);
+
+                if (! empty($deletedNos)) {
+                    foreach ($deletedNos as $no) {
+                        $this->createSyncTask($no, ['product_code' => $no], SyncTempRecordProduct::opt_two, "", $time);
+                    }
+                }
+            });
+        }
+    }
+
+    public function productInsert($time)
+    {
+        DB::table('sync_temp_records_product')
+            ->where('crt_time', $time)
+            ->where('status', SyncTempRecordProduct::status_zero) // 只处理待处理的数据
+            ->orderBy('id')
+            ->chunkById(100, function ($records) {
+                $batchData = [];
+                $record_ids = [];
+                $processedItems = []; // 用于批量维护快照
+
+                foreach ($records as $record) {
+                    $u8Data = is_array($record->payload)
+                        ? $record->payload
+                        : json_decode($record->payload, true);
+
+                    if (empty($u8Data)) continue;
+
+                    // 组织接口数据
+                    $batchData[] = [
+                        'operationType'    => SyncTempRecordProduct::$opMapping[$record->op_type],
+                        'materialCode'     => $u8Data['product_code'] ?? '',
+                        'materialName'     => $u8Data['product_name'] ?? '',
+                        'materialSpec'     => $u8Data['product_size'] ?? '',
+                        'materialTypeCode' => $u8Data['product_category_code'] ?? '',
+                        'materialTypeName' => $u8Data['product_category_name'] ?? '',
+                        'unitCode'         => $u8Data['product_unit_title'] ?? '',
+                        'netWeight'        => 0,
+                        'grossWeight'      => $u8Data['grossWeight'] ?? 0,
+                    ];
+
+                    $record_ids[] = $record->id;
+
+                    // 缓存这一行的数据,用于后续成功后批量写回快照
+                    $processedItems[] = [
+                        'code'    => $record->code,
+                        'ufts'    => $record->ufts,
+                        'op_type' => $record->op_type,
+                        'payload' => $record->payload
+                    ];
+                }
+
+                if (empty($batchData)) return;
+
+                // 5. 发送数据
+                list($status, $msg) = $this->sendToTargetSystem($batchData);
+
+                if (!$status) {
+                    // 批量失败记录错误
+                    SyncTempRecordProduct::whereIn('id', $record_ids)
+                        ->update(['status' => SyncTempRecordProduct::status_two, 'error_msg' => $msg]);
+                } else {
+                    // 批量成功更新状态
+                    SyncTempRecordProduct::whereIn('id', $record_ids)
+                        ->update(['status' => SyncTempRecordProduct::status_one]);
+
+                    // --- 批量维护快照表 ---
+                    foreach ($processedItems as $item) {
+                        if ($item['op_type'] == SyncTempRecordProduct::opt_two) {
+                            // 删除操作:从快照移除
+                            DB::table('sync_snapshot_product')
+                                ->where('code', $item['code'])
+                                ->delete();
+                        } else {
+                            // 新增或修改:更新/插入快照
+                            DB::table('sync_snapshot_product')->updateOrInsert(
+                                ['code' => $item['code']],
+                                [
+                                    'ufts'          => $item['ufts'],
+                                    'payload'       => $item['payload'],
+                                    'code' => $item['code']
+                                ]
+                            );
+                        }
+                    }
+                }
+            });
+    }
+
+    public function sendToTargetSystem($jsonBody){
+        // 4. 接口路由
+        $path = '/erp/material';
+        $apiUrl = config('wms.api_url') . $path;
+
+        // 5. 调用
+        list($status, $result) = $this->post_helper($apiUrl, $jsonBody, ['Content-Type: application/json']);
+        return [$status, $result];
+    }
+
+    private function createSyncTask($no, $payload, $opType, $ufts = "", $time) {
+        SyncTempRecordProduct::create([
+            'code'   => $no,
+            'payload' => json_encode($payload),
+            'ufts'  => $ufts,
+            'op_type' => $opType,
+            'crt_time'=> $time,
+        ]);
+    }
+
+    public function post_helper($url, $data, $header = [], $timeout = 30)
+    {
+        Log::channel('apiLog')->info('存货同步', ["api" => $url, "param" => $data]);
+
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_POST, 1);
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
+        if(!is_null($data)) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
+
+        $r = curl_exec($ch);
+
+        if ($r === false) {
+            $errorMessage = curl_error($ch);
+            curl_close($ch);
+            Log::channel('apiLog')->error('存货同步异常:WMS_CURL_ERROR', ["msg" => $errorMessage]);
+            return [false, "网络错误: " . $errorMessage];
+        }
+        curl_close($ch);
+
+        $return = json_decode($r, true);
+        Log::channel('apiLog')->info('存货同步返回结果', ["res" => $return]);
+
+        // 判断逻辑:code 存在且为 200 才算 true
+        if (isset($return['code']) && $return['code'] == 200) {
+            return [true, $return];
+        }
+
+        $msg = $return['message'] ?? '未知接口错误';
+        return [false, $msg];
+    }
+}

+ 13 - 0
app/Http/Controllers/Api/TestController.php

@@ -2,6 +2,19 @@
 
 namespace App\Http\Controllers\Api;
 
+use App\Jobs\ProcessDataJob;
+use App\Model\Record;
+
 class TestController extends BaseController
 {
+    public function aa(){dd(1);
+        $res = Record::where('del_time',2)
+            ->where('result','<>','')
+            ->get()->toArray();
+        foreach ($res as $record_array){
+            ProcessDataJob::dispatch($record_array)->onQueue(Record::$job);
+        }
+
+        dd(1);
+    }
 }

+ 21 - 0
app/Http/Controllers/Api/U8ThirdPartyController.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace App\Http\Controllers\Api;
+
+use App\Service\U8ThirdPartyService;
+use Illuminate\Http\Request;
+
+class U8ThirdPartyController extends BaseController
+{
+    public function settleU8Data(Request $request)
+    {
+        $service = new U8ThirdPartyService();
+        list($status,$data) = $service->settleU8Data($request->all());
+
+        if($status){
+            return $this->json_return(200,'',$data);
+        }else{
+            return $this->json_return(201,$data);
+        }
+    }
+}

+ 147 - 0
app/Jobs/ProcessWMSArchiveDataJob.php

@@ -0,0 +1,147 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Model\SyncTempRecordProduct;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Symfony\Component\Console\Output\ConsoleOutput;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ProcessWMSArchiveDataJob implements ShouldQueue
+{
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+
+    protected $data;
+    public $timeout = 30;
+
+    public function __construct($data)
+    {
+        //record表
+        $this->data = $data;
+    }
+
+    public function handle()
+    {
+        try {
+            list($bool, $msg) = $this->settle();
+            if(! $bool) $this->finalDo($msg);
+        } catch (\Throwable $e) {
+            $this->finalDo("异常:" . $e->getMessage());
+            $this->delete();
+        }
+    }
+
+    private function finalDo($msg){
+        SyncTempRecordProduct::where('id', $this->data['id'])
+            ->update(['error_msg' => $msg, 'status' => SyncTempRecordProduct::status_two]);
+    }
+
+    private function settle()
+    {
+        $id = $this->data['id'];
+        $record = SyncTempRecordProduct::where('id', $id)->first();
+
+        // 1. 安全检查:如果记录不存在,或者已经是成功状态(status_one),则退出
+        if (!$record || $record->status != SyncTempRecordProduct::status_zero) {
+            return [true, '已处理或记录不存在'];
+        }
+
+        // 2. 解析 U8 原始数据
+        $u8Data = is_array($record->payload) ? $record->payload : json_decode($record->payload, true);
+
+        $jsonBody = [
+            'operationType' => SyncTempRecordProduct::$opMapping[$record->op_type],
+            'materialCode'  => $u8Data['product_code'],
+            'materialName'  => $u8Data['product_name'],
+            'materialSpec'  => $u8Data['product_size'] ?? '',
+            'materialTypeCode' => $u8Data['product_category_code'],
+            'materialTypeName' => $u8Data['product_category_name'],
+            'unitCode'       => $u8Data['product_unit_title'],
+            'netWeight'      => 0,
+            'grossWeight'   => $u8Data['grossWeight'] ?? 0,
+        ];
+
+        // 4. 接口路由
+        $path = '/erp/material';
+        $apiUrl = config('wms.api_url') . $path;
+
+        // 5. 调用
+        list($status, $result) = $this->post_helper($apiUrl, $jsonBody, ['Content-Type: application/json']);
+
+        if (! $status) {
+            // 接口返回失败或网络失败
+            $record->update(['status' => SyncTempRecordProduct::status_two, 'error_msg' => $result]);
+            return [false, $result];
+        }
+
+        // 6. 接口返回成功 (200) 的后续操作
+        DB::transaction(function () use ($record, $u8Data) {
+            // 更新本地流水状态为成功
+            $record->update([
+                'status' => SyncTempRecordProduct::status_one,
+            ]);
+
+            // 同步维护快照表,保证下次 Command 比对正确
+            if ($record->op_type == SyncTempRecordProduct::opt_two) {
+                DB::table('sync_snapshot_product')
+                    ->where('code', $u8Data['product_code'])
+                    ->where('ufts', $u8Data['ufts'])
+                    ->delete();
+            } else {
+                DB::table('sync_snapshot_product')->updateOrInsert(
+                    ['code' => $u8Data['product_code']],
+                    ['code' => $u8Data['product_code'], 'ufts' => $u8Data['ufts'], 'payload' => json_encode($u8Data)]
+                );
+            }
+        });
+
+        return [true, ''];
+    }
+
+    public function post_helper($url, $data, $header = [], $timeout = 30)
+    {
+        Log::channel('apiLog')->info('存货同步', ["api" => $url, "param" => $data]);
+
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_POST, 1);
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
+        if(!is_null($data)) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
+
+        $r = curl_exec($ch);
+
+        if ($r === false) {
+            $errorMessage = curl_error($ch);
+            curl_close($ch);
+            Log::channel('apiLog')->error('存货同步异常:WMS_CURL_ERROR', ["msg" => $errorMessage]);
+            return [false, "网络错误: " . $errorMessage];
+        }
+        curl_close($ch);
+
+        $return = json_decode($r, true);
+        Log::channel('apiLog')->info('存货同步返回结果', ["res" => $return]);
+
+        // 判断逻辑:code 存在且为 200 才算 true
+        if (isset($return['code']) && $return['code'] == 200) {
+            return [true, $return];
+        }
+
+        $msg = $return['message'] ?? '未知接口错误';
+        return [false, $msg];
+    }
+
+    protected function echoMessage(OutputInterface $output)
+    {
+        //输出消息
+        $output->writeln(json_encode($this->data));
+    }
+}

+ 254 - 0
app/Jobs/ProcessWMSDataJob.php

@@ -0,0 +1,254 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Model\SyncTempRecord;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Queue\InteractsWithQueue;
+use Illuminate\Queue\SerializesModels;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Symfony\Component\Console\Output\ConsoleOutput;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ProcessWMSDataJob implements ShouldQueue
+{
+    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
+
+    protected $data;
+    public $timeout = 30;
+
+    public function __construct($data)
+    {
+        //record表
+        $this->data = $data;
+    }
+
+    public function handle()
+    {
+        try {
+            list($bool, $msg) = $this->settle();
+            if(! $bool) $this->finalDo($msg);
+        } catch (\Throwable $e) {
+            $this->finalDo("异常:" . $e->getMessage());
+            $this->delete();
+        }
+    }
+
+    private function finalDo($msg){
+        SyncTempRecord::where('id', $this->data['id'])
+            ->update(['error_msg' => $msg, 'status' => SyncTempRecord::status_one]);
+    }
+
+    private function settle()
+    {
+        $id = $this->data['id'];
+        $record = SyncTempRecord::where('id', $id)->first();
+
+        // 1. 安全检查:如果记录不存在,或者已经是成功状态(status_one),则退出
+        if (!$record || $record->status != SyncTempRecord::status_zero) {
+            return [true, '已处理或记录不存在'];
+        }
+
+        // 2. 解析 U8 原始数据
+        $u8Data = is_array($record->payload) ? $record->payload : json_decode($record->payload, true);
+
+        $type = $record->type;
+        if($type == SyncTempRecord::type_eight){
+            //检验单
+            list($status, $msg) = $this->bjOrder($record, $u8Data);
+        }else{
+            //同步到音飞
+            list($status, $msg) = $this->orderInsert($record, $u8Data);
+        }
+
+        return [$status, $msg];
+    }
+
+    public function orderInsert($record, $u8Data){
+        $orderType = SyncTempRecord::$map[$record->type];
+        if(is_array($orderType)) $orderType = $orderType[$record->type_2];
+
+        // 3. 组装报文
+        $jsonBody = [
+            'orderType'     => $orderType,
+            'orderNo'       => $record->u8_no,
+            'orderId'       => $record->u8_id,
+            'operationType' => SyncTempRecord::$opMapping[$record->op_type],
+            'lottar1'       => '',
+            'lottar2'       => '',
+            'lottar3'       => '',
+            'lottar4'       => '',
+            'lottar5'       => '',
+            'lottar6'       => '',
+            'lottar7'       => '',
+            'lottar8'       => '',
+            'lottar9'       => '',
+            'details'       => []
+        ];
+
+        if (isset($u8Data['details']) && is_array($u8Data['details'])) {
+            foreach ($u8Data['details'] as $item) {
+                $jsonBody['details'][] = [
+                    'lineNum'       => $item['lineNum'] ?? 0,
+                    'materialCode'  => $item['materialCode'] ?? '',
+                    'planQty'       => (float)($item['planQty'] ?? 0),
+                    'lottar1'       => $item['lottar1'] ?? "",
+                    'lottar2'       => '',
+                    'lottar3'       => '',
+                    'lottar4'       => '',
+                    'lottar5'       => '',
+                    'lottar6'       => '',
+                    'lottar7'       => '',
+                    'lottar8'       => '',
+                    'lottar9'       => '',
+                ];
+            }
+        }
+
+        // 4. 接口路由
+        $inboundTypes = [SyncTempRecord::type_one, SyncTempRecord::type_three];
+        $path = in_array($record->type, $inboundTypes) ? '/erp/inbound' : '/erp/outbound';
+        $apiUrl = config('wms.api_url') . $path;
+
+        // 5. 调用 post_helper
+        list($status, $result) = $this->post_helper($apiUrl, $jsonBody, ['Content-Type: application/json']);
+
+        if (!$status) {
+            // 接口返回失败或网络失败
+            $record->update(['status' => SyncTempRecord::status_two, 'error_msg' => $result]);
+            return [false, $result];
+        }
+
+        // 6. 接口返回成功 (200) 的后续操作
+        DB::transaction(function () use ($record, $u8Data) {
+            // 更新本地流水状态为成功
+            $record->update([
+                'status' => SyncTempRecord::status_one,
+            ]);
+
+            // 同步维护快照表,保证下次 Command 比对正确
+            if ($record->op_type == SyncTempRecord::opt_two) {
+                DB::table('sync_snapshot')
+                    ->where('type', $record->type)
+                    ->where('u8_no', $record->u8_no)
+                    ->where('u8_id', $record->u8_id)
+                    ->delete();
+            } else {
+                DB::table('sync_snapshot')->updateOrInsert(
+                    ['type' => $record->type, 'u8_no' => $record->u8_no],
+                    ['u8_id' => $record->u8_id,'last_upd_time' => $record->u8_upd, 'order_date' => $u8Data['order_date'], 'payload' => json_encode($u8Data)]
+                );
+            }
+        });
+
+        return [true, ''];
+    }
+
+    //代码是有bug的 因为业务
+    public function bjOrder($record, $u8Data){
+        $hg_quantity = $u8Data['hg_quantity'] ?? 0;
+        $rb_quantity = $u8Data['rb_quantity'] ?? 0;
+        $hg_not_quantity = $u8Data['hg_not_quantity'] ?? 0;
+
+        if($hg_quantity > 0){
+            list($status, $msg) = $this->zj($record, $u8Data, 1);
+        }elseif ($rb_quantity > 0) {
+            list($status, $msg) = $this->zj($record, $u8Data,3);
+        }elseif($hg_not_quantity > 0){
+            list($status, $msg) = $this->zj($record, $u8Data,2);
+        }
+
+        return [$status ?? true, $msg ?? ""];
+    }
+
+    //todo
+    public function zj($record, $u8Data, $type){
+        // 3. 组装报文
+        $jsonBody = [
+            'orderNo'       => $record->u8_no,
+            'materialCode' => $u8Data['materialCode'],
+            'lot' => $u8Data['lot'] ?? "",
+            'inspectionStatus' => $type,
+        ];
+
+        // 4. 接口路由
+        $apiUrl = config('wms.api_url') . '/erp/inspection';
+
+        // 5. 调用 post_helper
+        list($status, $result) = $this->post_helper($apiUrl, $jsonBody, ['Content-Type: application/json']);
+
+        if (!$status) {
+            // 接口返回失败或网络失败
+            $record->update(['status' => SyncTempRecord::status_two, 'error_msg' => $result]);
+            return [false, $result];
+        }
+
+        // 6. 接口返回成功 (200) 的后续操作
+        DB::transaction(function () use ($record, $u8Data) {
+            // 更新本地流水状态为成功
+            $record->update([
+                'status' => SyncTempRecord::status_one,
+            ]);
+
+            // 同步维护快照表,保证下次 Command 比对正确
+            if ($record->op_type == SyncTempRecord::opt_two) {
+                DB::table('sync_snapshot')
+                    ->where('type', $record->type)
+                    ->where('u8_no', $record->u8_no)
+                    ->where('u8_id', $record->u8_id)
+                    ->delete();
+            } else {
+                DB::table('sync_snapshot')->updateOrInsert(
+                    ['type' => $record->type, 'u8_no' => $record->u8_no],
+                    ['u8_id' => $record->u8_id,'last_upd_time' => $record->u8_upd, 'order_date' => $u8Data['order_date'], 'payload' => json_encode($u8Data)]
+                );
+            }
+        });
+
+        return [true, ''];
+    }
+
+    public function post_helper($url, $data, $header = [], $timeout = 30)
+    {
+        Log::channel('apiLog')->info('WMS_POST_START', ["api" => $url, "param" => $data]);
+
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_POST, 1);
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
+        if(!is_null($data)) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
+
+        $r = curl_exec($ch);
+
+        if ($r === false) {
+            $errorMessage = curl_error($ch);
+            curl_close($ch);
+            Log::channel('apiLog')->error('WMS_CURL_ERROR', ["msg" => $errorMessage]);
+            return [false, "网络错误: " . $errorMessage];
+        }
+        curl_close($ch);
+
+        $return = json_decode($r, true);
+        Log::channel('apiLog')->info('WMS_POST_RETURN', ["res" => $return]);
+
+        // 判断逻辑:code 存在且为 200 才算 true
+        if (isset($return['code']) && $return['code'] == 200) {
+            return [true, $return];
+        }
+
+        $msg = $return['message'] ?? '未知接口错误';
+        return [false, $msg];
+    }
+
+    protected function echoMessage(OutputInterface $output)
+    {
+        //输出消息
+        $output->writeln(json_encode($this->data));
+    }
+}

+ 14 - 0
app/Model/SyncSnapShot.php

@@ -0,0 +1,14 @@
+<?php
+
+namespace App\Model;
+
+use Illuminate\Database\Eloquent\Model;
+
+class SyncSnapShot extends Model
+{
+    protected $guarded = [];
+    protected $table = "sync_snap_shot"; //指定表
+    const CREATED_AT = null;
+    const UPDATED_AT = null;
+    protected $dateFormat = 'U';
+}

+ 63 - 0
app/Model/SyncTempRecord.php

@@ -0,0 +1,63 @@
+<?php
+
+namespace App\Model;
+
+use Illuminate\Database\Eloquent\Model;
+
+class SyncTempRecord extends Model
+{
+    protected $guarded = [];
+    protected $table = "sync_temp_records"; //指定表
+    const CREATED_AT = 'crt_time';
+    const UPDATED_AT = 'upd_time';
+    protected $dateFormat = 'U';
+
+    const opt_zero = 0; // 新增
+    const opt_one = 1;  // 修改
+    const opt_two = 2;  // 删除
+
+    // 3. 构建操作类型映射
+    public static $opMapping = [
+        self::opt_zero => 'C',
+        self::opt_one  => 'U',
+        self::opt_two  => 'D',
+    ];
+
+    const status_zero = 0;
+    const status_one = 1;
+    const status_two = 2;
+
+    const type_one = 'purchaseArrival'; // 采购到货单 0 | 采购退货单 1
+    const type_two = 'materialRequest'; //领料申请单
+    const type_three = 'productBj'; // 产品报检单
+    const type_four = 'salesOrder'; // 销售订单
+    const type_five = 'other_in';// 其他入
+    const type_six = 'salesReturn';//销售退货单
+    const type_seven = 'other_out';//其他出
+    const type_eight = 'in_check';//在库检验
+
+    public static $settle_type = [
+        self::type_one => 'one',
+        self::type_two => 'two',
+        self::type_three => 'three',
+        self::type_four => 'four',
+        self::type_five => 'five',
+        self::type_six => 'six',
+        self::type_seven => 'seven',
+        self::type_eight => 'eight',
+    ];
+
+    public static $map = [
+        self::type_one => [
+            0 => 'PR_IN', // 采购到货
+            1 => 'PR_OUT',// 采购退货
+        ],
+        self::type_two => 'PK_OUT', // 领料申请单
+        self::type_three => 'WIP_IN',  // 产品报检单
+        self::type_four => 'FG_OUT', // 销售订单
+        self::type_five => 'MS_IN', // 其他入
+        self::type_six => 'IV_ADJ', // 销售退货
+        self::type_seven => 'MS_OUT', // 其他出
+        self::type_eight => 'U8_check', // 检验单
+    ];
+}

+ 29 - 0
app/Model/SyncTempRecordProduct.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Model;
+
+use Illuminate\Database\Eloquent\Model;
+
+class SyncTempRecordProduct extends Model
+{
+    protected $guarded = [];
+    protected $table = "sync_temp_records_product"; //指定表
+    const CREATED_AT = 'crt_time';
+    const UPDATED_AT = 'upd_time';
+    protected $dateFormat = 'U';
+
+    const opt_zero = 0; // 新增
+    const opt_one = 1;  // 修改
+    const opt_two = 2;  // 删除
+
+    // 3. 构建操作类型映射
+    public static $opMapping = [
+        self::opt_zero => 'C',
+        self::opt_one  => 'U',
+        self::opt_two  => 'D',
+    ];
+
+    const status_zero = 0;
+    const status_one = 1;
+    const status_two = 2;
+}

+ 37 - 0
app/Service/Service.php

@@ -565,6 +565,43 @@ class Service
         return true;
     }
 
+    /**
+     * 判断地址(IP或域名)是否网络通畅
+     * @param string $address 域名或IP
+     * @param int $port 端口(U8通常是1433,或者你的7151)
+     * @param int $timeout 超时秒数
+     * @return array [bool 是否通畅, string 结果描述]
+     */
+    function checkNetworkStatus($address, $port = 80, $timeout = 2) {
+        // 1. 去掉可能存在的空格或 http 协议头
+        $address = str_replace(['http://', 'https://'], '', trim($address));
+        $address = explode('/', $address)[0]; // 只取域名部分
+
+        $isIp = filter_var($address, FILTER_VALIDATE_IP);
+        $targetIp = $address;
+
+        // 2. 如果是域名,尝试解析
+        if (!$isIp) {
+            $targetIp = gethostbyname($address);
+            if ($targetIp === $address) {
+                return [false, "域名解析失败"];
+            }
+        }
+
+        // 3. 尝试建立 TCP 连接(这是判断通畅的核心)
+        $startTime = microtime(true);
+        $fp = @fsockopen($targetIp, $port, $errno, $errstr, $timeout);
+        $endTime = microtime(true);
+
+        if (!$fp) {
+            return [false, "无法连接到 {$address}:{$port}。错误: {$errstr} ({$errno})"];
+        }
+
+        fclose($fp);
+        $ms = round(($endTime - $startTime) * 1000, 2);
+        return [true, "连接成功!目标IP: {$targetIp},响应延迟: {$ms}ms"];
+    }
+
     public function delStorageFile($old, $new = [], $dir = "upload_files/"){
         foreach ($old as $value){
             if(! in_array($value, $new)){

+ 357 - 0
app/Service/U8ThirdPartyService.php

@@ -0,0 +1,357 @@
+<?php
+
+namespace App\Service;
+
+use App\Model\SyncSnapShot;
+use App\Model\SyncTempRecord;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Log;
+
+class U8ThirdPartyService extends Service
+{
+   const one = 1;
+   const two = 2;
+   const three = 3;
+   const four = 4;
+   const five = 5;
+   const six = 6;
+   const seven = 7;
+   const eight = 8;
+   const type_all = [
+       self::one => '采购入库',
+       self::two => '采购退货',
+       self::three => '材料出库',
+       self::four => '采购入库',
+       self::five => '采购入库',
+       self::six => '采购入库',
+       self::seven => '采购入库',
+       self::eight => '采购入库',
+   ];
+
+   public function settleU8Data($data){
+       if(empty($data['type'])) return [false, 'type类型不能为空'];
+       if(! isset(self::type_all[$data['type']])) return [false, 'type类型错误'];
+       $type = $data['type'];
+
+       list($status, $msg) = $this->getToken();
+//       if(! $status) return [false, $msg];
+       $data['u8_data'] = $msg;
+
+       if($type == self::one){
+           list($status, $msg) = $this->purchaseIn($data);
+       }elseif ($type == self::two){
+           list($status, $msg) = $this->purchaseReturn($data);
+       }elseif ($type == self::three){
+           list($status, $msg) = $this->materialOut($data);
+       }elseif ($type == self::four){
+           list($status, $msg) = $this->purchaseIn($data);
+       }elseif ($type == self::five){
+           list($status, $msg) = $this->purchaseIn($data);
+       }elseif ($type == self::six){
+           list($status, $msg) = $this->purchaseIn($data);
+       }elseif ($type == self::seven){
+           list($status, $msg) = $this->purchaseIn($data);
+       }elseif ($type == self::eight){
+           list($status, $msg) = $this->purchaseIn($data);
+       }
+   }
+
+   public function getToken(){
+        $key = "drb_u8_api";
+        $config = config('wms.drb');
+
+        // 1. 自检网络通畅
+        list($bool, $msg) = $this->checkNetworkStatus($config['api_host'], $config['api_port']);
+        if (!$bool) return [false, "网络连接失败: " . $msg];
+
+        $host = $config['api_host'] . ":" . $config['api_port'];
+
+        // 2. 尝试从缓存获取
+        $token = Cache::get($key);
+
+        if (! $token) {
+            // 3. 缓存失效,请求新 Token
+            $url = $host . "/api/System/GetToken";
+            $date = date("Y-m-d");
+            $json = [
+                "U8DbName"      => $config['database'],
+                "sUserId"       => $config['user_id'],
+                "sPassword"     => $config['user_password'],
+                "LoginDateTime" => $date,
+                "bPersist"      => true
+            ];
+
+            $header = ['Content-Type:application/json'];
+
+            // 调用你的 POST 辅助函数
+            list($status, $result) = $this->post_helper1($url, json_encode($json), $header);
+
+            if (!$status) return [false, "用友token获取失败: " . $result];
+            if (!isset($result['code'])) return [false, '获取用友登录信息失败: 响应格式异常'];
+            if ($result['code'] != 0) return [false, "U8错误: " . ($result['msg'] ?? '未知错误')];
+
+            $token = $result['data']['Token'] ?? "";
+
+            if (empty($token)) return [false, "接口返回的 Token 为空"];
+
+            // 30分钟 = 1800秒
+            Cache::put($key, $token, now()->addMinutes(30));
+        }
+
+        return [true, ['host' => $host, 'token' => $token]];
+    }
+
+   public function purchaseIn($data){
+       if(empty($data['orderId'])) return [false, '采购到货单ID不能为空'];
+       if(empty($data['orderNo'])) return [false, '采购到货单单号不能为空'];
+       if(empty($data['detail'])) return [false, '表体信息detail不能为空'];
+
+       $body = [];
+       foreach ($data['detail'] as $key => $value){
+           if(empty($value['lineNum'])) return [false, '行号不能为空'];
+           if(! is_numeric($value['lineNum'])) return [false, '行号错误'];
+           if(empty($value['materialCode'])) return [false, '存货编码不能为空'];
+           if(empty($value['productDate']) || ! $this->validateProductDate($value['productDate'])) return [false, '生产日期为空或格式错误'];
+           if(empty($value['failureDate']) || ! $this->validateProductDate($value['failureDate'])) return [false, '失效日期为空或格式错误'];
+           if(empty($value['lot'])) return [false, '批号不能为空'];
+           $body[] = [
+               'ivouchrowno' => $value['lineNum'],
+               'dPDate' => $value['productDate'],
+               'dVDate' => $value['failureDate'],
+               'cBatch' => $value['lot'],
+               'editprop' => 'M',
+           ];
+       }
+
+       //调用所需
+       $host = $data['u8_data']['host'];
+       $token = $data['u8_data']['token'];
+
+       //采购到货单弃审
+       $header = ["Authorization: {$token}",'Content-Type:application/json'];
+       $url = $host . "/api/PuArrVouch/UnVerify";
+       $json = [
+           "VouchId" => $data['orderId'],
+       ];
+       $json = json_encode($json);
+
+       list($status, $result) = $this->post_helper1($url,$json, $header, 30);
+       if(! $status) return [false, $result];
+
+       if(! isset($result['code'])) return [false, '采购到货单弃审失败'];
+       if($result['code'] != 0) return [false, $result['msg']];
+
+       //采购到货单编辑并审核
+       $url = $host . "/api/PuArrVouch/Update";
+       $json_final[] = [
+            "Inum" => "PuArrVouch",
+            "data" =>[
+                "iHead" => [
+                    "cCode" => $data['orderNo'],
+                    "IsVerify" => true,
+                    "debug" => true,
+                ],
+                "iBody" => $body,
+            ],
+       ];
+
+       $json = json_encode($json_final);
+       list($status, $result) = $this->post_helper1($url,$json, $header, 30);
+       if(! $status) return [false, $result];
+
+       if(! isset($result['code'])) return [false, '采购到货单编辑并审核失败'];
+       if($result['code'] != 0) return [false, $result['msg']];
+
+       return [true, ''];
+   }
+
+   public function materialOut($data){
+       if(empty($data['orderId'])) return [false, '领料申请单ID不能为空'];
+       if(empty($data['orderNo'])) return [false, '领料申请单单号不能为空'];
+       if(empty($data['detail'])) return [false, '表体信息detail不能为空'];
+
+       $body = [];
+       foreach ($data['detail'] as $key => $value){
+           if(empty($value['lineNum'])) return [false, '行号不能为空'];
+           if(! is_numeric($value['lineNum'])) return [false, '行号错误'];
+           if(empty($value['materialCode'])) return [false, '存货编码不能为空'];
+           if(empty($value['productDate']) || ! $this->validateProductDate($value['productDate'])) return [false, '生产日期为空或格式错误'];
+           if(empty($value['failureDate']) || ! $this->validateProductDate($value['failureDate'])) return [false, '失效日期为空或格式错误'];
+           if(empty($value['lot'])) return [false, '批号不能为空'];
+           $body[] = [
+               'ivouchrowno' => $value['lineNum'],
+               'dPDate' => $value['productDate'],
+               'dVDate' => $value['failureDate'],
+               'cBatch' => $value['lot'],
+               'editprop' => 'M',
+           ];
+       }
+
+       //调用所需
+       $host = $data['u8_data']['host'];
+       $token = $data['u8_data']['token'];
+
+       //采购到货单弃审
+       $header = ["Authorization: {$token}",'Content-Type:application/json'];
+       $url = $host . "/api/PuArrVouch/UnVerify";
+       $json = [
+           "VouchId" => $data['orderId'],
+       ];
+       $json = json_encode($json);
+
+       list($status, $result) = $this->post_helper1($url,$json, $header, 30);
+       if(! $status) return [false, $result];
+
+       if(! isset($result['code'])) return [false, '采购到货单弃审失败'];
+       if($result['code'] != 0) return [false, $result['msg']];
+
+       //采购到货单编辑并审核
+       $url = $host . "/api/PuArrVouch/Update";
+       $json_final[] = [
+            "Inum" => "PuArrVouch",
+            "data" =>[
+                "iHead" => [
+                    "cCode" => $data['orderNo'],
+                    "IsVerify" => true,
+                    "debug" => true,
+                ],
+                "iBody" => $body,
+            ],
+       ];
+
+       $json = json_encode($json_final);
+       list($status, $result) = $this->post_helper1($url,$json, $header, 30);
+       if(! $status) return [false, $result];
+
+       if(! isset($result['code'])) return [false, '采购到货单编辑并审核失败'];
+       if($result['code'] != 0) return [false, $result['msg']];
+
+       return [true, ''];
+   }
+
+   public function purchaseReturn($data){
+       if(empty($data['orderId'])) return [false, '采购退货单ID不能为空'];
+       if(empty($data['orderNo'])) return [false, '采购退货单单号不能为空'];
+
+       //获取单据
+       $service = new U8ThirtyPartyDatabaseServerService();
+       list($status, $order) = $service->getArrivalVouchById($data['orderId']);
+       if(! $status) return [false, $order];
+dd($order);
+
+        $final_data = [];
+        foreach ($order['details'] as $value){
+            if(empty($value['cWhCode'])) continue;
+
+            if(isset($final_data[$value['cWhCode']])){
+
+            }else{
+                $tmp = [
+                    "Inum" => "PurchaseIn",
+                    "data" =>[
+                        "iHead" => [
+                            "bIsRedVouch" => true, // 表体必须负数
+                            "bCalPrice" => true, // 是否由接口计算金额
+                            "cWhCode" => $value['cWhCode'],
+                            "cRdCode" => $order['rd_code'], // 入库类别
+                            "cDepCode" => $order['depart_code'],
+                            "cARVCode" => $order['no'],
+                            "cSource" => "采购到货单",
+                            "cBusType" => "普通采购",
+                            "cMemo" => "",
+                            "dDate" => date("Y-m-d"),
+                            "IsVerify" => true,
+                        ],
+                        "iBody" => $order['details'],
+                    ],
+                ];
+            }
+        }
+       //调用所需
+       $host = $data['u8_data']['host'] ?? "";
+       $token = $data['u8_data']['token'] ?? "";
+
+       //采购退货单生成
+       $header = ["Authorization: {$token}",'Content-Type:application/json'];
+
+       //生成红字采购入库单
+       $url = $host . "/api/PurchaseIn/Add";
+       $json_final[] = [
+            "Inum" => "PurchaseIn",
+            "data" =>[
+                "iHead" => [
+                    "bIsRedVouch" => true, // 表体必须负数
+                    "bCalPrice" => true, // 是否由接口计算金额
+                    "cWhCode" => $data['warehouseCode'],
+                    "cRdCode" => $order['rd_code'], // 入库类别
+                    "cDepCode" => $order['depart_code'],
+                    "cARVCode" => $order['no'],
+                    "cSource" => "采购到货单",
+                    "cBusType" => "普通采购",
+                    "cMemo" => "",
+                    "dDate" => date("Y-m-d"),
+                    "IsVerify" => true,
+                ],
+                "iBody" => $order['details'],
+            ],
+       ];
+
+       $json = json_encode($json_final);
+       list($status, $result) = $this->post_helper1($url,$json, $header, 30);
+       if(! $status) return [false, $result];
+
+       if(! isset($result['code'])) return [false, '红字采购入库单生成并审核失败'];
+       if($result['code'] != 0) return [false, $result['msg']];
+
+       return [true, ''];
+   }
+
+   public function post_helper1($url, $data, $header = [], $timeout = 20){
+        Log::channel('apiLog')->info('POST', ["api" => $url , "param" => json_decode($data,true) ,"header" => $header]);
+
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch,  CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_ENCODING, '');
+        curl_setopt($ch, CURLOPT_POST, 1);
+        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
+        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
+
+        if(!is_null($data)) curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+        $r = curl_exec($ch);
+
+        if ($r === false) {
+            // 获取错误号
+            $errorNumber = curl_errno($ch);
+            // 获取错误信息
+            $errorMessage = curl_error($ch);
+            $message = "cURL Error #{$errorNumber}: {$errorMessage}";
+
+            Log::channel('apiLog')->info('POST结果', ["message" => $message ]);
+            return [false, $message];
+        }
+        curl_close($ch);
+
+        $return = json_decode($r, true);
+        unset($r);
+        Log::channel('apiLog')->info('POST结果', ["message" => $return ]);
+
+        return [true, $return];
+    }
+
+   function validateProductDate($dateStr) {
+        // SQL Server 常用的 datetime 格式包含 .v (毫秒)
+        // 注意:这里的格式必须严格对应 " 2025-12-13 00:00:00.000"
+        // 如果字符串开头有空格,格式字符串里也要留空格
+        $format = ' Y-m-d H:i:s.v';
+
+        $d = \DateTime::createFromFormat($format, $dateStr);
+
+        // 检查是否转换成功,并且转换后的格式与原字符串完全一致
+        return $d && $d->format($format) === $dateStr;
+    }
+}

+ 296 - 0
app/Service/U8ThirtyPartyDatabaseServerService.php

@@ -0,0 +1,296 @@
+<?php
+namespace App\Service;
+
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+
+class U8ThirtyPartyDatabaseServerService extends Service
+{
+    protected $connectionName = 'u8_third_sqlserver';
+
+    // Service 内部查询
+    public function getPendingBills($config, $lastId)
+    {
+        try {
+
+            $mainTable = $config['main'];
+            $detailTable = $config['detail'];
+            $mainKey = $config['main_key'];
+            $foreignKey = $config['key'];
+            $whereRaw = $config['whereRaw'];
+            $main_field = $config['main_field'];
+            $son_field = $config['son_field'];
+            $limit = $config['limit'];
+
+            // 1. 获取主表
+            $mainRows = DB::connection($this->connectionName)
+                ->table($mainTable)
+                ->lock('WITH(NOLOCK)')
+                ->where($mainKey, '>', $lastId) // 改用 ID
+                ->whereRaw($whereRaw)
+                ->select($main_field)
+                ->orderBy($mainKey, 'ASC') // 按 ID 排序保证分页连续
+                ->limit($limit)
+                ->get();
+
+            if ($mainRows->isEmpty()) return [true, []];
+            if (empty($foreignKey) || empty($son_field)) {
+                $array = $mainRows->map(function ($item) {
+                    return (array) $item;
+                })->toArray();
+                return [true, $array];
+            }
+
+            // 2. 批量获取子表
+            $mainIds = $mainRows->pluck('id')->toArray();
+//            $details = DB::connection($this->connectionName)
+//                ->table($detailTable . ' as detail') // 给子表起别名
+//                ->leftJoin('Inventory as inv', 'detail.cInvCode', '=', 'inv.cInvCode') // 关联存货表
+//                ->lock('WITH(NOLOCK)')
+//                ->whereIn('detail.' . $foreignKey, $mainIds) // 注意这里要指定表别名
+//                ->select($son_field) // 这里会读取你配置里的新字段
+//                ->get()
+//                ->groupBy('id');
+            $details = DB::connection($this->connectionName)
+                ->table($detailTable. ' as detail')
+                ->lock('WITH(NOLOCK)')
+                ->whereIn($foreignKey, $mainIds)
+                ->select($son_field)
+                ->addSelect(DB::raw("ROW_NUMBER() OVER(PARTITION BY detail.{$foreignKey} ORDER BY detail.AutoID) as lineNum"))
+                ->get()
+                ->groupBy('id');
+
+            // 3. 组合
+            $result = $mainRows->map(function($main) use ($details, $foreignKey) {
+                $res = (array)$main;
+                $mainId = $res['id'];
+                $res['details'] = isset($details[$mainId])
+                    ? $details[$mainId]->map(function($item) {
+                        return (array)$item; // 强制把子表对象转成数组
+                    })->values()->toArray()
+                    : [];
+                return $res;
+            })->toArray();
+
+            return [true, $result];
+
+        } catch (\Throwable $e) {
+            Log::channel('apiLog')->info('查询失败', [
+                "error"    => $e->getMessage(),
+            ]);
+            return [false, $e->getMessage()];
+        }
+    }
+
+    public function getBillSingle($config, $id)
+    {
+        try {
+            $mainTable = $config['main'];
+            $detailTable = $config['detail'];
+            $mainKey = $config['main_key']; // 比如 ID
+            $foreignKey = $config['key'];   // 比如 id (子表关联主表的字段)
+            $main_field = $config['main_field'];
+            $son_field = $config['son_field'];
+
+            // 1. 获取主表单条记录
+            $mainRow = DB::connection($this->connectionName)
+                ->table($mainTable)
+                ->lock('WITH(NOLOCK)')
+                ->where($mainKey, $id) // 精确匹配单据 ID
+                ->select($main_field)
+                ->first();
+
+            if (!$mainRow) {
+                return [false, "单据 ID:{$id} 未找到数据"];
+            }
+
+            $result = (array)$mainRow;
+
+            // 2. 如果不需要子表,直接返回
+            if (empty($foreignKey) || empty($son_field)) {
+                return [true, $result];
+            }
+
+            // 3. 获取该单据对应的子表详情
+            // 注意:U8 子表通常用 id 关联主表的 ID,这里使用配置中的 $foreignKey
+            $details = DB::connection($this->connectionName)
+                ->table($detailTable . ' as detail')
+                ->lock('WITH(NOLOCK)')
+                ->where($foreignKey, $id)
+                ->select($son_field)
+                // 自动生成行号,按 AutoID 排序
+//                ->addSelect(DB::raw("ROW_NUMBER() OVER(ORDER BY detail.AutoID) as lineNum"))
+                ->orderBy('detail.AutoID', 'ASC')
+                ->get();
+
+            // 4. 组合数据
+            $result['details'] = $details->map(function ($item) {
+                return (array)$item;
+            })->toArray();
+
+            return [true, $result];
+
+        } catch (\Throwable $e) {
+            Log::channel('apiLog')->info('查询单个单据失败', [
+                "id"    => $id,
+                "error" => $e->getMessage(),
+            ]);
+            return [false, $e->getMessage()];
+        }
+    }
+
+    public function getArrivalVouchById($orderId)
+    {
+        try {
+            // 1. 获取主表 (PU_ArrivalVouch) 并关联 采购类型表 (PurchaseType)
+            $main = DB::connection($this->connectionName)
+                ->table('PU_ArrivalVouch as main')
+                ->leftJoin('PurchaseType as pt', 'main.cPTCode', '=', 'pt.cPTCode') // 关联采购类型表
+                ->lock('WITH(NOLOCK)')
+                ->where('main.ID', $orderId)
+                ->select([
+                    'main.ID as id',
+                    'main.cCode as no',
+                    'main.cVenCode as supply_code',
+                    'main.cDepCode as depart_code',
+                    'main.dDate as order_date',
+                    'main.cBusType as b_type',
+                    'main.cPTCode as cg_type',
+                    'pt.cRdCode as rd_code'
+                ])
+                ->first();
+
+            if (!$main) {
+                return [false, "到货单 ID:{$orderId} 不存在"];
+            }
+
+            $order = (array)$main;
+
+            // 2. 获取子表 (PU_ArrivalVouchs)
+            $details = DB::connection($this->connectionName)
+                ->table('PU_ArrivalVouchs as detail')
+                ->lock('WITH(NOLOCK)')
+                ->where('ID', $orderId) // 子表里的 ID 关联主表的 ID
+                ->select([
+                    'detail.cInvCode',
+                    'detail.iQuantity',
+                    'detail.iQuantity as iNNum',
+                    'detail.iinvexchrate',
+                    'detail.cBatch',
+                    'detail.dPDate',
+                    'detail.dVDate',
+                    'detail.Autoid as iArrsId',
+                    'detail.ivouchrowno as iRowNo',
+                    'detail.iOriTaxCost',
+                    'detail.iTaxRate',
+//                    'detail.cordercode',
+                    'detail.cWhCode',
+                ])
+                ->orderBy('detail.ivouchrowno', 'ASC') // 按 U8 原始行号排序
+                ->get();
+
+            // 3. 组合数据并返回
+            $order['details'] = $details->map(function ($item) {
+                return (array)$item;
+            })->toArray();
+
+            return [true, $order];
+
+        } catch (\Throwable $e) {
+            \Log::error("获取到货单失败: " . $e->getMessage());
+            return [false, "数据库查询异常: " . $e->getMessage()];
+        }
+    }
+
+    /**
+     * 获取存货档案(带分类名称)
+     * @param array $config 配置项
+     * @param mixed $lastId 上次分页的标识(这里用存货编码 cInvCode 或 自动增量标识)
+     * @return array [bool, items]
+     */
+    public function getInventoryData1($config, $lastId = null)
+    {
+        try {
+            $query = DB::connection('u8_third_sqlserver')
+                ->table('Inventory as main')
+                ->leftJoin('InventoryClass as sub', 'main.cInvCCode', '=', 'sub.cInvCCode')
+                ->select([
+                    'main.cInvCode as product_code',
+                    'main.cInvName as product_name',
+                    'main.cInvStd as product_size',
+                    'main.cInvCCode as product_category_code',
+                    'sub.cInvCName as product_category_name', // 直接联查分类名
+                    'main.cComUnitCode as product_unit',
+                    'main.fOutExcess as out_limit',
+                    'main.cCurrencyName as common_name',
+                    'main.fGrossW as grossWeight',
+                    'main.dModifyDate as upd_time'
+                ]);
+
+            // 分页逻辑
+            if ($lastId) {
+                $query->where('main.cInvCode', '>', $lastId);
+            }
+
+            // 基础过滤条件
+            if (!empty($config['whereRaw'])) {
+                $query->whereRaw($config['whereRaw']);
+            }
+
+            $items = $query->orderBy('main.cInvCode', 'asc')
+                ->limit($config['limit'])
+                ->get();
+
+            // 将 Collection 转为数组方便后续处理
+            $items = json_decode(json_encode($items), true);
+
+            return [true, $items];
+        } catch (\Exception $e) {
+            Log::channel('apiLog')->error("U8获取存货异常: " . $e->getMessage());
+            return [false, []];
+        }
+    }
+
+    public function getInventoryData($config, $lastCode = null)
+    {
+        try {
+            $query = DB::connection('u8_third_sqlserver')
+                ->table('Inventory as main')
+                ->leftJoin('InventoryClass as sub', 'main.cInvCCode', '=', 'sub.cInvCCode')
+                ->leftJoin('ComputationUnit as unit', 'main.cComUnitCode', '=', 'unit.cComUnitCode')
+                ->select([
+                    'main.cInvCode as product_code',
+                    'main.cInvName as product_name',
+                    'main.cInvStd as product_size',
+                    'main.cInvCCode as product_category_code',
+                    'sub.cInvCName as product_category_name', // 直接联查分类名
+                    'main.cComUnitCode as product_unit',
+                    'unit.cComUnitName as product_unit_title',
+//                    'main.fOutExcess as out_limit',
+//                    'main.cCurrencyName as common_name',
+                    'main.fGrossW as grossWeight',
+                    // 关键点:将二进制 ufts 转换为 0x... 字符串
+                    DB::raw("master.sys.fn_varbintohexstr(main.pubufts) as ufts_str")
+                ]);
+
+            // 使用 product_code 做分页依据
+            if ($lastCode) {
+                $query->where('main.cInvCode', '>', $lastCode);
+            }
+
+            if (!empty($config['whereRaw'])) {
+                $query->whereRaw($config['whereRaw']);
+            }
+
+            // 必须按编码排序,保证分页不重不漏
+            $items = $query->orderBy('main.cInvCode', 'asc')
+                ->limit($config['limit'])
+                ->get();
+
+            return [true, json_decode(json_encode($items), true)];
+        } catch (\Exception $e) {
+            Log::channel('apiLog')->error("U8获取存货异常: " . $e->getMessage());
+            return [false, []];
+        }
+    }
+}

+ 19 - 0
config/database.php

@@ -100,6 +100,25 @@ return [
             'username' => '',
             'password' => '',
         ],
+
+        'u8_third_sqlserver' => [
+            'driver'   => 'sqlsrv',
+            'host'     => env('SQLSRV_HOST'),
+            'port'     => env('SQLSRV_PORT'),
+            'database' => env('SQLSRV_DATABASE'), // 这里填用友账套数据库名
+            'username' => env('SQLSRV_USERNAME'),
+            'password' => env('SQLSRV_PASSWORD'),
+            'charset'  => 'utf8',
+            'prefix'   => '',
+            'options'  => [
+                // 关键:防止花生壳网络闪断导致的连接丢失,设置合理的超时
+                \PDO::SQLSRV_ATTR_QUERY_TIMEOUT => 30,
+                // 关键:开启连接池(在固定连接名下)有助于复用底层连接,减少握手次数
+//                'ConnectionPooling' => 1,
+                // 确保异常抛出,以便 Service 里的 try-catch 能抓到
+                \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
+            ],
+        ],
     ],
 
     /*

+ 16 - 0
config/wms.php

@@ -0,0 +1,16 @@
+<?php
+
+return [
+    "api_url" => "http://fat.informrack.com:7151/wms-admin",
+    "drb" => [
+        'api_host' => '',
+        'api_port' => '',
+        'database' => 'UFDATA_200_2021',
+        'user_id' => 'demo',
+        'user_password' => 'DEMO',
+        'database_port' => '41402',
+        'username' => 'sa',
+        'password' => 'Aa1',
+    ],
+];
+

+ 2 - 0
routes/api.php

@@ -30,6 +30,8 @@ Route::any('getTemplateFields','Api\DingTalkController@getTemplateFields');
 
 Route::any('dinCallback','Api\DingTalkController@dinCallback');
 
+Route::any('settleU8Data','Api\U8ThirdPartyController@settleU8Data');
+
 Route::group(['middleware'=> ['checkLogin']],function ($route){
     //文件上传统一方法
     $route->any('uploadFile', 'Api\FileUploadController@uploadFile');