From 9140aee230de0ef41de9682a9353fbd372e2bcaa Mon Sep 17 00:00:00 2001
From: chen.lin <1442464845@qq.com>
Date: 星期二, 03 三月 2026 13:43:22 +0800
Subject: [PATCH] 云仓WMS接口

---
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/AppMapper.java                                       |   11 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java                  |    3 
 version/db/open_api_app.sql                                                                                    |   16 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/AppController.java                      |   79 +
 rsf-server/src/main/resources/mapper/manager/AsnOrderMapper.xml                                                |   15 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/dto/CommonResponse.java                              |   20 
 rsf-server/src/main/java/com/vincent/rsf/server/ServerBoot.java                                                |    2 
 rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogShow.jsx                                                   |   41 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/AsnOrderMapper.java                             |    7 
 rsf-admin/src/page/system/openApiApp/index.jsx                                                                 |   11 
 rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/OpenApiAppMapper.java                            |   11 
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventorySummaryParam.java           |   23 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/AsnOrderTemplate.java                     |    4 
 rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/ReceiveMsgServiceImpl.java                    |  137 +
 rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/AsnOrderLogSchedule.java                     |  112 +
 version/db/open_api_app_menu.sql                                                                               |    7 
 rsf-admin/src/i18n/en.js                                                                                       |    1 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/CheckOrderParams.java                |    2 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/CloudWmsNotifyLog.java                          |   88 +
 rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/AsnOrderItemMapper.java                         |    4 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/OpenApi.java                                                |    2 
 rsf-server/src/main/java/com/vincent/rsf/server/api/feign/fallback/CloudWmsErpFeignClientFallbackFactory.java  |   17 
 rsf-server/src/main/java/com/vincent/rsf/server/system/service/OpenApiAppService.java                          |    7 
 rsf-server/src/main/java/com/vincent/rsf/server/api/service/CloudWmsReportService.java                         |   33 
 rsf-open-api/src/main/resources/application-dev.yml                                                            |    7 
 rsf-admin/src/layout/MyMenu.jsx                                                                                |    2 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/Constants.java                              |   15 
 rsf-server/src/main/resources/mapper/manager/AsnOrderItemMapper.xml                                            |    7 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/WmsConstant.java                            |   44 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/WmsErpServiceImpl.java                         |  379 +++-
 rsf-server/src/main/java/com/vincent/rsf/server/api/feign/fallback/CloudWmsErpFeignClientFallback.java         |   95 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/TokenServiceImpl.java                          |   56 
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventoryAdjustReportParam.java      |   37 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/CloudWmsNotifyLogMapper.java                    |   11 
 rsf-server/src/main/java/com/vincent/rsf/server/system/controller/OpenApiAppController.java                    |   78 +
 rsf-admin/src/config/MyDataProvider.js                                                                         |   36 
 version/db/cloud_wms_notify_config.sql                                                                         |    7 
 rsf-server/src/main/java/com/vincent/rsf/server/api/config/RemotesInfoProperties.java                          |   15 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsErpController.java                            |   87 
 rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/MobileServiceImpl.java                        |    2 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/AppServiceImpl.java                            |   11 
 rsf-open-api/pom.xml                                                                                           |   33 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/AsnOrderItemLog.java                            |    4 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/ErpMatnrParms.java                            |   17 
 rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogEdit.jsx                                                   |    2 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/filter/AppIdAuthenticationFilter.java              |   92 +
 rsf-server/src/main/resources/application-dev.yml                                                              |   16 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CloudWmsNotifyLogServiceImpl.java         |   90 +
 rsf-admin/src/page/histories/asnOrderLog/AsnOrderItemLogList.jsx                                               |   25 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/GetTokenParam.java                            |   21 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/ErpOpParams.java                              |   44 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/CryptoConfig.java                                    |   15 
 version/db/man_cloud_wms_notify_log.sql                                                                        |   25 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/utils/TokenUtils.java                              |   87 +
 rsf-admin/src/page/histories/outStockOrderLog/OutStockOrderLogList.jsx                                         |    7 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/AppService.java                                     |    7 
 rsf-server/src/main/java/com/vincent/rsf/server/api/service/ReceiveMsgService.java                             |   10 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/WmsServerFeignClient.java                         |   55 
 rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/OpenApiAppServiceImpl.java                 |   41 
 rsf-server/src/main/java/com/vincent/rsf/server/api/feign/CloudWmsErpFeignClient.java                          |   35 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/WebMvcConfig.java                                    |    2 
 rsf-open-api/src/main/resources/application-prod.yml                                                           |    2 
 rsf-server/src/main/java/com/vincent/rsf/server/system/entity/OpenApiApp.java                                  |   34 
 rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/CloudWmsReportServiceImpl.java                |   99 +
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InOutResultReportParam.java          |   42 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/AsnOrderServiceImpl.java                  |   36 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/FeignConfig.java                                     |   18 
 rsf-admin/src/page/ResourceContent.js                                                                          |    6 
 rsf-server/src/main/java/com/vincent/rsf/server/system/constant/GlobalConfigCode.java                          |   15 
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventoryDetailsParam.java           |   38 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/WmsOrderItemParam.java                        |   55 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WkOrderItem.java                                |    4 
 rsf-admin/src/page/histories/outStockOrderLog/index.jsx                                                        |   11 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/ApiSecurityConfig.java                               |   28 
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/ErpQueryController.java                     |   18 
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/SyncOrderParams.java                 |    6 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/erp/ErpReportFeignClient.java                         |   24 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/fallback/WmsServerFeignClientFallbackFactory.java |   17 
 rsf-admin/src/page/system/openApiApp/OpenApiAppList.jsx                                                        |   84 +
 rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java                           |    6 
 rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogCreate.jsx                                                 |    2 
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/BaseMatParms.java                    |   19 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/TokenService.java                                   |   20 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/TransferTemplate.java                     |    4 
 version/db/out_stock_order_log_menu.sql                                                                        |   10 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/WmsErpService.java                                  |   22 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java                      |   76 +
 rsf-admin/src/page/system/openApiApp/OpenApiAppCreate.jsx                                                      |   91 +
 rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogListBase.jsx                                               |  123 +
 rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogPanel.jsx                                                  |    4 
 rsf-admin/src/i18n/zh.js                                                                                       |   20 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/App.java                                         |   34 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/CloudWmsNotifyLogService.java                  |   29 
 rsf-server/pom.xml                                                                                             |   19 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/AuthController.java                              |   37 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/service/AppAuthService.java                        |   58 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/GlobalExceptionHandler.java                          |   38 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/ScheduleTriggerController.java              |   78 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/fallback/WmsServerFeignClientFallback.java        |  130 +
 rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogList.jsx                                                   |  213 --
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/CloudWmsMockController.java                     |   67 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/CustomErrorAttributes.java                           |   36 
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/FlexibleDateDeserializer.java        |   76 +
 rsf-open-api/src/main/resources/application.yml                                                                |    4 
 rsf-server/src/test/java/com/vincent/rsf/server/common/security/SecurityDemoControllerTest.java                |   29 
 rsf-admin/src/page/system/openApiApp/OpenApiAppEdit.jsx                                                        |   61 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/CloudWmsNotifySchedule.java                  |  123 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/dto/ResultData.java                                  |   31 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/ReviseLogServiceImpl.java                 |   47 
 109 files changed, 3,650 insertions(+), 564 deletions(-)

diff --git a/rsf-admin/src/config/MyDataProvider.js b/rsf-admin/src/config/MyDataProvider.js
index 1e13a53..ff209c0 100644
--- a/rsf-admin/src/config/MyDataProvider.js
+++ b/rsf-admin/src/config/MyDataProvider.js
@@ -1,14 +1,17 @@
 import request from "../utils/request";
 import * as Common from "../utils/common";
 
+// 鍑哄簱鍘嗗彶鍗曚笌鍏ュ簱鍘嗗彶鍗曞叡鐢� asnOrderLog 鎺ュ彛锛屼粎鍓嶇 resource 涓嶅悓
+const getApiResource = (resource) => (resource === "outStockOrderLog" ? "asnOrderLog" : resource);
+
 const MyDataProvider = {
   // *** https://marmelab.com/react-admin/DataProviderWriting.html ***
 
   // get a list of records based on sort, filter, and pagination
   getList: async (resource, params) => {
-    // console.log("getList", resource, params);
+    const apiResource = getApiResource(resource);
     const _params = Common.integrateParams(params);
-    const res = await request.post(resource + "/page", _params);
+    const res = await request.post(apiResource + "/page", _params);
     const { code, msg, data } = res.data;
     if (code === 200) {
       return Promise.resolve({
@@ -21,8 +24,8 @@
 
   // get a single record by id
   getOne: async (resource, params) => {
-    // console.log("getOne", resource, params);
-    const res = await request.get(resource + "/" + params.id);
+    const apiResource = getApiResource(resource);
+    const res = await request.get(apiResource + "/" + params.id);
     const { code, msg, data } = res.data;
     if (code === 200) {
       return Promise.resolve({
@@ -37,11 +40,11 @@
 
   // get a list of records based on an array of ids
   getMany: async (resource, params) => {
-
     if (resource === "user") {
       await new Promise((r) => setTimeout(r, 1000));
     }
-    const res = await request.post(resource + "/many/" + params.ids);
+    const apiResource = getApiResource(resource);
+    const res = await request.post(apiResource + "/many/" + params.ids);
     const { code, msg, data } = res.data;
     if (code === 200) {
       return Promise.resolve({
@@ -63,7 +66,8 @@
 
   // create a record
   create: async (resource, params) => {
-    const res = await request.post(resource + "/save", params?.data);
+    const apiResource = getApiResource(resource);
+    const res = await request.post(apiResource + "/save", params?.data);
     const { code, msg, data } = res.data;
     if (code === 200) {
       return Promise.resolve({
@@ -77,7 +81,8 @@
 
   // update a record based on a patch
   update: async (resource, params) => {
-    const res = await request.post(resource + "/update", {
+    const apiResource = getApiResource(resource);
+    const res = await request.post(apiResource + "/update", {
       id: params.id,
       ...params.data,
     });
@@ -92,9 +97,9 @@
 
   // update a list of records based on an array of ids and a common patch
   updateMany: async (resource, params) => {
-    console.log("updateMany", resource, params);
+    const apiResource = getApiResource(resource);
     const res = await request.post(
-      resource + "/update/many",
+      apiResource + "/update/many",
       params.ids.map((id) => ({ id, ...params.data })),
     );
     const { code, msg, data } = res.data;
@@ -108,8 +113,8 @@
 
   // delete a record by id
   delete: async (resource, params) => {
-    console.log("delete", resource, params);
-    const res = await request.post(resource + "/remove/" + [params.id]);
+    const apiResource = getApiResource(resource);
+    const res = await request.post(apiResource + "/remove/" + [params.id]);
     const { code, msg, data } = res.data;
     if (code === 200) {
       return Promise.resolve({
@@ -123,8 +128,8 @@
 
   // delete a list of records based on an array of ids
   deleteMany: async (resource, params) => {
-    console.log("deleteMany", resource, params);
-    const res = await request.post(resource + "/remove/" + params?.ids);
+    const apiResource = getApiResource(resource);
+    const res = await request.post(apiResource + "/remove/" + params?.ids);
     const { code, msg, data } = res.data;
     if (code === 200) {
       return Promise.resolve({
@@ -136,9 +141,10 @@
 
   // export excel from all data
   export: async (resource, params) => {
+    const apiResource = getApiResource(resource);
     const _params = Common.integrateParams(params);
     try {
-      const res = await request.post(`${resource}/export`, _params, {
+      const res = await request.post(`${apiResource}/export`, _params, {
         responseType: "blob",
       });
       return res;
diff --git a/rsf-admin/src/i18n/en.js b/rsf-admin/src/i18n/en.js
index feb2d8a..d04c278 100644
--- a/rsf-admin/src/i18n/en.js
+++ b/rsf-admin/src/i18n/en.js
@@ -174,6 +174,7 @@
         asnOrder: 'AsnOrder',
         asnOrderItem: 'AsnOrderItem',
         asnOrderLog: 'asnOrderLog',
+        outStockOrderLog: 'Outbound Order Log',
         asnOrderItemLog: 'asnOrderItemLog',
         purchase: 'Purchase',
         purchaseItem: 'PurchaseItem',
diff --git a/rsf-admin/src/i18n/zh.js b/rsf-admin/src/i18n/zh.js
index 089f883..f11001f 100644
--- a/rsf-admin/src/i18n/zh.js
+++ b/rsf-admin/src/i18n/zh.js
@@ -3,6 +3,9 @@
 const customChineseMessages = {
     ...chineseMessages,
     hello: '浣犲ソ涓栫晫',
+    resources: {
+        config: { name: '閰嶇疆鍙傛暟' },
+    },
     common: {
         response: {
             success: "鎿嶄綔鎴愬姛",
@@ -172,9 +175,10 @@
         companys: '寰�鏉ヤ紒涓�',
         serialRuleItem: '缂栫爜瑙勫垯瀛愯〃',
         serialRule: '缂栫爜瑙勫垯',
-        asnOrder: '鏀惰揣閫氱煡鍗�',
+        asnOrder: '鍏ュ簱閫氱煡鍗�',
         asnOrderItem: '鏀惰揣鏄庣粏',
-        asnOrderLog: '鏀惰揣鍘嗗彶鍗�',
+        asnOrderLog: '鍏ュ簱鍘嗗彶鍗�',
+        outStockOrderLog: '鍑哄簱鍘嗗彶鍗�',
         asnOrderItemLog: '鏀惰揣鍘嗗彶鏄庣粏',
         purchase: 'PO鍗�',
         purchaseItem: 'PO鍗曟槑缁�',
@@ -202,7 +206,7 @@
         logs: '鏃ュ織',
         permissions: '鏉冮檺绠$悊',
         delivery: 'DO鍗�',
-        outStock: '鍑哄簱鍗�',
+        outStock: '鍑哄簱閫氱煡鍗�',
         outStockItem: '鍑哄簱鍗曟槑缁�',
         inStockPoces: '鍏ュ簱绠$悊',
         outStockPoces: '鍑哄簱绠$悊',
@@ -647,7 +651,7 @@
                 maxWeight: "鏈�澶ч噸閲�",
             },
             asnOrder: {
-                code: "ASN鍗曞彿",
+                code: "WMS鍗曞彿",
                 poCode: "PO缂栫爜",
                 poId: "PO鏍囪瘑",
                 type: "鍗曟嵁绫诲瀷",
@@ -1389,6 +1393,14 @@
         selectWave: '娉㈡瑙勫垯',
     },
     ra: {
+        boolean: {
+            true: '姝e父',
+            false: '绂佺敤',
+        },
+        message: {
+            delete_title: '鍒犻櫎 %{name} #%{id}',
+            delete_content: '鎮ㄧ‘瀹氳鍒犻櫎姝ら」鍚楋紵',
+        },
         action: {
             search: '鎼滅储',
             add_filter: '杩囨护鏉′欢',
diff --git a/rsf-admin/src/layout/MyMenu.jsx b/rsf-admin/src/layout/MyMenu.jsx
index 1d5e543..f3fa3b1 100644
--- a/rsf-admin/src/layout/MyMenu.jsx
+++ b/rsf-admin/src/layout/MyMenu.jsx
@@ -81,7 +81,7 @@
       } else {
         if (node.component) {
           // RCS娴嬭瘯锛氭寚鍚戠嫭绔嬮〉璺敱锛屽彲鍗曠嫭鎵撳紑涓�鏁撮〉锛堟棤渚ц竟鏍�/鏍囩鏍忥級
-          const to = node.component === 'rcsTest' ? '/rcsTest-page' : node.component;
+          const to = node.component === 'rcsTest' ? '/rcsTest-page' : `/${node.component}`;
           return (
             <MenuItemLink
               key={node.id}
diff --git a/rsf-admin/src/page/ResourceContent.js b/rsf-admin/src/page/ResourceContent.js
index b5be33d..9be9260 100644
--- a/rsf-admin/src/page/ResourceContent.js
+++ b/rsf-admin/src/page/ResourceContent.js
@@ -38,6 +38,7 @@
 import waitPakin from "./waitPakin";
 import waitPakinLog from "./histories/waitPakinLog";
 import asnOrderLog from "./histories/asnOrderLog";
+import outStockOrderLog from "./histories/outStockOrderLog";
 import task from "./task";
 import taskLog from "./histories/taskLog";
 import stock from "./orders/stock";
@@ -65,6 +66,7 @@
 import inStatisticItem from './statistics/inStockItem';
 import statisticCount from './statistics/stockStatisticNum';
 import rcsTest from './rcsTest';
+import openApiApp from './system/openApiApp';
 
 const ResourceContent = (node) => {
   switch (node.component) {
@@ -120,6 +122,8 @@
       return asnOrder;
     case "asnOrderLog":
       return asnOrderLog;
+    case "outStockOrderLog":
+      return outStockOrderLog;
     case "purchase":
       return purchase;
     case "fields":
@@ -190,6 +194,8 @@
       return statisticCount;
     case "rcsTest":
       return rcsTest;
+    case "openApiApp":
+      return openApiApp;
     default:
       return {
         list: ListGuesser,
diff --git a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderItemLogList.jsx b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderItemLogList.jsx
index dc87470..b03b432 100644
--- a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderItemLogList.jsx
+++ b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderItemLogList.jsx
@@ -86,12 +86,12 @@
     <TextInput source="trackCode" label="table.field.asnOrderItemLog.trackCode" />,
     <TextInput source="barcode" label="table.field.asnOrderItemLog.barcode" />,
     <TextInput source="packName" label="table.field.asnOrderItemLog.packName" />,
-    <SelectInput source="ntyStatus" label="table.field.asnOrderItemLog.ntyStatus"
-        choices={[
-            { id: 0, name: ' 鏈笂鎶�' },
-            { id: 1, name: ' 宸蹭笂鎶�' },
-        ]}
-    />,
+    // <SelectInput source="ntyStatus" label="table.field.asnOrderItemLog.ntyStatus"
+    //     choices={[
+    //         { id: 0, name: ' 鏈笂鎶�' },
+    //         { id: 1, name: ' 宸蹭笂鎶�' },
+    //     ]}
+    // />,
 
     <TextInput label="common.field.memo" source="memo" />,
     <SelectInput
@@ -105,11 +105,16 @@
     />,
 ]
 
-const AsnOrderItemLogList = () => {
+/**
+ * @param {Object} props
+ * @param {number} [props.logId] - 鍏ュ簱鍘嗗彶鍗曚富閿紝浼犲叆鏃跺彧鏄剧ず璇ュ崟鐨勬槑缁嗭紙鐢ㄤ簬璇︽儏椤碉級
+ */
+const AsnOrderItemLogList = ({ logId: logIdProp }) => {
     const translate = useTranslate();
     const [createDialog, setCreateDialog] = useState(false);
     const [drawerVal, setDrawerVal] = useState(false);
-    const recodeId = useGetRecordId();
+    const recordId = useGetRecordId();
+    const logId = logIdProp != null ? logIdProp : recordId;
 
     return (
         <Box display="flex">
@@ -126,7 +131,7 @@
                 title={"menu.asnOrderItemLog"}
                 empty={false}
                 filters={filters}
-                filter={{ logId: recodeId }}
+                filter={{ logId }}
                 sort={{ field: "create_time", order: "desc" }}
                 actions={(
                     <TopToolbar>
@@ -163,7 +168,7 @@
                     <TextField source="qrcode" label="table.field.asnOrderItemLog.qrcode" />
                     <TextField source="trackCode" label="table.field.asnOrderItemLog.trackCode" />
                     <TextField source="packName" label="table.field.asnOrderItemLog.packName" />
-                    <TextField source="ntyStatus$" label="table.field.asnOrderItemLog.ntyStatus" sortable={false} />
+                    {/*<TextField source="ntyStatus$" label="table.field.asnOrderItemLog.ntyStatus" sortable={false} />*/}
                     <TextField source="updateBy$" label="common.field.updateBy" />
                     <TextField source="createBy$" label="common.field.createBy" />
                     <DateField source="createTime" label="common.field.createTime" showTime />
diff --git a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogCreate.jsx b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogCreate.jsx
index 4598f98..9230d02 100644
--- a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogCreate.jsx
+++ b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogCreate.jsx
@@ -158,6 +158,7 @@
                                         ]}
                                     />
                                 </Grid>
+                                {/* 璐ㄦ涓婃姤鐘舵��
                                 <Grid item xs={6} display="flex" gap={1}>
                                     <SelectInput
                                         label="table.field.asnOrderLog.ntyStatus"
@@ -169,6 +170,7 @@
                                         ]}
                                     />
                                 </Grid>
+                                */}
 
                                 <Grid item xs={6} display="flex" gap={1}>
                                     <StatusSelectInput />
diff --git a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogEdit.jsx b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogEdit.jsx
index e737102..2985232 100644
--- a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogEdit.jsx
+++ b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogEdit.jsx
@@ -126,6 +126,7 @@
                                     readOnly
                                     source="arrTime"
                                 />
+                                {/* 璐ㄦ涓婃姤鐘舵��
                                 <SelectInput
                                     label="table.field.asnOrderLog.ntyStatus"
                                     source="ntyStatus"
@@ -137,6 +138,7 @@
                                     ]}
                                     validate={required()}
                                 />
+                                */}
                             </Stack>
                         </Grid>
                     </Grid>
diff --git a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogList.jsx b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogList.jsx
index 8eb27ae..0000393 100644
--- a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogList.jsx
+++ b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogList.jsx
@@ -1,210 +1,7 @@
-import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
-import { useNavigate } from 'react-router-dom';
-import {
-    List,
-    DatagridConfigurable,
-    SearchInput,
-    TopToolbar,
-    SelectColumnsButton,
-    EditButton,
-    FilterButton,
-    CreateButton,
-    ExportButton,
-    BulkDeleteButton,
-    WrapperField,
-    useRecordContext,
-    useTranslate,
-    useNotify,
-    useListContext,
-    FunctionField,
-    TextField,
-    NumberField,
-    DateField,
-    BooleanField,
-    ReferenceField,
-    TextInput,
-    DateTimeInput,
-    DateInput,
-    SelectInput,
-    NumberInput,
-    ReferenceInput,
-    ReferenceArrayInput,
-    AutocompleteInput,
-    DeleteButton,
-    Button,
-    useRecordSelection,
-    useRefresh,
-} from 'react-admin';
-import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
-import DictionarySelect from "../../components/DictionarySelect";
-import MyCreateButton from "../../components/MyCreateButton";
-import MyExportButton from '../../components/MyExportButton';
-import { Box, Typography, Card, Stack } from '@mui/material';
-import ConfirmButton from '../../components/ConfirmButton';
-import PageDrawer from "../../components/PageDrawer";
-import AsnOrderLogCreate from "./AsnOrderLogCreate";
-import CachedIcon from '@mui/icons-material/Cached';
-import EmptyData from "../../components/EmptyData";
-import AsnOrderLogPanel from "./AsnOrderLogPanel";
-import { styled } from '@mui/material/styles';
-import * as Common from '@/utils/common';
-import request from '@/utils/request';
+import React from "react";
+import AsnOrderLogListBase from "./AsnOrderLogListBase";
 
-
-const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
-    '& .css-1vooibu-MuiSvgIcon-root': {
-        height: '.9em'
-    },
-    '& .RaDatagrid-row': {
-        cursor: 'auto'
-    },
-    '& .column-name': {
-    },
-    '& .opt': {
-        width: 150
-    },
-    '& .MuiTableCell-root': {
-        whiteSpace: 'nowrap',
-        overflow: 'visible',
-        textOverflow: 'unset'
-    }
-}));
-
-
-
-const AsnOrderLogList = () => {
-    const translate = useTranslate();
-    const [createDialog, setCreateDialog] = useState(false);
-    const [drawerVal, setDrawerVal] = useState(false);
-    const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_order_type')) || [];
-
-    const filters = [
-        <SearchInput source="condition" alwaysOn />,
-        <TextInput source="code" label="table.field.asnOrderLog.code" />,
-        <TextInput source="poCode" label="table.field.asnOrderLog.poCode" />,
-        <NumberInput source="poId" label="table.field.asnOrderLog.poId" />,
-        // <TextInput source="type" label="table.field.asnOrderLog.type" />,
-        // <TextInput source="wkType" label="table.field.asnOrderLog.wkType" />,
-        <NumberInput source="anfme" label="table.field.asnOrderLog.anfme" />,
-        <NumberInput source="qty" label="table.field.asnOrderLog.qty" />,
-        <TextInput source="logisNo" label="table.field.asnOrderLog.logisNo" />,
-        <DateInput source="arrTime" label="table.field.asnOrderLog.arrTime" />,
-        // <SelectInput source="ntyStatus" label="table.field.asnOrderLog.ntyStatus"
-        //     choices={[
-        //         { id: 0, name: ' 鏈笂鎶�' },
-        //         { id: 1, name: ' 宸蹭笂鎶�' },
-        //         { id: 2, name: ' 閮ㄥ垎涓婃姤' },
-        //     ]}
-        // />,
-        <AutocompleteInput
-            choices={dicts}
-            optionText="label"
-            label="table.field.asnOrder.type"
-            source="type"
-            // defaultValue="in"
-            optionValue="value"
-            parse={v => v}
-            alwaysOn
-        />,
-        <ReferenceInput source="wkType" reference="dictData" filter={{ dictTypeCode: 'sys_business_type', group: "1" }} label="table.field.asnOrder.wkType" alwaysOn>
-            <AutocompleteInput label="table.field.asnOrder.wkType" optionValue="value" />
-        </ReferenceInput>,
-        <DictionarySelect
-            label='table.field.asnOrder.exceStatus'
-            name="exceStatus"
-            group="1"
-            dictTypeCode="sys_asn_exce_status"
-            alwaysOn
-        />,
-    ]
-
-    return (
-        <Box display="flex">
-            <List
-                sx={{
-                    flexGrow: 1,
-                    transition: (theme) =>
-                        theme.transitions.create(['all'], {
-                            duration: theme.transitions.duration.enteringScreen,
-                        }),
-                    marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
-                }}
-                title={"menu.asnOrderLog"}
-                empty={false}
-                filters={filters}
-                sort={{ field: "create_time", order: "desc" }}
-                actions={(
-                    <TopToolbar>
-                        <FilterButton />
-                        <SelectColumnsButton preferenceKey='asnOrderLog' />
-                        {/* <MyExportButton /> */}
-                    </TopToolbar>
-                )}
-                perPage={DEFAULT_PAGE_SIZE}
-            >
-                <StyledDatagrid
-                    preferenceKey='asnOrderLog'
-                    bulkActionButtons={false}
-                    rowClick={'edit'}
-                    expand={false}
-                    expandSingle={true}
-                    omit={['id', 'createTime', 'createBy', 'memo', 'logisNo', 'poId', 'rleStatus$', 'statusBool', 'createBy$']}
-                >
-                    <NumberField source="id" />
-                    <TextField source="code" label="table.field.asnOrderLog.code" />
-                    <TextField source="poCode" label="table.field.asnOrderLog.poCode" />
-                    <NumberField source="poId" label="table.field.asnOrderLog.poId" />
-                    <TextField source="type$" label="table.field.asnOrderLog.type" />
-                    <TextField source="wkType$" label="table.field.asnOrderLog.wkType" />
-                    <NumberField source="anfme" label="table.field.asnOrderLog.anfme" />
-                    <NumberField source="qty" label="table.field.asnOrderLog.qty" />
-                    <TextField source="logisNo" label="table.field.asnOrderLog.logisNo" />
-                    <DateField source="arrTime" label="table.field.asnOrderLog.arrTime" showTime />
-                    <TextField source="rleStatus$" label="table.field.asnOrderLog.rleStatus" sortable={false} />
-                    <TextField source="ntyStatus$" label="table.field.asnOrderLog.ntyStatus" sortable={false} />
-                    <TextField source="updateBy$" label="common.field.updateBy" />
-                    <DateField source="updateTime" label="common.field.updateTime" showTime />
-                    <TextField source="createBy$" label="common.field.createBy" />
-                    <DateField source="createTime" label="common.field.createTime" showTime />
-                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
-                    <TextField source="memo" label="common.field.memo" sortable={false} />
-                    <WrapperField cellClassName="opt" label="common.field.opt">
-                        <ContinueButton />
-                    </WrapperField>
-                </StyledDatagrid>
-            </List>
-            <AsnOrderLogCreate
-                open={createDialog}
-                setOpen={setCreateDialog}
-            />
-            <PageDrawer
-                title='AsnOrderLog Detail'
-                drawerVal={drawerVal}
-                setDrawerVal={setDrawerVal}
-            >
-            </PageDrawer>
-        </Box>
-    )
+/** 鍏ュ簱鍘嗗彶鍗曞垪琛細鍥哄畾 type=in锛岃姹傚悗绔� asnOrderLog 鎺ュ彛 */
+export default function AsnOrderLogList() {
+    return <AsnOrderLogListBase typeFilter="in" listTitle="menu.asnOrderLog" />;
 }
-
-export default AsnOrderLogList;
-
-
-const ContinueButton = () => {
-    const refresh = useRefresh();
-    const record = useRecordContext();
-    const notify = useNotify();
-    const continueReceipt = async () => {
-        const { data: { code, data, msg } } = await request.post(`/asnOrderLog/continue/${record.id}`);
-        if (code === 200) {
-            notify(msg);
-        } else {
-            notify(msg);
-        }
-        refresh();
-    }
-
-    return (
-        record.type == 'in' ? <ConfirmButton label={"toolbar.continue"} startIcon={<CachedIcon />} onConfirm={continueReceipt} /> : <></>
-    )
-}
\ No newline at end of file
diff --git a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogListBase.jsx b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogListBase.jsx
new file mode 100644
index 0000000..b12b131
--- /dev/null
+++ b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogListBase.jsx
@@ -0,0 +1,123 @@
+import React, { useState } from "react";
+import {
+    List,
+    DatagridConfigurable,
+    SearchInput,
+    TopToolbar,
+    SelectColumnsButton,
+    ShowButton,
+    FilterButton,
+    WrapperField,
+    TextField,
+    NumberField,
+    DateField,
+    BooleanField,
+    TextInput,
+    DateInput,
+    NumberInput,
+    ReferenceInput,
+    AutocompleteInput,
+} from 'react-admin';
+import { PAGE_DRAWER_WIDTH, DEFAULT_PAGE_SIZE } from '@/config/setting';
+import DictionarySelect from "../../components/DictionarySelect";
+import { Box } from '@mui/material';
+import PageDrawer from "../../components/PageDrawer";
+import AsnOrderLogCreate from "./AsnOrderLogCreate";
+import { styled } from '@mui/material/styles';
+
+const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
+    '& .css-1vooibu-MuiSvgIcon-root': { height: '.9em' },
+    '& .RaDatagrid-row': { cursor: 'auto' },
+    '& .opt': { width: 150 },
+    '& .MuiTableCell-root': { whiteSpace: 'nowrap', overflow: 'visible', textOverflow: 'unset' }
+}));
+
+/**
+ * 鍏ュ簱/鍑哄簱鍘嗗彶鍗曞垪琛ㄥ叡鐢ㄩ鏋讹紝浠� type 涓庢爣棰樼敱澶栭儴浼犲叆銆�
+ * 鍏ュ簱鍘嗗彶鍗曪細typeFilter="in", listTitle="menu.asnOrderLog"
+ * 鍑哄簱鍘嗗彶鍗曪細typeFilter="out", listTitle="menu.outStockOrderLog"
+ * 鍚庣鎺ュ彛鍧囦负 asnOrderLog锛堝嚭搴撶敱 dataProvider 鏄犲皠锛夛紝鍙傛暟 filter.type 涓嶅悓銆�
+ */
+export default function AsnOrderLogListBase({ typeFilter, listTitle }) {
+    const [createDialog, setCreateDialog] = useState(false);
+    const [drawerVal, setDrawerVal] = useState(false);
+
+    const filters = [
+        <SearchInput source="condition" alwaysOn />,
+        <TextInput source="code" label="table.field.asnOrderLog.code" />,
+        <TextInput source="poCode" label="table.field.asnOrderLog.poCode" />,
+        <NumberInput source="poId" label="table.field.asnOrderLog.poId" />,
+        <NumberInput source="anfme" label="table.field.asnOrderLog.anfme" />,
+        <NumberInput source="qty" label="table.field.asnOrderLog.qty" />,
+        <TextInput source="logisNo" label="table.field.asnOrderLog.logisNo" />,
+        <DateInput source="arrTime" label="table.field.asnOrderLog.arrTime" />,
+        <ReferenceInput source="wkType" reference="dictData" filter={{ dictTypeCode: 'sys_business_type', group: "1" }} label="table.field.asnOrder.wkType" alwaysOn>
+            <AutocompleteInput label="table.field.asnOrder.wkType" optionValue="value" />
+        </ReferenceInput>,
+        <DictionarySelect
+            label='table.field.asnOrder.exceStatus'
+            name="exceStatus"
+            group="1"
+            dictTypeCode="sys_asn_exce_status"
+            alwaysOn
+        />,
+    ];
+
+    return (
+        <Box display="flex">
+            <List
+                key={`orderLog-list-${typeFilter}`}
+                sx={{
+                    flexGrow: 1,
+                    transition: (theme) => theme.transitions.create(['all'], { duration: theme.transitions.duration.enteringScreen }),
+                    marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
+                }}
+                title={listTitle}
+                empty={false}
+                filters={filters}
+                filterDefaultValues={{ type: typeFilter }}
+                filter={{ type: typeFilter }}
+                sort={{ field: "create_time", order: "desc" }}
+                actions={(
+                    <TopToolbar>
+                        <FilterButton />
+                        <SelectColumnsButton preferenceKey='asnOrderLog' />
+                    </TopToolbar>
+                )}
+                perPage={DEFAULT_PAGE_SIZE}
+            >
+                <StyledDatagrid
+                    preferenceKey='asnOrderLog'
+                    bulkActionButtons={false}
+                    rowClick={false}
+                    expand={false}
+                    expandSingle={true}
+                    omit={['id', 'createTime', 'createBy', 'memo', 'logisNo', 'poId', 'rleStatus$', 'statusBool', 'createBy$']}
+                >
+                    <NumberField source="id" />
+                    <TextField source="code" label="table.field.asnOrderLog.code" />
+                    <TextField source="poCode" label="table.field.asnOrderLog.poCode" />
+                    <NumberField source="poId" label="table.field.asnOrderLog.poId" />
+                    <TextField source="type$" label="table.field.asnOrderLog.type" />
+                    <TextField source="wkType$" label="table.field.asnOrderLog.wkType" />
+                    <NumberField source="anfme" label="table.field.asnOrderLog.anfme" />
+                    <NumberField source="qty" label="table.field.asnOrderLog.qty" />
+                    <TextField source="logisNo" label="table.field.asnOrderLog.logisNo" />
+                    <DateField source="arrTime" label="table.field.asnOrderLog.arrTime" showTime />
+                    <TextField source="rleStatus$" label="table.field.asnOrderLog.rleStatus" sortable={false} />
+                    <TextField source="updateBy$" label="common.field.updateBy" />
+                    <DateField source="updateTime" label="common.field.updateTime" showTime />
+                    <TextField source="createBy$" label="common.field.createBy" />
+                    <DateField source="createTime" label="common.field.createTime" showTime />
+                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
+                    <TextField source="memo" label="common.field.memo" sortable={false} />
+                    <WrapperField cellClassName="opt" label="common.field.opt">
+                        <ShowButton label="toolbar.detail" />
+                    </WrapperField>
+                </StyledDatagrid>
+            </List>
+            <AsnOrderLogCreate open={createDialog} setOpen={setCreateDialog} />
+            <PageDrawer title='AsnOrderLog Detail' drawerVal={drawerVal} setDrawerVal={setDrawerVal} />
+        </Box>
+    );
+}
diff --git a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogPanel.jsx b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogPanel.jsx
index a335119..4ae86d4 100644
--- a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogPanel.jsx
+++ b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogPanel.jsx
@@ -104,10 +104,12 @@
             field: 'packName',
             headerName: translate('table.field.asnOrderItemLog.packName')
         },
+        /* 璐ㄦ涓婃姤鐘舵��
         {
             field: 'ntyStatus$',
             headerName: translate('table.field.asnOrderItemLog.ntyStatus')
-        }]
+        }
+        */]
 
     const maktxChange = (value) => {
         setMaktx(value)
diff --git a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogShow.jsx b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogShow.jsx
index 6ba1720..2acd903 100644
--- a/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogShow.jsx
+++ b/rsf-admin/src/page/histories/asnOrderLog/AsnOrderLogShow.jsx
@@ -1,12 +1,27 @@
 import { BooleanField, DateField, NumberField, ReferenceField, Show, SimpleShowLayout, TextField ,DateInput,
     SelectInput,required,useTranslate,
     useRecordContext,} from 'react-admin';
-    import { Stack, Grid, Box, Typography, Card } from '@mui/material';
-    import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
-    import EditBaseAside from "../../components/EditBaseAside";
-    import CustomerTopToolBar from "../../components/EditTopToolBar";
-    import AsnOrderItemLogList from "./AsnOrderItemLogList"
+import { Stack, Grid, Box, Typography, Card } from '@mui/material';
+import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
+import EditBaseAside from "../../components/EditBaseAside";
+import CustomerTopToolBar from "../../components/EditTopToolBar";
+import AsnOrderItemLogList from "./AsnOrderItemLogList";
 
+const AsnOrderLogDetailWithItems = () => {
+    const record = useRecordContext();
+    const translate = useTranslate();
+    if (!record?.id) return null;
+    return (
+        <>
+            <Grid item xs={24} md={16} sx={{ marginTop: '1em', width: '100%' }}>
+                <Typography variant="h6" gutterBottom>
+                    {translate('common.edit.title.common')}
+                </Typography>
+            </Grid>
+            <AsnOrderItemLogList logId={record.id} />
+        </>
+    );
+};
 
 const Aa = () =>{
     const translate = useTranslate();
@@ -22,7 +37,6 @@
             <SimpleShowLayout
             shouldUnregister
             warnWhenUnsavedChanges
-            
             mode="onTouched"
             defaultValues={{}}
             >
@@ -113,28 +127,23 @@
                                     <DateField source="arrTime" label="type" showTime/>
                                 </Box>
                             </Grid>
-                            <Grid item  display="flex" gap={1} minWidth={150}>                            
+                            {/* 璐ㄦ涓婃姤鐘舵��
+                            <Grid item  display="flex" gap={1} minWidth={150}>
                                 <Box flexGrow={1}>
                                     <Typography variant="body2" sx={{fontSize: 20}}>
                                         {translate('table.field.asnOrderLog.ntyStatus')}
                                     </Typography>
                                     <TextField source="ntyStatus$" label="type"/>
                                 </Box>
-                            </Grid> 
+                            </Grid>
+                            */} 
                         </Stack>
                     </Grid>
                 </Grid>
-    
+                <AsnOrderLogDetailWithItems />
             </SimpleShowLayout>
         </Show>
-        <Grid item xs={24} md={16} sx={{ marginTop: '1em' }}>
-                <Typography variant="h6" gutterBottom >
-                    {translate('common.edit.title.common')}
-                </Typography>
-            </Grid>
-            <AsnOrderItemLogList />
         </>
-        
        ); 
 }
 
diff --git a/rsf-admin/src/page/histories/outStockOrderLog/OutStockOrderLogList.jsx b/rsf-admin/src/page/histories/outStockOrderLog/OutStockOrderLogList.jsx
new file mode 100644
index 0000000..2c56e16
--- /dev/null
+++ b/rsf-admin/src/page/histories/outStockOrderLog/OutStockOrderLogList.jsx
@@ -0,0 +1,7 @@
+import React from "react";
+import AsnOrderLogListBase from "../asnOrderLog/AsnOrderLogListBase";
+
+/** 鍑哄簱鍘嗗彶鍗曞垪琛細鍥哄畾 type=out锛岃姹傚悗绔� asnOrderLog 鎺ュ彛锛坉ataProvider 鏄犲皠 outStockOrderLog鈫抋snOrderLog锛� */
+export default function OutStockOrderLogList() {
+    return <AsnOrderLogListBase typeFilter="out" listTitle="menu.outStockOrderLog" />;
+}
diff --git a/rsf-admin/src/page/histories/outStockOrderLog/index.jsx b/rsf-admin/src/page/histories/outStockOrderLog/index.jsx
new file mode 100644
index 0000000..696cdaa
--- /dev/null
+++ b/rsf-admin/src/page/histories/outStockOrderLog/index.jsx
@@ -0,0 +1,11 @@
+import React from "react";
+import OutStockOrderLogList from "./OutStockOrderLogList";
+import AsnOrderLogEdit from "../asnOrderLog/AsnOrderLogEdit";
+import AsnorderlogShow from "../asnOrderLog/AsnOrderLogShow";
+
+export default {
+    list: OutStockOrderLogList,
+    edit: AsnOrderLogEdit,
+    show: AsnorderlogShow,
+    recordRepresentation: (record) => `${record.id}`,
+};
diff --git a/rsf-admin/src/page/system/openApiApp/OpenApiAppCreate.jsx b/rsf-admin/src/page/system/openApiApp/OpenApiAppCreate.jsx
new file mode 100644
index 0000000..1093def
--- /dev/null
+++ b/rsf-admin/src/page/system/openApiApp/OpenApiAppCreate.jsx
@@ -0,0 +1,91 @@
+import React from "react";
+import {
+    CreateBase,
+    useTranslate,
+    TextInput,
+    NumberInput,
+    SaveButton,
+    SelectInput,
+    Toolbar,
+    Form,
+    required,
+    useNotify,
+} from 'react-admin';
+import { Dialog, DialogContent, DialogTitle, Grid, Box } from '@mui/material';
+import DialogCloseButton from "@/page/components/DialogCloseButton";
+
+const OpenApiAppCreate = (props) => {
+    const { open, setOpen } = props;
+    const notify = useNotify();
+
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") setOpen(false);
+    };
+
+    const handleSuccess = () => {
+        setOpen(false);
+        notify('鏂板鎴愬姛');
+    };
+
+    const handleError = (error) => {
+        notify(error?.message || '鏂板澶辫触', { type: 'error' });
+    };
+
+    return (
+        <CreateBase
+            resource="openApiApp"
+            record={{}}
+            mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
+        >
+            <Dialog
+                open={open}
+                onClose={handleClose}
+                fullWidth
+                disableRestoreFocus
+                maxWidth="sm"
+            >
+                <Form>
+                    <DialogTitle sx={{ position: 'sticky', top: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                        鏂板搴旂敤
+                        <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
+                            <DialogCloseButton onClose={handleClose} />
+                        </Box>
+                    </DialogTitle>
+                    <DialogContent sx={{ mt: 2 }}>
+                        <Grid container rowSpacing={2} columnSpacing={2}>
+                            <Grid item xs={12}>
+                                <TextInput label="搴旂敤ID" source="id" validate={required()} fullWidth />
+                            </Grid>
+                            <Grid item xs={12}>
+                                <TextInput label="搴旂敤瀵嗛挜" source="screct" validate={required()} fullWidth />
+                            </Grid>
+                            <Grid item xs={12}>
+                                <TextInput label="搴旂敤鍚嶇О" source="name" fullWidth />
+                            </Grid>
+                            <Grid item xs={12}>
+                                <TextInput label="搴旂敤URL" source="url" fullWidth />
+                            </Grid>
+                            <Grid item xs={12}>
+                                <SelectInput
+                                    label="鍚敤鐘舵��"
+                                    source="enable"
+                                    choices={[
+                                        { id: 1, name: '鍚敤' },
+                                        { id: 0, name: '鏈惎鐢�' },
+                                    ]}
+                                    defaultValue={1}
+                                    fullWidth
+                                />
+                            </Grid>
+                        </Grid>
+                    </DialogContent>
+                    <Box sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000, p: 2, display: 'flex', justifyContent: 'flex-end' }}>
+                        <Toolbar><SaveButton /></Toolbar>
+                    </Box>
+                </Form>
+            </Dialog>
+        </CreateBase>
+    );
+};
+
+export default OpenApiAppCreate;
diff --git a/rsf-admin/src/page/system/openApiApp/OpenApiAppEdit.jsx b/rsf-admin/src/page/system/openApiApp/OpenApiAppEdit.jsx
new file mode 100644
index 0000000..d6aef54
--- /dev/null
+++ b/rsf-admin/src/page/system/openApiApp/OpenApiAppEdit.jsx
@@ -0,0 +1,61 @@
+import React from "react";
+import {
+    Edit,
+    SimpleForm,
+    TextInput,
+    SaveButton,
+    SelectInput,
+    Toolbar,
+    required,
+    DeleteButton,
+} from 'react-admin';
+import { Grid, Stack } from '@mui/material';
+import { EDIT_MODE } from '@/config/setting';
+import EditBaseAside from "@/page/components/EditBaseAside";
+import CustomerTopToolBar from "@/page/components/EditTopToolBar";
+
+const FormToolbar = () => (
+    <Toolbar sx={{ justifyContent: 'space-between' }}>
+        <SaveButton />
+        <DeleteButton mutationMode="optimistic" />
+    </Toolbar>
+);
+
+const OpenApiAppEdit = () => {
+    return (
+        <Edit
+            resource="openApiApp"
+            redirect="list"
+            mutationMode={EDIT_MODE}
+            actions={<CustomerTopToolBar />}
+            aside={<EditBaseAside />}
+        >
+            <SimpleForm
+                toolbar={<FormToolbar />}
+                defaultValues={{ enable: 1 }}
+            >
+                <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
+                    <Grid item xs={12} md={8}>
+                        <Stack spacing={2}>
+                            <TextInput label="搴旂敤ID" source="id" validate={required()} disabled />
+                            <TextInput label="搴旂敤瀵嗛挜" source="screct" validate={required()} fullWidth />
+                            <TextInput label="搴旂敤鍚嶇О" source="name" fullWidth />
+                            <TextInput label="搴旂敤URL" source="url" fullWidth />
+                            <SelectInput
+                                label="鍚敤鐘舵��"
+                                source="enable"
+                                choices={[
+                                    { id: 1, name: '鍚敤' },
+                                    { id: 0, name: '鏈惎鐢�' },
+                                ]}
+                                fullWidth
+                            />
+                        </Stack>
+                    </Grid>
+                </Grid>
+            </SimpleForm>
+        </Edit>
+    );
+};
+
+export default OpenApiAppEdit;
diff --git a/rsf-admin/src/page/system/openApiApp/OpenApiAppList.jsx b/rsf-admin/src/page/system/openApiApp/OpenApiAppList.jsx
new file mode 100644
index 0000000..7a28161
--- /dev/null
+++ b/rsf-admin/src/page/system/openApiApp/OpenApiAppList.jsx
@@ -0,0 +1,84 @@
+import React, { useState } from "react";
+import {
+    List,
+    DatagridConfigurable,
+    SearchInput,
+    TopToolbar,
+    SelectColumnsButton,
+    EditButton,
+    FilterButton,
+    TextField,
+    TextInput,
+    FunctionField,
+    SelectInput,
+    WrapperField,
+    DeleteButton,
+} from 'react-admin';
+import { Box } from '@mui/material';
+import { styled } from '@mui/material/styles';
+import OpenApiAppCreate from "./OpenApiAppCreate";
+import EmptyData from "@/page/components/EmptyData";
+import MyCreateButton from "@/page/components/MyCreateButton";
+import { OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
+
+const StyledDatagrid = styled(DatagridConfigurable)(() => ({
+    '& .RaDatagrid-row': { cursor: 'auto' },
+    '& .opt': { width: 140 },
+}));
+
+const filters = [
+    <SearchInput source="condition" alwaysOn />,
+    <TextInput source="id" label="搴旂敤ID" />,
+    <TextInput source="name" label="搴旂敤鍚嶇О" />,
+    <SelectInput
+        label="鍚敤鐘舵��"
+        source="enable"
+        choices={[
+            { id: 1, name: '鍚敤' },
+            { id: 0, name: '鏈惎鐢�' },
+        ]}
+    />,
+];
+
+const OpenApiAppList = () => {
+    const [createDialog, setCreateDialog] = useState(false);
+
+    return (
+        <Box display="flex">
+            <List
+                title="搴旂敤绠$悊"
+                empty={<EmptyData onClick={() => setCreateDialog(true)} />}
+                filters={filters}
+                sort={{ field: "id", order: "asc" }}
+                actions={(
+                    <TopToolbar>
+                        <FilterButton />
+                        <MyCreateButton onClick={() => setCreateDialog(true)} />
+                        <SelectColumnsButton preferenceKey="openApiApp" />
+                    </TopToolbar>
+                )}
+                perPage={DEFAULT_PAGE_SIZE}
+            >
+                <StyledDatagrid
+                    preferenceKey="openApiApp"
+                    bulkActionButtons={() => <DeleteButton mutationMode={OPERATE_MODE} />}
+                    rowClick={false}
+                    omit={['tenantId']}
+                >
+                    <TextField source="id" label="搴旂敤ID" />
+                    <TextField source="name" label="搴旂敤鍚嶇О" />
+                    <TextField source="screct" label="搴旂敤瀵嗛挜" />
+                    <TextField source="url" label="搴旂敤URL" />
+                    <FunctionField source="enable" label="鍚敤" render={(r) => (r.enable === 1 ? '鍚敤' : '鏈惎鐢�')} />
+                    <WrapperField cellClassName="opt" label="鎿嶄綔">
+                        <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
+                        <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
+                    </WrapperField>
+                </StyledDatagrid>
+            </List>
+            <OpenApiAppCreate open={createDialog} setOpen={setCreateDialog} />
+        </Box>
+    );
+};
+
+export default OpenApiAppList;
diff --git a/rsf-admin/src/page/system/openApiApp/index.jsx b/rsf-admin/src/page/system/openApiApp/index.jsx
new file mode 100644
index 0000000..c238918
--- /dev/null
+++ b/rsf-admin/src/page/system/openApiApp/index.jsx
@@ -0,0 +1,11 @@
+import React from "react";
+import { ShowGuesser } from "react-admin";
+import OpenApiAppList from "./OpenApiAppList";
+import OpenApiAppEdit from "./OpenApiAppEdit";
+
+export default {
+    list: OpenApiAppList,
+    edit: OpenApiAppEdit,
+    show: ShowGuesser,
+    recordRepresentation: (record) => record?.name || record?.id || '',
+};
diff --git a/rsf-open-api/pom.xml b/rsf-open-api/pom.xml
index 7a6ca0a..2cc6940 100644
--- a/rsf-open-api/pom.xml
+++ b/rsf-open-api/pom.xml
@@ -21,6 +21,39 @@
             <artifactId>rsf-common</artifactId>
             <version>1.0.0</version>
         </dependency>
+        <!-- OpenFeign锛氳浆鍙戣皟鐢ㄧ珛搴� WMS 鎺ュ彛 -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <!-- 鐔旀柇鍣細Feign 璋冪敤澶辫触鏃惰Е鍙� Fallback锛屽湪 Feign 鍐呯粺涓�杩斿洖閿欒鍝嶅簲 -->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
+        </dependency>
+        <!-- JWT锛歍oken 鐢熸垚涓庢牎楠岋紙/erp 璁よ瘉锛� -->
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.11.5</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.11.5</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>0.11.5</version>
+            <scope>runtime</scope>
+        </dependency>
+        <!-- BCrypt锛歡etToken 鏃舵牎楠� appSecret 鐢ㄥ搱甯屾瘮瀵� -->
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-crypto</artifactId>
+        </dependency>
     </dependencies>
     <build>
         <finalName>rsf-open-api</finalName>
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/OpenApi.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/OpenApi.java
index b058da5..d5cdcd1 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/OpenApi.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/OpenApi.java
@@ -4,8 +4,10 @@
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
 import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
+import org.springframework.cloud.openfeign.EnableFeignClients;
 
 @SpringBootApplication(exclude = {SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class })
+@EnableFeignClients(basePackages = "com.vincent.rsf.openApi.feign")
 public class OpenApi {
     public static void main(String[] args) {
         SpringApplication.run(OpenApi.class, args);
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/ApiSecurityConfig.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/ApiSecurityConfig.java
new file mode 100644
index 0000000..b2b6c7d
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/ApiSecurityConfig.java
@@ -0,0 +1,28 @@
+package com.vincent.rsf.openApi.config;
+
+import com.vincent.rsf.openApi.security.filter.AppIdAuthenticationFilter;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.Resource;
+
+/**
+ * API 瀹夊叏閰嶇疆 璁よ瘉杩囨护鍣�
+ */
+@Configuration
+public class ApiSecurityConfig {
+
+    @Resource
+    private AppIdAuthenticationFilter appIdAuthenticationFilter;
+
+    @Bean
+    public FilterRegistrationBean<AppIdAuthenticationFilter> apiAuthenticationFilter() {
+        FilterRegistrationBean<AppIdAuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
+        registrationBean.setFilter(appIdAuthenticationFilter);
+        registrationBean.addUrlPatterns("/api/*", "/erp/*", "/cloudwms/*", "/mes/*", "/agv/*");
+        registrationBean.setName("apiAuthenticationFilter");
+        registrationBean.setOrder(1);
+        return registrationBean;
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/CryptoConfig.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/CryptoConfig.java
new file mode 100644
index 0000000..99b038b
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/CryptoConfig.java
@@ -0,0 +1,15 @@
+package com.vincent.rsf.openApi.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+@Configuration
+public class CryptoConfig {
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/CustomErrorAttributes.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/CustomErrorAttributes.java
new file mode 100644
index 0000000..8bf81c8
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/CustomErrorAttributes.java
@@ -0,0 +1,36 @@
+package com.vincent.rsf.openApi.config;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.web.error.ErrorAttributeOptions;
+import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.WebRequest;
+
+import java.util.Map;
+
+/**
+ * 鑷畾涔夐敊璇睘鎬э細鍝嶅簲浣撲腑涓嶈繑鍥� path锛堜粎鎵撳嵃鏃ュ織锛夛紱淇濊瘉涓氬姟寮傚父淇℃伅鍐欏叆 message 杩斿洖缁欏墠绔��
+ */
+@Component
+public class CustomErrorAttributes extends DefaultErrorAttributes {
+
+    private static final Logger log = LoggerFactory.getLogger(CustomErrorAttributes.class);
+    private static final String KEY_PATH = "path";
+    private static final String KEY_MESSAGE = "message";
+
+    @Override
+    public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
+        Map<String, Object> attrs = super.getErrorAttributes(webRequest, options);
+        Object path = attrs.remove(KEY_PATH);
+        if (path != null) {
+            log.warn("Error path: {}", path);
+        }
+        // 淇濊瘉涓氬姟寮傚父淇℃伅杩斿洖缁欏墠绔紙濡� CoolException锛�
+        Throwable error = getError(webRequest);
+        if (error != null && error.getMessage() != null && !error.getMessage().isEmpty()) {
+            attrs.put(KEY_MESSAGE, error.getMessage());
+        }
+        return attrs;
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/FeignConfig.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/FeignConfig.java
new file mode 100644
index 0000000..d17f973
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/FeignConfig.java
@@ -0,0 +1,18 @@
+package com.vincent.rsf.openApi.config;
+
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Feign 璇锋眰閰嶇疆锛屼笌鍘熸湁 RestTemplate 杞彂淇濇寔涓�鑷寸殑璇锋眰澶�
+ */
+@Configuration
+public class FeignConfig {
+
+    @Bean
+    public RequestInterceptor wmsApiVersionInterceptor() {
+        return (RequestTemplate template) -> template.header("api-version", "v2.0");
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/GlobalExceptionHandler.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/GlobalExceptionHandler.java
new file mode 100644
index 0000000..655e5e0
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/GlobalExceptionHandler.java
@@ -0,0 +1,38 @@
+package com.vincent.rsf.openApi.config;
+
+import com.vincent.rsf.framework.exception.CoolException;
+import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.entity.dto.ResultData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * 鍏ㄥ眬寮傚父澶勭悊锛岃繑鍥炲�肩鍚� 8.2.3锛歝ode銆乵sg銆乨ata锛堝惈 result锛歋UCCESS/FAIL锛夈��
+ */
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
+
+    @ExceptionHandler(CoolException.class)
+    public ResponseEntity<CommonResponse> handleCoolException(CoolException e) {
+        log.warn("涓氬姟寮傚父: {}", e.getMessage());
+        CommonResponse r = CommonResponse.error(e.getMessage());
+        return ResponseEntity.status(HttpStatus.OK).body(r);
+    }
+
+    @ExceptionHandler(Exception.class)
+    public ResponseEntity<CommonResponse> handleException(Exception e) {
+        log.error("绯荤粺寮傚父", e);
+        String msg = e.getMessage() != null ? e.getMessage() : "绯荤粺寮傚父";
+        CommonResponse r = new CommonResponse();
+        r.setCode(500);
+        r.setMsg(msg);
+        r.setData(ResultData.fail());
+        return ResponseEntity.status(HttpStatus.OK).body(r);
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/WebMvcConfig.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/WebMvcConfig.java
index c663a60..7775881 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/WebMvcConfig.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/WebMvcConfig.java
@@ -27,7 +27,7 @@
     public void addInterceptors(InterceptorRegistry registry) {
         registry.addInterceptor(getAsyncHandlerInterceptor())
                 .addPathPatterns("/**")
-                .excludePathPatterns("/swagger-resources/**", "/webjars/**","/erp/**", "/v2/**","/v3/**","/doc.html/**", "/swagger-ui.html/**");
+                .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/cloudwms/**", "/erp/**", "/v2/**", "/v3/**", "/doc.html/**", "/swagger-ui.html/**");
     }
 
 
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/AuthController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/AuthController.java
new file mode 100644
index 0000000..b6d6e06
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/AuthController.java
@@ -0,0 +1,37 @@
+package com.vincent.rsf.openApi.controller;
+
+import com.vincent.rsf.framework.common.Cools;
+import com.vincent.rsf.openApi.entity.constant.Constants;
+import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.entity.params.GetTokenParam;
+import com.vincent.rsf.openApi.security.service.AppAuthService;
+import com.vincent.rsf.openApi.security.utils.TokenUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@Api(tags = "搴旂敤璁よ瘉绠$悊")
+public class AuthController {
+
+    @Autowired
+    private AppAuthService appAuthService;
+
+    @ApiOperation("鑾峰彇App璁よ瘉Token")
+    @PostMapping("/getToken")
+    public CommonResponse getToken(@RequestBody GetTokenParam param) {
+        String appId = param != null ? param.getAppId() : null;
+        String appSecret = param != null ? param.getAppSecret() : null;
+        if (Cools.isEmpty(appId, appSecret)) {
+            return CommonResponse.error("AppId鍜孉ppSecret涓嶈兘涓虹┖");
+        }
+        if (!appAuthService.validateApp(appId, appSecret)) {
+            return CommonResponse.error("AppId鎴朅ppSecret鏃犳晥");
+        }
+        String token = Constants.TOKEN_PREFIX + TokenUtils.generateToken(appId, appSecret);
+        return CommonResponse.ok().setMsg("鑾峰彇Token鎴愬姛").setData(token);
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsErpController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsErpController.java
index ffdf7de..5194534 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsErpController.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsErpController.java
@@ -17,7 +17,7 @@
 import java.util.Objects;
 
 @RestController
-@RequestMapping("/erp")
+@RequestMapping({"/erp","/cloudwms"})
 @Api("ERP鎺ュ彛瀵规帴")
 public class WmsErpController {
 
@@ -41,50 +41,37 @@
 
 
     /**
-     * 璁㈠崟淇敼
-     * @param params
-     * @return
+     * 鍏�/鍑哄簱閫氱煡鍗曚笅鍙戯紙瀵规帴鍗忚 8.3锛夛細鏂板/淇敼/鍙栨秷銆俹perateType 1鏂板 2淇敼 3鍙栨秷锛屼笉浼犳垨 1/2 鏃舵湁鍒欐洿鏂般�佹棤鍒欐柊澧烇紱3 鏃舵寜鍙栨秷銆�
+     * @param params 鍗曟嵁鍙傛暟锛堝惈 orderNo銆乷rderItems銆乷perateType 绛夛紝瑙佸鎺ユ枃妗o級
+     * @return 鎿嶄綔缁撴灉
      */
-    @ApiOperation("鍗曟嵁淇敼")
-    @PostMapping("/order/upadte")
-    public CommonResponse modifyOrderDtel(@RequestBody ErpOpParams params) {
-        if (Objects.isNull(params)) {
-            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
-        }
-        return wmsErpService.updateOrderDetl(params);
-    }
-
-
-    /**
-     * 璁㈠崟鏂板
-     * @param params
-     * @return
-     */
-    @ApiOperation("鏂板鍗曟嵁")
+    @ApiOperation("鏂板鍗曟嵁锛堝吋瀹逛慨鏀广�佸彇娑堬級")
     @PostMapping("/order/add")
     public CommonResponse orderAdd(@RequestBody ErpOpParams params) {
         if (Objects.isNull(params)) {
             throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
         }
-        return wmsErpService.updateOrderDetl(params);
+        return wmsErpService.addOrUpdateOrder(params);
     }
 
-
     /**
-     * 鍒犻櫎璁㈠崟
-     * @param params
-     * @return
+     * 鍙栨秷璁㈠崟/鍙栨秷鍗曟嵁銆備笌 /order/add 浼� operateType=3 鐨勫彇娑堥�昏緫涓�鑷达紝鍧囪浆鍙戠珛搴� sync/orders/delete銆�
+     * @param params 鑷冲皯鍖呭惈 orderNo锛屽彲閫� orderItems
      */
-    @ApiOperation("鍒犻櫎璁㈠崟")
-    @PostMapping("/order/del")
-    public CommonResponse orderDel(@RequestBody ErpOpParams params) {
+    @ApiOperation("鍙栨秷璁㈠崟")
+    @PostMapping({"/order/cancel", "/order/del"})
+    public CommonResponse orderCancel(@RequestBody ErpOpParams params) {
         if (Objects.isNull(params)) {
             throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
         }
-        return wmsErpService.orderDel(params);
+        return wmsErpService.orderCancel(params);
     }
 
-    @ApiOperation("鍩虹鐗╂枡淇℃伅鏇存柊")
+    /**
+     * 鐗╂枡鍩虹淇℃伅鍚屾锛堝鎺ュ崗璁� 8.2锛�
+     * 鏀寔 operateType锛�1鏂板 2淇敼 3绂佺敤 4鍚敤锛涜姹備綋鏀寔鍗忚瀛楁 matNr/makTx 涓� matnr/maktx銆�
+     */
+    @ApiOperation("鍩虹鐗╂枡淇℃伅鍚屾锛堟敮鎸� operateType銆乵atNr/makTx锛�")
     @PostMapping("/mat/sync/auth/v1")
     public CommonResponse syncMatnrs(@RequestBody ErpMatnrParms parms) {
         if (Objects.isNull(parms)) {
@@ -94,19 +81,37 @@
     }
 
 
-    @ApiOperation("璁㈠崟淇℃伅涓婃姤")
-    @PostMapping("/report/order")
-    public CommonResponse reportOrders(@RequestBody ReportParams params) {
-        if (Objects.isNull(params)) {
-            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
-        }
-        return wmsErpService.reportOrders(params);
+//    @ApiOperation("璁㈠崟淇℃伅涓婃姤")
+//    @PostMapping("/report/order")
+//    public CommonResponse reportOrders(@RequestBody ReportParams params) {
+//        if (Objects.isNull(params)) {
+//            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+//        }
+//        return wmsErpService.reportOrders(params);
+//    }
+//
+//    @ApiOperation("鐩樼偣宸紓淇敼")
+//    @PostMapping("/check/locitem/update")
+//    public CommonResponse reportCheck(@RequestBody ReportParams params) {
+//        return wmsErpService.reportCheck(params);
+//    }
+
+    @ApiOperation("搴撲綅淇℃伅鏌ヨ")
+    @PostMapping("/query/locs/detls")
+    public CommonResponse queryLocsDetls(@RequestBody Map<String, Object> params) {
+        return wmsErpService.queryLocsDetls(params);
     }
 
-    @ApiOperation("鐩樼偣宸紓淇敼")
-    @PostMapping("/check/locitem/update")
-    public CommonResponse reportCheck(@RequestBody ReportParams params) {
-        return wmsErpService.reportCheck(params);
+    @ApiOperation("搴撳瓨鏄庣粏鏌ヨ锛堝鎺ュ崗璁�8.4锛�")
+    @PostMapping("/inventory/details")
+    public CommonResponse inventoryDetails(@RequestBody(required = false) Map<String, Object> params) {
+        return wmsErpService.inventoryDetails(params);
+    }
+
+    @ApiOperation("搴撳瓨姹囨�绘煡璇紙瀵规帴鍗忚8.5锛�")
+    @PostMapping("/inventory/summary")
+    public CommonResponse inventorySummary(@RequestBody(required = false) Map<String, Object> params) {
+        return wmsErpService.inventorySummary(params);
     }
 
 }
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/AppController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/AppController.java
new file mode 100644
index 0000000..41be371
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/AppController.java
@@ -0,0 +1,79 @@
+package com.vincent.rsf.openApi.controller.platform;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.vincent.rsf.openApi.entity.app.App;
+import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.service.AppService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/app")
+@Api(tags = "搴旂敤绠$悊")
+public class AppController {
+
+    @Autowired
+    private AppService appService;
+
+    @ApiOperation("鍒嗛〉鏌ヨ搴旂敤鍒楄〃")
+    @GetMapping("/page")
+    public CommonResponse page(@RequestParam(defaultValue = "1") Integer current,
+                               @RequestParam(defaultValue = "10") Integer size) {
+        Page<App> page = appService.page(new Page<>(current, size));
+        return CommonResponse.ok().setData(page);
+    }
+
+    @ApiOperation("鏌ヨ鎵�鏈夊簲鐢�")
+    @GetMapping("/list")
+    public CommonResponse list() {
+        List<App> list = appService.list();
+        return CommonResponse.ok().setData(list);
+    }
+
+    @ApiOperation("鏍规嵁ID鏌ヨ搴旂敤")
+    @GetMapping("/{id}")
+    public CommonResponse getById(@PathVariable String id) {
+        App app = appService.getById(id);
+        return CommonResponse.ok().setData(app);
+    }
+
+    @ApiOperation("鏂板搴旂敤")
+    @PostMapping
+    public CommonResponse save(@RequestBody App app) {
+        if (appService.save(app)) {
+            return CommonResponse.ok().setMsg("鏂板鎴愬姛");
+        }
+        return CommonResponse.error("鏂板澶辫触");
+    }
+
+    @ApiOperation("鏇存柊搴旂敤")
+    @PutMapping
+    public CommonResponse update(@RequestBody App app) {
+        if (appService.updateById(app)) {
+            return CommonResponse.ok().setMsg("鏇存柊鎴愬姛");
+        }
+        return CommonResponse.error("鏇存柊澶辫触");
+    }
+
+    @ApiOperation("鍒犻櫎搴旂敤")
+    @DeleteMapping("/{id}")
+    public CommonResponse delete(@PathVariable String id) {
+        if (appService.removeById(id)) {
+            return CommonResponse.ok().setMsg("鍒犻櫎鎴愬姛");
+        }
+        return CommonResponse.error("鍒犻櫎澶辫触");
+    }
+
+    @ApiOperation("鎵归噺鍒犻櫎搴旂敤")
+    @DeleteMapping("/batch")
+    public CommonResponse deleteBatch(@RequestBody List<String> ids) {
+        if (appService.removeByIds(ids)) {
+            return CommonResponse.ok().setMsg("鎵归噺鍒犻櫎鎴愬姛");
+        }
+        return CommonResponse.error("鎵归噺鍒犻櫎澶辫触");
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/App.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/App.java
new file mode 100644
index 0000000..36ca515
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/App.java
@@ -0,0 +1,34 @@
+package com.vincent.rsf.openApi.entity.app;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@TableName("open_api_app")
+public class App implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("appId")
+    @TableId(value = "id")
+    private String id;
+
+    @ApiModelProperty("appSecret")
+    private String screct;
+
+    @ApiModelProperty("appName")
+    private String name;
+
+    @ApiModelProperty("appUrl")
+    private String url;
+
+    @ApiModelProperty("鏄惁鍚敤 0鏈惎鐢� 1鍚敤")
+    private Integer enable;
+
+    @ApiModelProperty("绉熸埛id")
+    private Long tenantId;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/Constants.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/Constants.java
index 0fe215b..32926c9 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/Constants.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/Constants.java
@@ -91,6 +91,21 @@
     public static final String TOKEN_TYPE = "Bearer";
 
     /**
+     * Authorization 澶翠腑鐨� Token 鍓嶇紑
+     */
+    public static final String TOKEN_PREFIX = "Bearer ";
+
+    /**
+     * HTTP 澶� Authorization 鐨� key锛堝皬鍐欙紝璇锋眰澶翠笉鍖哄垎澶у皬鍐欙級
+     */
+    public static final String HEADER_AUTHORIZATION = "Authorization";
+
+    /**
+     * 璇锋眰灞炴�э細璁よ瘉閫氳繃鍚庡啓鍏ョ殑 appId
+     */
+    public static final String REQUEST_ATTR_APP_ID = "appId";
+
+    /**
      * 搴撳瓨鍑哄簱
      */
     public static final String TASK_TYPE_OUT_STOCK = "outStock";
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/WmsConstant.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/WmsConstant.java
index d903d43..1fd4f59 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/WmsConstant.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/WmsConstant.java
@@ -13,7 +13,7 @@
     //璁㈠崟淇℃伅淇敼/娣诲姞
     public static String MODIFY_ORDER_DETLS = "/rsf-server/order/sync/orders/update";
 
-    //鍒犻櫎鍗曟嵁淇℃伅
+    // 鍙栨秷鍗曟嵁锛堢珛搴撲晶 /order/sync/orders/delete 瀹炵幇鍙栨秷閫昏緫锛�
     public static String ORDER_DEL = "/rsf-server/order/sync/orders/delete";
 
     //鑾峰彇鍑哄叆搴撴祦姘�
@@ -25,6 +25,48 @@
     //璁㈠崟瀹屾垚鍥炲啓
     public static String REPORT_ORDER_CALLBACK = "/C3Api?SysCode=WMS";
 
+    //搴撲綅淇℃伅鏌ヨ锛堜簯浠撳彧璋� open-api锛岀敱 open-api 杞彂绔嬪簱锛�
+    public static String QUERY_LOCS_DETLS = "/rsf-server/erp/query/locs/detls";
 
+    //璋冩嫧鍗曚俊鎭煡璇�
+    public static String QUERY_TRANSFER = "/rsf-server/erp/query/transfer";
+
+    //鐗╂枡鍒嗙被鍒楄〃鏌ヨ
+    public static String QUERY_MATNR_GROUP = "/rsf-server/erp/query/matnr/group";
+
+    // ========== 鍗曟嵁鍚屾锛堢珛搴撴彁渚涳紝浜戜粨璋� open-api 杞彂锛� ==========
+    /** 閲囪喘鍗曞悓姝� */
+    public static String ORDER_SYNC_PURCHASE = "/rsf-server/order/sync/purchase";
+    /** 鍑哄簱閫氱煡鍗�(DO鍗�)鍚屾 */
+    public static String ORDER_SYNC_DELIVERY = "/rsf-server/order/sync/delivery";
+    /** 鏀惰揣閫氱煡鍗�/鍗曟嵁鍚屾 */
+    public static String ORDER_SYNC_CHECKS = "/rsf-server/order/sync/checks";
+    /** 璋冩嫧鍗曞悓姝� */
+    public static String ORDER_SYNC_TRANSFERS = "/rsf-server/order/sync/transfers";
+    /** 搴撳瓨璋冩暣鍗曞悓姝� */
+    public static String ORDER_SYNC_REVISES = "/rsf-server/order/sync/revises";
+    /** 璐ㄦ鍗曚笂鎶� */
+    public static String ORDER_SYNC_QLY_INSPECT = "/rsf-server/order/sync/qlyInspect";
+    /** 鐩樼偣宸紓鍗曞悓姝� */
+    public static String ORDER_SYNC_CHECK_RESULT = "/rsf-server/order/sync/check/result";
+
+    // ========== 鍩虹鏁版嵁鍚屾锛堢珛搴撴彁渚涳級 ==========
+    /** 鍩虹鐗╂枡淇℃伅鍚屾锛堟壒閲忥級 */
+    public static String BASE_SYNC_MATNRS = "/rsf-server/base/sync/base/matnrs";
+    /** 搴撲綅淇℃伅鍚屾 */
+    public static String BASE_SYNC_LOCS = "/rsf-server/base/sync/locs";
+    /** 鐗╂枡鍒嗙粍淇℃伅鍚屾 */
+    public static String BASE_SYNC_MAT_GROUPS = "/rsf-server/base/sync/matGroups";
+    /** 搴撳尯鏁版嵁鍚屾 */
+    public static String BASE_SYNC_WAREHOUSE_AREAS = "/rsf-server/base/sync/warehouse/areas";
+    /** 浠撳簱鏁版嵁鍚屾 */
+    public static String BASE_SYNC_WAREHOUSE = "/rsf-server/base/sync/warehouse";
+    /** 浼佷笟淇℃伅鍚屾 */
+    public static String BASE_SYNC_COMPANIES = "/rsf-server/base/sync/companies";
+
+    /** 瀵规帴鍗忚 8.4 搴撳瓨鏄庣粏鏌ヨ */
+    public static String INVENTORY_DETAILS = "/rsf-server/erp/inventory/details";
+    /** 瀵规帴鍗忚 8.5 搴撳瓨姹囨�绘煡璇� */
+    public static String INVENTORY_SUMMARY = "/rsf-server/erp/inventory/summary";
 
 }
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/dto/CommonResponse.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/dto/CommonResponse.java
index 0e89156..cf25342 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/dto/CommonResponse.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/dto/CommonResponse.java
@@ -25,4 +25,24 @@
     @ApiModelProperty("鍝嶅簲缁撴灉")
     private Object data;
 
+    public static CommonResponse ok() {
+        CommonResponse r = new CommonResponse();
+        r.setCode(200);
+        r.setMsg("鎿嶄綔鎴愬姛");
+        return r;
+    }
+
+    /** 8.2.3 鏍煎紡锛氭垚鍔熶笖 data 浠呭惈 result */
+    public static CommonResponse okWithResult() {
+        return ok().setData(ResultData.success());
+    }
+
+    public static CommonResponse error(String msg) {
+        CommonResponse r = new CommonResponse();
+        r.setCode(500);
+        r.setMsg(msg != null ? msg : "鎿嶄綔澶辫触");
+        r.setData(ResultData.fail());
+        return r;
+    }
+
 }
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/dto/ResultData.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/dto/ResultData.java
new file mode 100644
index 0000000..581953d
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/dto/ResultData.java
@@ -0,0 +1,31 @@
+package com.vincent.rsf.openApi.entity.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 8.2.3 杩斿洖鍊� data 鏁版嵁妯″瀷锛氭墽琛岀粨鏋� SUCCESS/FAIL
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ApiModel(value = "ResultData", description = "鎵ц缁撴灉")
+public class ResultData {
+
+    @ApiModelProperty(value = "鎵ц缁撴灉锛歋UCCESS 鎴愬姛锛汧AIL 澶辫触", example = "SUCCESS")
+    private String result;
+
+    public static final String SUCCESS = "SUCCESS";
+    public static final String FAIL = "FAIL";
+
+    public static ResultData success() {
+        return new ResultData(SUCCESS);
+    }
+
+    public static ResultData fail() {
+        return new ResultData(FAIL);
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/ErpMatnrParms.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/ErpMatnrParms.java
index 0c7af17..41f931d 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/ErpMatnrParms.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/ErpMatnrParms.java
@@ -1,20 +1,29 @@
 package com.vincent.rsf.openApi.entity.params;
 
-
+import com.fasterxml.jackson.annotation.JsonAlias;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.experimental.Accessors;
 
+/**
+ * 鐗╂枡鍩虹淇℃伅鍚屾鍏ュ弬锛堝鎺ュ崗璁� 8.2锛�
+ * 鍗忚瀛楁 matNr/makTx 涓� matnr/maktx 鍧囧彲鎺ユ敹銆�
+ */
 @Data
 @Accessors(chain = true)
 public class ErpMatnrParms {
 
-    @ApiModelProperty("鐗╂枡鍚嶇О")
-    private String maktx;
+    @ApiModelProperty(value = "鎿嶄綔绫诲瀷锛�1鏂板 2淇敼 3绂佺敤 4鍚敤", example = "1")
+    private Integer operateType;
 
-    @ApiModelProperty("鐗╂枡缂栫爜*")
+    @ApiModelProperty(value = "鐗╂枡缂栫爜*锛堝崗璁瓧娈� matNr 鍚屼箟锛�")
+    @JsonAlias("matNr")
     private String matnr;
 
+    @ApiModelProperty(value = "鐗╂枡鍚嶇О锛堝崗璁瓧娈� makTx 鍚屼箟锛�")
+    @JsonAlias("makTx")
+    private String maktx;
+
     @ApiModelProperty("鐗╂枡鍒嗙粍")
     private String groupName;
 
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/ErpOpParams.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/ErpOpParams.java
index 45c2a34..4012b5e 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/ErpOpParams.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/ErpOpParams.java
@@ -1,6 +1,6 @@
 package com.vincent.rsf.openApi.entity.params;
 
-
+import com.fasterxml.jackson.annotation.JsonAlias;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -8,30 +8,44 @@
 
 import java.util.List;
 
+/**
+ * 鍏�/鍑哄簱閫氱煡鍗曚笅鍙戯紙瀵规帴鍗忚 8.3锛夎姹傚弬鏁般��
+ * 浠� 8.3 鏂囨。瀛楁涓轰富锛屽叾浠栨棫瀛楁灏介噺涓嶇敤銆�
+ */
 @Data
 @Accessors(chain = true)
-@ApiModel(value = "ErpOpParams", description = "ERP鎿嶄綔璇锋眰鍙傛暟")
+@ApiModel(value = "ErpOpParams", description = "8.3 鍏�/鍑哄簱閫氱煡鍗曚笅鍙戝弬鏁�")
 public class ErpOpParams {
 
-    /**
-     * 鍗曞彿
-     */
-    @ApiModelProperty("璁㈠崟鍙�")
+    @ApiModelProperty(value = "璁㈠崟缂栫爜", required = true)
     private String orderNo;
 
-    @ApiModelProperty("涓氬姟绫诲瀷")
+    @ApiModelProperty(value = "鍗曟嵁鍐呯爜锛屽敮涓�鏍囪瘑锛岃嫢娌℃湁鍙ˉ鍏呰鍗曠紪鐮�", required = true)
+    private String orderInternalCode;
+
+    @ApiModelProperty(value = "璁㈠崟绫诲瀷锛�1 鍑哄簱鍗曪紱2 鍏ュ簱鍗曪紱3 璋冩嫧鍗�", required = true)
+    private Integer orderType;
+
+    @ApiModelProperty(value = "涓氬姟绫诲瀷锛屽锛氶噰璐叆搴撳崟銆侀攢鍞嚭搴撳崟銆佽皟鎷ㄧ敵璇峰崟绛�", required = true)
     private String wkType;
 
-    @ApiModelProperty("璁㈠崟绫诲瀷")
-    private String type;
+    @ApiModelProperty(value = "涓氬姟鏃ユ湡锛屾椂闂存埑绮剧‘鍒扮", required = true)
+    private Long businessTime;
 
-    @ApiModelProperty("鏁伴噺")
-    private Double anfme;
+    @ApiModelProperty(value = "鍒涘缓鏃ユ湡锛屾椂闂存埑绮剧‘鍒扮", required = true)
+    private Long createTime;
 
-    @ApiModelProperty("鎵ц鐘舵��")
-    private Short exceStatus;
-
-    @ApiModelProperty("璁㈠崟鏄庣粏")
+    @ApiModelProperty(value = "璁㈠崟鏄庣粏", required = true)
     private List<WmsOrderItemParam> orderItems;
 
+    @ApiModelProperty("鍏�/鍑哄簱鎺ラ┏绔欑偣锛岄渶瑕佸垯琛ュ厖")
+    private String stationId;
+
+    @ApiModelProperty("鎿嶄綔绫诲瀷锛�1 鏂板锛堥粯璁わ級锛�2 淇敼锛�3 鍙栨秷")
+    private Integer operateType;
+
+    /** 鍏煎鏃у瓧娈碉細涓� orderInternalCode 浜岄�変竴 */
+    @JsonAlias("orderId")
+    @ApiModelProperty(hidden = true)
+    private Long orderId;
 }
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/GetTokenParam.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/GetTokenParam.java
new file mode 100644
index 0000000..d7ad7dc
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/GetTokenParam.java
@@ -0,0 +1,21 @@
+package com.vincent.rsf.openApi.entity.params;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 瀵规帴鍗忚 8.1 鑾峰彇Token 璇锋眰鍙傛暟
+ */
+@Data
+@Accessors(chain = true)
+@ApiModel(value = "GetTokenParam", description = "鑾峰彇Token")
+public class GetTokenParam {
+
+    @ApiModelProperty(value = "搴旂敤缂栫爜锛岀珛搴揥MS绾夸笅鍒嗛厤", required = true)
+    private String appId;
+
+    @ApiModelProperty(value = "搴旂敤绉橀挜锛岀珛搴揥MS绾夸笅鍒嗛厤", required = true)
+    private String appSecret;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/WmsOrderItemParam.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/WmsOrderItemParam.java
index e604554..29b304b 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/WmsOrderItemParam.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/params/WmsOrderItemParam.java
@@ -1,36 +1,33 @@
 package com.vincent.rsf.openApi.entity.params;
 
-
+import com.fasterxml.jackson.annotation.JsonAlias;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 import lombok.experimental.Accessors;
 
+/**
+ * 8.3 璁㈠崟鏄庣粏锛屼互鏂囨。瀛楁涓轰富銆�
+ */
 @Data
 @Accessors(chain = true)
-@ApiModel(value = "WmsOrderItemParam", description = "璁㈠崟鏄庣粏鍙傛暟")
+@ApiModel(value = "WmsOrderItemParam", description = "8.3 璁㈠崟鏄庣粏")
 public class WmsOrderItemParam {
 
-    @ApiModelProperty("鐗╂枡缂栫爜")
-    private String matnr;
+    @ApiModelProperty(value = "琛屽唴鐮侊紝鍞竴鏍囪瘑", required = true)
+    private String lineId;
+
+    @ApiModelProperty(value = "鐗╂枡缂栫爜", required = true)
+    @JsonAlias("matnr")
+    private String matNr;
 
     @ApiModelProperty("鐗╂枡鍚嶇О")
-    private String maktx;
+    @JsonAlias("maktx")
+    private String makTx;
 
-    @ApiModelProperty("瀹㈠崟鍙�")
-    private String platOrderCode;
-
-    @ApiModelProperty("骞冲彴鏍囪瘑锛堣鍙凤級")
-    private String platItemId;
-
-    @ApiModelProperty("宸ュ崟鍙�")
-    private String platWorkCode;
-
-    @ApiModelProperty("椤圭洰鍙�")
-    private String projectCode;
-
-    @ApiModelProperty("鐜伴噾绁ㄥ彿")
-    private String crushNo;
+    @ApiModelProperty(value = "鏁伴噺锛岃嫢鏈夊皬鏁伴粯璁や繚鐣�2浣�", required = true)
+    @JsonAlias("qty")
+    private String anfme;
 
     @ApiModelProperty("瑙勬牸")
     private String spec;
@@ -38,19 +35,21 @@
     @ApiModelProperty("鍨嬪彿")
     private String model;
 
-    @ApiModelProperty("鏁伴噺")
-    private Double anfme;
-
-    @ApiModelProperty("搴撳瓨鍗曚綅")
+    @ApiModelProperty("鍗曚綅")
     private String unit;
 
-    @ApiModelProperty("鎵规")
+    @ApiModelProperty("鎵规鍙�")
     private String batch;
 
-    @ApiModelProperty("宸叉敹鏁伴噺")
-    private Double qty;
+    @ApiModelProperty("鎵樼洏鐮侊紝鍑哄簱鍗曟椂鍙寚瀹氳鎵樼洏鍑哄簱")
+    private String palletId;
 
-    @ApiModelProperty("鏉″舰鐮�")
-    private String barcode;
+    @ApiModelProperty("璁″垝璺熻釜鍙�")
+    private String planNo;
 
+    @ApiModelProperty("寤鸿鍏ュ簱浠撳簱")
+    private String targetWareHouseId;
+
+    @ApiModelProperty("寤鸿鍑哄簱浠撳簱")
+    private String sourceWareHouseId;
 }
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/erp/ErpReportFeignClient.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/erp/ErpReportFeignClient.java
new file mode 100644
index 0000000..cac8bbb
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/erp/ErpReportFeignClient.java
@@ -0,0 +1,24 @@
+package com.vincent.rsf.openApi.feign.erp;
+
+import com.vincent.rsf.openApi.entity.params.ReportParams;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import java.util.Map;
+
+/**
+ * 璋冪敤 ERP/浜戜粨 涓婃姤鎺ュ彛鐨� OpenFeign 瀹㈡埛绔紙璁㈠崟瀹屾垚鍥炲啓銆佺洏鐐瑰樊寮傚崟淇敼锛夈��
+ * 鍙�夋柟寮忥紝褰撳墠浠嶄娇鐢� HttpEntity(RestTemplate)锛涘惎鐢ㄦ椂鍦� WmsErpServiceImpl 涓垏鎹€��
+ */
+@FeignClient(
+    name = "erp-report",
+    url = "${platform.erp.host:http://127.0.0.1}:${platform.erp.port:8080}"
+)
+public interface ErpReportFeignClient {
+
+    /** 璁㈠崟瀹屾垚鍥炲啓 / 鐩樼偣宸紓鍗曚慨鏀癸細鍚屼竴 ERP 鍥炶皟鍦板潃 */
+    @PostMapping(value = "/C3Api?SysCode=WMS", consumes = MediaType.APPLICATION_JSON_VALUE)
+    Map<String, Object> report(@RequestBody ReportParams params);
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/WmsServerFeignClient.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/WmsServerFeignClient.java
new file mode 100644
index 0000000..bcc327e
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/WmsServerFeignClient.java
@@ -0,0 +1,55 @@
+package com.vincent.rsf.openApi.feign.wms;
+
+import com.vincent.rsf.openApi.entity.params.ErpMatnrParms;
+import com.vincent.rsf.openApi.entity.params.ErpOpParams;
+import com.vincent.rsf.openApi.feign.wms.fallback.WmsServerFeignClientFallbackFactory;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * WMS Server锛堢珛搴擄級Feign 瀹㈡埛绔�
+ * 鐢ㄤ簬 open-api 杞彂璋冪敤 rsf-server 鐨勬帴鍙o紝浜戜粨鍙皟 open-api锛岀敱鏈� Feign 杞彂鑷崇珛搴撱��
+ *
+ * url 浠� application 涓鍙� platform.wms.host 涓� platform.wms.port锛�
+ * 鍚屾満閮ㄧ讲鏃跺彲閰嶄负鏈湴鍦板潃锛屽垎寮�閮ㄧ讲鏃堕厤 server 瀹為檯鍦板潃銆�
+ */
+@FeignClient(
+    name = "wms-server",
+    url = "${platform.wms.host:http://127.0.0.1}:${platform.wms.port:8086}",
+    path = "",
+    fallbackFactory = WmsServerFeignClientFallbackFactory.class
+)
+public interface WmsServerFeignClient {
+
+    /** 璁㈠崟淇℃伅鍙婃槑缁嗘煡璇� */
+    @PostMapping("/rsf-server/erp/query/order")
+    Map<String, Object> queryOrderAndDetls(@RequestBody ErpOpParams params);
+
+    /** 璁㈠崟淇℃伅淇敼/娣诲姞 */
+    @PostMapping("/rsf-server/order/sync/orders/update")
+    Map<String, Object> updateOrderDetls(@RequestBody List<Map<String, Object>> body);
+
+    /** 鍒犻櫎/鍙栨秷鍗曟嵁锛堟湇鍔$鎺ユ敹 List&lt;SyncOrderParams&gt;锛� */
+    @PostMapping("/rsf-server/order/sync/orders/delete")
+    Map<String, Object> orderDel(@RequestBody List<Map<String, Object>> body);
+
+    /** 鐗╂枡淇℃伅鍚屾 */
+    @PostMapping("/rsf-server/base/mat/sync/auth/v1")
+    Map<String, Object> syncMatnrs(@RequestBody ErpMatnrParms params);
+
+    /** 搴撲綅淇℃伅鏌ヨ */
+    @PostMapping("/rsf-server/erp/query/locs/detls")
+    Map<String, Object> queryLocsDetls(@RequestBody Map<String, Object> params);
+
+    /** 搴撳瓨鏄庣粏鏌ヨ锛堝鎺ュ崗璁� 8.4锛� */
+    @PostMapping("/rsf-server/erp/inventory/details")
+    Map<String, Object> inventoryDetails(@RequestBody Map<String, Object> params);
+
+    /** 搴撳瓨姹囨�绘煡璇紙瀵规帴鍗忚 8.5锛� */
+    @PostMapping("/rsf-server/erp/inventory/summary")
+    Map<String, Object> inventorySummary(@RequestBody Map<String, Object> params);
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/fallback/WmsServerFeignClientFallback.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/fallback/WmsServerFeignClientFallback.java
new file mode 100644
index 0000000..1d354f6
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/fallback/WmsServerFeignClientFallback.java
@@ -0,0 +1,130 @@
+package com.vincent.rsf.openApi.feign.wms.fallback;
+
+import com.vincent.rsf.framework.common.R;
+import com.vincent.rsf.openApi.entity.params.ErpMatnrParms;
+import com.vincent.rsf.openApi.entity.params.ErpOpParams;
+import com.vincent.rsf.openApi.feign.wms.WmsServerFeignClient;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * WMS Server Feign 瀹㈡埛绔檷绾у鐞嗭紝鍦� Feign 鍐呯粺涓�杩斿洖閿欒鍝嶅簲锛堜笉鎶涘紓甯革級銆�
+ * 鐢� WmsServerFeignClientFallbackFactory 鍒涘缓骞朵紶鍏ュ紓甯� cause銆�
+ */
+@Slf4j
+@Component
+public class WmsServerFeignClientFallback implements WmsServerFeignClient {
+
+    /** 瑙﹀彂闄嶇骇鏃剁殑寮傚父锛岀敱 FallbackFactory 浼犲叆锛涙棤 cause 鏃朵负 null */
+    private final Throwable cause;
+
+    public WmsServerFeignClientFallback() {
+        this.cause = null;
+    }
+
+    public WmsServerFeignClientFallback(Throwable cause) {
+        this.cause = cause;
+    }
+
+    private Map<String, Object> errorResponse() {
+        return R.error(filterErrorMessage(cause));
+    }
+
+    /**
+     * 杩囨护閿欒娑堟伅涓殑URL锛屽彧淇濈暀閿欒绫诲瀷
+     * @param throwable 寮傚父瀵硅薄锛堝彲閫夛級
+     * @return 杩囨护鍚庣殑瀹屾暣閿欒娑堟伅锛堝寘鍚�"鏌ヨ澶辫触锛�"鍓嶇紑锛�
+     */
+    public static String filterErrorMessage(Throwable throwable) {
+        if (throwable == null) {
+            return "鏌ヨ澶辫触锛氭湇鍔¤皟鐢ㄥけ璐ワ紝璇风◢鍚庨噸璇�";
+        }
+        return filterErrorMessage(throwable.getMessage());
+    }
+
+    /**
+     * 杩囨护閿欒娑堟伅涓殑URL锛屽彧淇濈暀閿欒绫诲瀷
+     * @param errorMessage 閿欒娑堟伅瀛楃涓诧紙鍙�夛級
+     * @return 杩囨护鍚庣殑瀹屾暣閿欒娑堟伅锛堝寘鍚�"鏌ヨ澶辫触锛�"鍓嶇紑锛�
+     */
+    public static String filterErrorMessage(String errorMessage) {
+        if (errorMessage == null || errorMessage.isEmpty()) {
+            return "鏌ヨ澶辫触锛氭湭鐭ラ敊璇�";
+        }
+
+        String filteredMessage = errorMessage;
+
+        // 濡傛灉鍖呭惈"executing"锛岃鏄庢槸HTTP璇锋眰閿欒锛屽幓鎺塙RL閮ㄥ垎
+        if (filteredMessage.contains("executing")) {
+            int executingIndex = filteredMessage.indexOf("executing");
+            if (executingIndex > 0) {
+                // 鎻愬彇"executing"涔嬪墠鐨勯儴鍒嗭紙濡�"Read timed out"锛�
+                filteredMessage = filteredMessage.substring(0, executingIndex).trim();
+            } else {
+                // 濡傛灉"executing"鍦ㄥ紑澶达紝浣跨敤榛樿閿欒娑堟伅
+                filteredMessage = "璇锋眰瓒呮椂";
+            }
+        }
+        // 濡傛灉鍖呭惈"http://"鎴�"https://"锛屼篃灏濊瘯鍘绘帀URL閮ㄥ垎
+        else if (filteredMessage.contains("http://") || filteredMessage.contains("https://")) {
+            // 浣跨敤姝e垯琛ㄨ揪寮忓幓鎺塙RL
+            filteredMessage = filteredMessage.replaceAll("https?://[^\\s]+", "").trim();
+            if (filteredMessage.isEmpty()) {
+                filteredMessage = "璇锋眰澶辫触";
+            }
+        }
+
+        // 濡傛灉杩囨护鍚庣殑娑堟伅涓虹┖锛屼娇鐢ㄩ粯璁ら敊璇秷鎭�
+        if (filteredMessage.isEmpty()) {
+            filteredMessage = "鏈煡閿欒";
+        }
+
+        // 杩斿洖鍖呭惈"鏌ヨ澶辫触锛�"鍓嶇紑鐨勫畬鏁撮敊璇秷鎭�
+        return "鏌ヨ澶辫触锛�" + filteredMessage;
+    }
+
+    @Override
+    public Map<String, Object> queryOrderAndDetls(ErpOpParams params) {
+        log.error("璋冪敤绔嬪簱WMS Server璁㈠崟淇℃伅鏌ヨ鎺ュ彛澶辫触锛岃Е鍙戦檷绾�", cause);
+        return errorResponse();
+    }
+
+    @Override
+    public Map<String, Object> updateOrderDetls(List<Map<String, Object>> body) {
+        log.error("璋冪敤绔嬪簱WMS Server璁㈠崟淇敼鎺ュ彛澶辫触锛岃Е鍙戦檷绾�", cause);
+        return errorResponse();
+    }
+
+    @Override
+    public Map<String, Object> orderDel(List<Map<String, Object>> body) {
+        log.error("璋冪敤绔嬪簱WMS Server鍙栨秷鍗曟嵁鎺ュ彛澶辫触锛岃Е鍙戦檷绾�", cause);
+        return errorResponse();
+    }
+
+    @Override
+    public Map<String, Object> syncMatnrs(ErpMatnrParms params) {
+        log.error("璋冪敤绔嬪簱WMS Server鐗╂枡淇℃伅鍚屾鎺ュ彛澶辫触锛岃Е鍙戦檷绾�", cause);
+        return errorResponse();
+    }
+
+    @Override
+    public Map<String, Object> queryLocsDetls(Map<String, Object> params) {
+        log.error("璋冪敤绔嬪簱WMS Server搴撲綅淇℃伅鏌ヨ鎺ュ彛澶辫触锛岃Е鍙戦檷绾�", cause);
+        return errorResponse();
+    }
+
+    @Override
+    public Map<String, Object> inventoryDetails(Map<String, Object> params) {
+        log.error("璋冪敤绔嬪簱WMS Server搴撳瓨鏄庣粏鏌ヨ鎺ュ彛澶辫触锛岃Е鍙戦檷绾�", cause);
+        return errorResponse();
+    }
+
+    @Override
+    public Map<String, Object> inventorySummary(Map<String, Object> params) {
+        log.error("璋冪敤绔嬪簱WMS Server搴撳瓨姹囨�绘煡璇㈡帴鍙eけ璐ワ紝瑙﹀彂闄嶇骇", cause);
+        return errorResponse();
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/fallback/WmsServerFeignClientFallbackFactory.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/fallback/WmsServerFeignClientFallbackFactory.java
new file mode 100644
index 0000000..4844c9a
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/feign/wms/fallback/WmsServerFeignClientFallbackFactory.java
@@ -0,0 +1,17 @@
+package com.vincent.rsf.openApi.feign.wms.fallback;
+
+import com.vincent.rsf.openApi.feign.wms.WmsServerFeignClient;
+import org.springframework.cloud.openfeign.FallbackFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * Feign 璋冪敤澶辫触鏃跺垱寤哄甫寮傚父淇℃伅鐨� Fallback锛屽湪 Feign 鍐呯粺涓�杩斿洖閿欒鍝嶅簲銆�
+ */
+@Component
+public class WmsServerFeignClientFallbackFactory implements FallbackFactory<WmsServerFeignClient> {
+
+    @Override
+    public WmsServerFeignClient create(Throwable cause) {
+        return new WmsServerFeignClientFallback(cause);
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/AppMapper.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/AppMapper.java
new file mode 100644
index 0000000..dd7f084
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/AppMapper.java
@@ -0,0 +1,11 @@
+package com.vincent.rsf.openApi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.vincent.rsf.openApi.entity.app.App;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+@Mapper
+@Repository
+public interface AppMapper extends BaseMapper<App> {
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/filter/AppIdAuthenticationFilter.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/filter/AppIdAuthenticationFilter.java
new file mode 100644
index 0000000..a87269e
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/filter/AppIdAuthenticationFilter.java
@@ -0,0 +1,92 @@
+package com.vincent.rsf.openApi.security.filter;
+
+import com.vincent.rsf.openApi.entity.constant.Constants;
+import com.vincent.rsf.openApi.security.service.AppAuthService;
+import com.vincent.rsf.openApi.security.utils.TokenUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.annotation.Resource;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * AppId/Token 璁よ瘉杩囨护鍣�
+ */
+@Slf4j
+@Component
+@Order(1)
+public class AppIdAuthenticationFilter extends OncePerRequestFilter {
+
+    @Resource
+    private AppAuthService appAuthService;
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+            throws ServletException, IOException {
+
+        String requestURI = request.getRequestURI();
+        if (isAuthRequest(requestURI)) {
+            filterChain.doFilter(request, response);
+            return;
+        }
+
+        String authHeader = request.getHeader(Constants.HEADER_AUTHORIZATION);
+        if (authHeader != null) {
+            String token = TokenUtils.extractTokenFromHeader(authHeader);
+            if (token != null && TokenUtils.validateTokenTime(token)) {
+                String tokenAppId = TokenUtils.getAppIdFromToken(token);
+                String tokenAppSecret = TokenUtils.getSecretFromToken(token);
+                if (!StringUtils.hasText(tokenAppId) || !StringUtils.hasText(tokenAppSecret)
+                        || !appAuthService.validateApp(tokenAppId, tokenAppSecret)) {
+                    log.warn("Token楠岃瘉澶辫触");
+                    sendErrorResponse(response, Constants.UNAUTHENTICATED_CODE, "璁よ瘉澶辫触锛岃鎻愪緵鏈夋晥鐨凾oken");
+                    return;
+                }
+                request.setAttribute(Constants.REQUEST_ATTR_APP_ID, tokenAppId);
+            } else {
+                log.warn("Token楠岃瘉澶辫触鎴栫己澶�");
+                sendErrorResponse(response, Constants.UNAUTHENTICATED_CODE, "璁よ瘉澶辫触锛岃鎻愪緵鏈夋晥鐨凾oken");
+                return;
+            }
+        } else {
+            log.warn("缂哄皯Token璁よ瘉淇℃伅");
+            sendErrorResponse(response, Constants.UNAUTHENTICATED_CODE, "璁よ瘉澶辫触锛岃鎻愪緵鏈夋晥鐨凾oken");
+            return;
+        }
+
+        filterChain.doFilter(request, response);
+    }
+
+    private void sendErrorResponse(HttpServletResponse response, int code, String message) throws IOException {
+        response.setStatus(code);
+        response.setContentType("application/json;charset=UTF-8");
+        PrintWriter writer = response.getWriter();
+        writer.write("{\"code\": " + code + ", \"msg\": \"" + message + "\", \"data\": null}");
+        writer.flush();
+    }
+
+    private boolean isAuthRequest(String requestURI) {
+        return requestURI != null && requestURI.contains("/getToken");
+    }
+
+    @Override
+    protected boolean shouldNotFilter(HttpServletRequest request) {
+        String requestURI = request.getRequestURI();
+        return requestURI == null
+                || requestURI.contains("/auth/")
+                || requestURI.contains("/public/")
+                || requestURI.contains("/doc.html")
+                || requestURI.contains("/swagger")
+                || requestURI.contains("/webjars")
+                || requestURI.contains("/v2/api-docs")
+                || requestURI.contains("/v3/api-docs");
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/service/AppAuthService.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/service/AppAuthService.java
new file mode 100644
index 0000000..195baac
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/service/AppAuthService.java
@@ -0,0 +1,58 @@
+package com.vincent.rsf.openApi.security.service;
+
+import com.vincent.rsf.openApi.entity.app.App;
+import com.vincent.rsf.openApi.service.AppService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+@Slf4j
+@Service
+public class AppAuthService {
+
+    @Resource
+    private AppService appService;
+    @Resource
+    private PasswordEncoder passwordEncoder;
+
+    public boolean validateApp(String appId, String appSecret) {
+        if (appId == null || appSecret == null) {
+            return false;
+        }
+        try {
+            App app = appService.getById(appId);
+            if (app == null) {
+                return false;
+            }
+            if (app.getEnable() != null && app.getEnable() != 1) {
+                return false;
+            }
+            String stored = app.getScrect();
+            if (stored == null) {
+                return false;
+            }
+            // 瀛樼殑鏄� BCrypt 鍝堝笇鍒欑敤 matches锛屽惁鍒欏吋瀹规槑鏂�
+            if (stored.startsWith("$2")) {
+                return passwordEncoder.matches(appSecret, stored);
+            }
+            return appSecret.equals(stored);
+        } catch (Exception e) {
+            log.error("validateApp寮傚父 appId={}", appId, e);
+            return false;
+        }
+    }
+
+    public App getAppInfo(String appId) {
+        if (appId == null) {
+            return null;
+        }
+        try {
+            return appService.getById(appId);
+        } catch (Exception e) {
+            log.error("getAppInfo澶辫触 appId={}", appId, e);
+            return null;
+        }
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/utils/TokenUtils.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/utils/TokenUtils.java
new file mode 100644
index 0000000..63f3fa0
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/utils/TokenUtils.java
@@ -0,0 +1,87 @@
+package com.vincent.rsf.openApi.security.utils;
+
+import com.vincent.rsf.openApi.entity.constant.Constants;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.SecretKey;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * JWT Token 宸ュ叿绫�
+ */
+public class TokenUtils {
+    private static final Logger log = LoggerFactory.getLogger(TokenUtils.class);
+
+    private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
+    private static final long TOKEN_EXPIRATION = 60 * 60 * 1000L;
+
+    public static String generateToken(Map<String, Object> claims) {
+        long now = System.currentTimeMillis();
+        Date expiration = new Date(now + TOKEN_EXPIRATION);
+        return Jwts.builder()
+                .setClaims(claims)
+                .setExpiration(expiration)
+                .signWith(SECRET_KEY, SignatureAlgorithm.HS256)
+                .compact();
+    }
+
+    public static String generateToken(String appId, String appSecret) {
+        Map<String, Object> claims = new HashMap<>();
+        claims.put("appId", appId);
+        claims.put("appSecret", appSecret);
+        claims.put("created", System.currentTimeMillis());
+        return generateToken(claims);
+    }
+
+    public static Claims parseToken(String token) {
+        try {
+            return Jwts.parserBuilder()
+                    .setSigningKey(SECRET_KEY)
+                    .build()
+                    .parseClaimsJws(token)
+                    .getBody();
+        } catch (JwtException e) {
+            log.error("瑙f瀽Token澶辫触: {}", e.getMessage());
+            return null;
+        }
+    }
+
+    public static boolean validateTokenTime(String token) {
+        try {
+            Claims claims = parseToken(token);
+            if (claims == null) {
+                return false;
+            }
+            Date expiration = claims.getExpiration();
+            return expiration != null && expiration.after(new Date());
+        } catch (JwtException e) {
+            log.error("楠岃瘉Token澶辫触: {}", e.getMessage());
+            return false;
+        }
+    }
+
+    public static String getAppIdFromToken(String token) {
+        Claims claims = parseToken(token);
+        return claims != null ? (String) claims.get("appId") : null;
+    }
+
+    public static String getSecretFromToken(String token) {
+        Claims claims = parseToken(token);
+        return claims != null ? (String) claims.get("appSecret") : null;
+    }
+
+    public static String extractTokenFromHeader(String authHeader) {
+        if (authHeader != null && authHeader.startsWith(Constants.TOKEN_PREFIX)) {
+            return authHeader.substring(Constants.TOKEN_PREFIX.length()).trim();
+        }
+        return null;
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/AppService.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/AppService.java
new file mode 100644
index 0000000..d9a5ab3
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/AppService.java
@@ -0,0 +1,7 @@
+package com.vincent.rsf.openApi.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.vincent.rsf.openApi.entity.app.App;
+
+public interface AppService extends IService<App> {
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/TokenService.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/TokenService.java
new file mode 100644
index 0000000..18bc174
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/TokenService.java
@@ -0,0 +1,20 @@
+package com.vincent.rsf.openApi.service;
+
+/**
+ * 瀵规帴鍗忚 8.1 Token 绛惧彂涓庢牎楠�
+ */
+public interface TokenService {
+
+    /**
+     * 鏍¢獙 appId+appSecret 骞剁鍙� token锛屾湁鏁堟湡 1 灏忔椂
+     * @param appId 搴旂敤缂栫爜
+     * @param appSecret 搴旂敤绉橀挜
+     * @return token 瀛楃涓诧紝澶辫触杩斿洖 null
+     */
+    String issueToken(String appId, String appSecret);
+
+    /**
+     * 鏍¢獙 token 鏄惁鏈夋晥
+     */
+    boolean validateToken(String token);
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/WmsErpService.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/WmsErpService.java
index 719f5fc..4536931 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/WmsErpService.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/WmsErpService.java
@@ -11,13 +11,31 @@
 
     CommonResponse getOrderInfo(ErpOpParams params);
 
-    CommonResponse updateOrderDetl(ErpOpParams params);
+    /** 鏂板鍗曟嵁锛堝吋瀹逛慨鏀癸級锛氬叆/鍑哄簱閫氱煡鍗曚笅鍙戯紝鏈夊垯鏇存柊銆佹棤鍒欐柊澧� */
+    CommonResponse addOrUpdateOrder(ErpOpParams params);
 
-    CommonResponse orderDel(ErpOpParams params);
+    /** 鍙栨秷璁㈠崟/鍙栨秷鍗曟嵁锛氱鍚堝彇娑堟潯浠舵椂鎵ц鍙栨秷閫昏緫 */
+    CommonResponse orderCancel(ErpOpParams params);
 
     CommonResponse syncMatnrs(ErpMatnrParms parms);
 
     CommonResponse reportOrders(ReportParams params);
 
     CommonResponse reportCheck(ReportParams params);
+
+    /** 搴撲綅淇℃伅鏌ヨ锛堣浆鍙戠珛搴擄級 */
+    CommonResponse queryLocsDetls(Map<String, Object> params);
+
+    /**
+     * 閫氱敤杞彂锛氬皢浜戜粨璇锋眰鍘熸牱杞彂鑷崇珛搴� WMS锛堟帴鍙f彁渚涙柟锛氱珛搴擄級
+     * @param wmsPath 绔嬪簱璺緞甯搁噺锛岃 WmsConstant
+     * @param body 璇锋眰浣擄紝鍙负 List 鎴� Map/瀵硅薄锛宯ull 鏃舵寜绌� body 杞彂
+     */
+    CommonResponse forwardToWms(String wmsPath, Object body);
+
+    /** 瀵规帴鍗忚 8.4 搴撳瓨鏄庣粏鏌ヨ */
+    CommonResponse inventoryDetails(Map<String, Object> params);
+
+    /** 瀵规帴鍗忚 8.5 搴撳瓨姹囨�绘煡璇� */
+    CommonResponse inventorySummary(Map<String, Object> params);
 }
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/AppServiceImpl.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/AppServiceImpl.java
new file mode 100644
index 0000000..0495a06
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/AppServiceImpl.java
@@ -0,0 +1,11 @@
+package com.vincent.rsf.openApi.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.vincent.rsf.openApi.entity.app.App;
+import com.vincent.rsf.openApi.mapper.AppMapper;
+import com.vincent.rsf.openApi.service.AppService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AppServiceImpl extends ServiceImpl<AppMapper, App> implements AppService {
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/TokenServiceImpl.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/TokenServiceImpl.java
new file mode 100644
index 0000000..5a01cd9
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/TokenServiceImpl.java
@@ -0,0 +1,56 @@
+package com.vincent.rsf.openApi.service.impl;
+
+import com.vincent.rsf.openApi.security.service.AppAuthService;
+import com.vincent.rsf.openApi.service.TokenService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Slf4j
+@Service
+public class TokenServiceImpl implements TokenService {
+
+    private static final long EXPIRE_MS = 60 * 60 * 1000L;
+
+    @Autowired
+    private AppAuthService appAuthService;
+
+    private final Map<String, Long> tokenExpire = new ConcurrentHashMap<>();
+
+    @Override
+    public String issueToken(String appId, String appSecret) {
+        if (!StringUtils.hasText(appId) || !StringUtils.hasText(appSecret)) {
+            return null;
+        }
+        if (!appAuthService.validateApp(appId, appSecret)) {
+            return null;
+        }
+        String token = UUID.randomUUID().toString().replace("-", "");
+        tokenExpire.put(token, System.currentTimeMillis() + EXPIRE_MS);
+        evictExpired();
+        return token;
+    }
+
+    @Override
+    public boolean validateToken(String token) {
+        if (!StringUtils.hasText(token)) {
+            return false;
+        }
+        Long expire = tokenExpire.get(token);
+        if (expire == null || System.currentTimeMillis() > expire) {
+            tokenExpire.remove(token);
+            return false;
+        }
+        return true;
+    }
+
+    private void evictExpired() {
+        long now = System.currentTimeMillis();
+        tokenExpire.entrySet().removeIf(e -> e.getValue() < now);
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/WmsErpServiceImpl.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/WmsErpServiceImpl.java
index 1b947c9..7b1a0d0 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/WmsErpServiceImpl.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/WmsErpServiceImpl.java
@@ -11,10 +11,13 @@
 import com.vincent.rsf.openApi.entity.constant.WmsConstant;
 import com.vincent.rsf.openApi.entity.dto.CommonResponse;
 import com.vincent.rsf.openApi.entity.dto.ErpCommonResponse;
+import com.vincent.rsf.openApi.entity.dto.ResultData;
 import com.vincent.rsf.openApi.entity.dto.OrderDto;
 import com.vincent.rsf.openApi.entity.params.ErpMatnrParms;
 import com.vincent.rsf.openApi.entity.params.ErpOpParams;
 import com.vincent.rsf.openApi.entity.params.ReportParams;
+import com.vincent.rsf.openApi.entity.params.WmsOrderItemParam;
+import com.vincent.rsf.openApi.feign.wms.WmsServerFeignClient;
 import com.vincent.rsf.openApi.service.WmsErpService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -26,6 +29,7 @@
 import org.springframework.web.client.RestTemplate;
 
 import java.util.*;
+import java.util.stream.Collectors;
 
 @Slf4j
 @Service("WmsErpService")
@@ -40,6 +44,47 @@
     @Autowired
     private RestTemplate restTemplate;
 
+    @Autowired
+    private WmsServerFeignClient wmsServerFeignClient;
+
+    /**
+     * 鍙�夛細鏀圭敤 OpenFeign 璋冪敤 ERP 涓婃姤锛堣鍗曞畬鎴愬洖鍐欍�佺洏鐐瑰樊寮備慨鏀癸級鏃跺惎鐢ㄣ��
+     * 1锛夊鍔� import锛歝om.vincent.rsf.openApi.feign.erp.ErpReportFeignClient锛�
+     * 2锛夊彇娑堜笅闈袱琛屾敞閲婏紝娉ㄥ叆 ErpReportFeignClient锛�
+     * 3锛夊湪 reportOrders銆乺eportCheck 涓敞閲婃帀鏈柟娉曞唴鏁存 HttpEntity/restTemplate 璇锋眰锛屾敼涓轰娇鐢ㄤ笅闈㈡敞閲婁腑鐨� erpReportFeignClient.report(params) 鍙婂搷搴旇В鏋愰�昏緫銆�
+     */
+    // @Autowired
+    // private ErpReportFeignClient erpReportFeignClient;
+
+    /**
+     * 灏� Feign 杩斿洖鐨� Map锛堟垨 R锛夎浆涓� CommonResponse锛岀鍚� 8.2.3锛歝ode銆乵sg銆乨ata锛堝惈 result锛夈��
+     * 鏃犳硶瑙f瀽鎴栭潪鎴愬姛锛坈ode!=200锛夋椂鐩存帴 throw CoolException锛屼笉杩斿洖閿欒浣撱��
+     */
+    private CommonResponse mapToCommonResponse(Map<String, Object> map) {
+        if (map == null) {
+            throw new CoolException("璇锋眰澶辫触");
+        }
+        Object c = map.get("code");
+        int code = c instanceof Number ? ((Number) c).intValue() : 500;
+        if (code != 200) {
+            String msg = map.get("msg") != null ? map.get("msg").toString() : "璇锋眰澶辫触";
+            throw new CoolException(msg);
+        }
+        CommonResponse r = new CommonResponse();
+        r.setCode(200);
+        r.setMsg(map.get("msg") != null ? map.get("msg").toString() : "鎿嶄綔鎴愬姛");
+        Object rawData = map.get("data");
+        if (rawData == null) {
+            r.setData(ResultData.success());
+        } else {
+            Map<String, Object> dataModel = new LinkedHashMap<>();
+            dataModel.put("result", ResultData.SUCCESS);
+            dataModel.put("data", rawData);
+            r.setData(dataModel);
+        }
+        return r;
+    }
+
     /**
      * 鑾峰彇璁㈠崟鏄庣粏
      *
@@ -51,131 +96,131 @@
         if (Objects.isNull(params.getOrderNo()) || params.getOrderNo().isEmpty()) {
             throw new CoolException("璁㈠崟鍙蜂笉鑳戒负绌猴紒锛�");
         }
-        /**WMS鍩虹閰嶇疆閾炬帴*/
-        String rcsUrl = wmsApi.getHost() + ":" + wmsApi.getPort() + WmsConstant.QUERY_ORDER_AND_DETLS;
-        log.info("鏌ヨ璁㈠崟淇℃伅鍙婄姸鎬侊細 {}锛� 璇锋眰鍙傛暟锛� {}", rcsUrl, JSONObject.toJSONString(params));
-        HttpHeaders headers = new HttpHeaders();
-        headers.add("Content-Type", "application/json");
-        headers.add("api-version", "v2.0");
-        HttpEntity httpEntity = new HttpEntity(params, headers);
-        ResponseEntity<String> exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class);
-        log.info("鏌ヨ鍝嶅簲缁撴灉锛� {}", exchange);
-        if (Objects.isNull(exchange.getBody())) {
-            throw new CoolException("鏌ヨ澶辫触锛侊紒");
-        } else {
-            ObjectMapper objectMapper = new ObjectMapper();
-            objectMapper.coercionConfigDefaults().setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
-            try {
-                CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class);
-                if (result.getCode() == 200) {
-                    JSONObject object = JSONObject.parseObject(JSONObject.toJSONString(result.getData()));
-                    OrderDto dto = new OrderDto();
-                    dto.setOrderNo(object.getString("code"))
-                            .setAnfme(object.getDouble("anfme"))
-                            .setType(object.getString("type"))
-                            .setWkType(object.getString("wkType"))
-                            .setQty(object.getDouble("qty"))
-                            .setPoCode(object.getString("poCode"))
-                            .setExceStatus(object.getShort("exceStatus"))
-                            .setWorkQty(object.getDouble("workQty"));
-                    result.setData(dto);
-                    return result;
-                } else {
-                    return result;
-//                    throw new CoolException("鏌ヨ澶辫触锛侊紒");
-                }
-            } catch (JsonProcessingException e) {
-                throw new CoolException(e.getMessage());
+        log.info("鏌ヨ璁㈠崟淇℃伅鍙婄姸鎬侊紝璇锋眰鍙傛暟锛� {}", JSONObject.toJSONString(params));
+        Map<String, Object> res = wmsServerFeignClient.queryOrderAndDetls(params);
+        CommonResponse result = mapToCommonResponse(res);
+        if (result.getCode() == 200 && result.getData() instanceof Map) {
+            @SuppressWarnings("unchecked")
+            Map<String, Object> dataModel = (Map<String, Object>) result.getData();
+            Object inner = dataModel.get("data");
+            if (inner != null) {
+                JSONObject object = JSONObject.parseObject(JSONObject.toJSONString(inner));
+                OrderDto dto = new OrderDto();
+                dto.setOrderNo(object.getString("code"))
+                        .setAnfme(object.getDouble("anfme"))
+                        .setType(object.getString("type"))
+                        .setWkType(object.getString("wkType"))
+                        .setQty(object.getDouble("qty"))
+                        .setPoCode(object.getString("poCode"))
+                        .setExceStatus(object.getShort("exceStatus"))
+                        .setWorkQty(object.getDouble("workQty"));
+                Map<String, Object> wrap = new LinkedHashMap<>();
+                wrap.put("result", ResultData.SUCCESS);
+                wrap.put("data", dto);
+                result.setData(wrap);
             }
+        }
+        return result;
+    }
+
+    /**
+     * 鏂板鍗曟嵁锛堝吋瀹逛慨鏀广�佸彇娑堬級锛�8.3 鍏�/鍑哄簱閫氱煡鍗曚笅鍙戙�俹perateType=3 鏃舵寜鍙栨秷澶勭悊銆�
+     * 浠� 8.3 鏂囨。瀛楁涓轰富锛岃浆鍙戠珛搴撴椂鏄犲皠涓烘湇鍔$ SyncOrderParams 瀛楁銆�
+     */
+    @Override
+    public CommonResponse addOrUpdateOrder(ErpOpParams params) {
+        if (Objects.isNull(params.getOrderNo()) || params.getOrderNo().isEmpty()) {
+            throw new CoolException("璁㈠崟鍙蜂笉鑳戒负绌猴紒锛�");
+        }
+        if (Integer.valueOf(3).equals(params.getOperateType())) {
+            log.info("order/add 鏀跺埌 operateType=3锛岃蛋缁熶竴鍙栨秷閫昏緫锛� {}", params.getOrderNo());
+            return doCancel(params);
+        }
+        Map<String, Object> mapParams = toServerOrderMap(params);
+        List<Map<String, Object>> maps = Collections.singletonList(mapParams);
+        log.info("鏂板/淇敼鍗曟嵁锛岃姹傚弬鏁帮細 {}", JSONArray.toJSONString(maps));
+        Map<String, Object> res = wmsServerFeignClient.updateOrderDetls(maps);
+        CommonResponse r = mapToCommonResponse(res);
+        // 8.3.3锛歞ata 浠呭惈 result锛屼笉杩斿洖涓氬姟杞借嵎
+        r.setData(ResultData.success());
+        return r;
+    }
+
+    /** 8.3 鍙傛暟杞负绔嬪簱 SyncOrderParams 缁撴瀯锛坥rderNo/type/wkType/anfme/arrTime/orderItems 绛夛級 */
+    private Map<String, Object> toServerOrderMap(ErpOpParams params) {
+        Map<String, Object> m = new HashMap<>();
+        m.put("orderNo", params.getOrderNo());
+        m.put("wkType", params.getWkType());
+        m.put("type", params.getOrderType() != null ? String.valueOf(params.getOrderType()) : null);
+        m.put("orderId", params.getOrderId());
+        double anfmeSum = 0;
+        if (params.getOrderItems() != null) {
+            List<Map<String, Object>> items = params.getOrderItems().stream()
+                    .map(this::toServerOrderItemMap)
+                    .collect(Collectors.toList());
+            m.put("orderItems", items);
+            for (WmsOrderItemParam item : params.getOrderItems()) {
+                anfmeSum += parseAnfme(item.getAnfme());
+            }
+        } else {
+            m.put("orderItems", Collections.emptyList());
+        }
+        m.put("anfme", anfmeSum);
+        if (params.getBusinessTime() != null) {
+            m.put("arrTime", new Date(params.getBusinessTime() * 1000));
+        } else if (params.getCreateTime() != null) {
+            m.put("arrTime", new Date(params.getCreateTime() * 1000));
+        }
+        return m;
+    }
+
+    private Map<String, Object> toServerOrderItemMap(WmsOrderItemParam item) {
+        Map<String, Object> m = new HashMap<>();
+        m.put("matnr", item.getMatNr());
+        m.put("maktx", item.getMakTx());
+        m.put("platItemId", item.getLineId());
+        m.put("anfme", parseAnfme(item.getAnfme()));
+        m.put("spec", item.getSpec());
+        m.put("model", item.getModel());
+        m.put("unit", item.getUnit());
+        m.put("batch", item.getBatch());
+        return m;
+    }
+
+    private static double parseAnfme(String anfme) {
+        if (anfme == null || anfme.trim().isEmpty()) {
+            return 0;
+        }
+        try {
+            return Double.parseDouble(anfme.trim());
+        } catch (NumberFormatException e) {
+            return 0;
         }
     }
 
     /**
-     * 璁㈠崟淇敼
-     *
-     * @param params
-     * @return
+     * 鍙栨秷璁㈠崟/鍙栨秷鍗曟嵁銆備笌 /order/add锛坥perateType=3锛夊叡鐢ㄥ悓涓�濂楀彇娑堥�昏緫锛岃浆鍙戠珛搴� sync/orders/delete銆�
      */
     @Override
-    public CommonResponse updateOrderDetl(ErpOpParams params) {
+    public CommonResponse orderCancel(ErpOpParams params) {
         if (Objects.isNull(params.getOrderNo()) || params.getOrderNo().isEmpty()) {
             throw new CoolException("璁㈠崟鍙蜂笉鑳戒负绌猴紒锛�");
         }
-        /**WMS鍩虹閰嶇疆閾炬帴*/
-        String wmsUrl = wmsApi.getHost() + ":" + wmsApi.getPort() + WmsConstant.MODIFY_ORDER_DETLS;
-        HttpHeaders headers = new HttpHeaders();
-        headers.add("Content-Type", "application/json");
-        headers.add("api-version", "v2.0");
-
-        List<Map<String, Object>> maps = new ArrayList<>();
-        Map<String, Object> mapParams = new HashMap<>();
-        mapParams.put("orderNo", params.getOrderNo());
-        mapParams.put("anfme", params.getAnfme());
-        mapParams.put("type", params.getType());
-        mapParams.put("wkType", params.getWkType());
-        mapParams.put("exceStatus", params.getExceStatus());
-        mapParams.put("orderItems", params.getOrderItems());
-        maps.add(mapParams);
-        log.info("淇敼璁㈠崟淇℃伅鍙婄姸鎬侊細 {}锛� 璇锋眰鍙傛暟锛� {}", wmsUrl, JSONArray.toJSONString(maps));
-        HttpEntity<List<Map<String, Object>>> httpEntity = new HttpEntity<>(maps, headers);
-        ResponseEntity<String> exchange = restTemplate.exchange(wmsUrl, HttpMethod.POST, httpEntity, String.class);
-        log.info("璁㈠崟淇敼杩斿洖缁撴灉锛� {}", exchange);
-        if (Objects.isNull(exchange.getBody())) {
-            throw new CoolException("鏌ヨ澶辫触锛侊紒");
-        } else {
-            ObjectMapper objectMapper = new ObjectMapper();
-            objectMapper.coercionConfigDefaults().setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
-            try {
-                CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class);
-                if (result.getCode() == 200) {
-//                    JSONObject object = JSONObject.parseObject(JSONObject.toJSONString(result.getData()));
-                    return result;
-                } else {
-                    return result;
-//                    throw new CoolException("鏌ヨ澶辫触锛侊紒");
-                }
-            } catch (JsonProcessingException e) {
-                throw new CoolException(e.getMessage());
-            }
-        }
+        return doCancel(params);
     }
 
-    /**
-     * 鍒犻櫎鍗曟嵁
-     *
-     * @param params
-     * @return
-     */
-    @Override
-    public CommonResponse orderDel(ErpOpParams params) {
-        if (Objects.isNull(params.getOrderNo()) || params.getOrderNo().isEmpty()) {
-            throw new CoolException("璁㈠崟鍙蜂笉鑳戒负绌猴紒锛�");
-        }
-        /**WMS鍩虹閰嶇疆閾炬帴*/
-        String rcsUrl = wmsApi.getHost() + ":" + wmsApi.getPort() + WmsConstant.ORDER_DEL;
-        log.info("鏌ヨ璁㈠崟淇℃伅鍙婄姸鎬侊細 {}锛� 璇锋眰鍙傛暟锛� {}", rcsUrl, JSONObject.toJSONString(params));
-        HttpHeaders headers = new HttpHeaders();
-        headers.add("Content-Type", "application/json");
-        headers.add("api-version", "v2.0");
-        HttpEntity httpEntity = new HttpEntity(params, headers);
-        ResponseEntity<String> exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class);
-        log.info("鏌ヨ鍝嶅簲缁撴灉锛� {}", exchange);
-        if (Objects.isNull(exchange.getBody())) {
-            throw new CoolException("鏌ヨ澶辫触锛侊紒");
-        } else {
-            ObjectMapper objectMapper = new ObjectMapper();
-            objectMapper.coercionConfigDefaults().setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
-            try {
-                CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class);
-                if (result.getCode() == 200) {
-                    return result;
-                } else {
-                    throw new CoolException("鏌ヨ澶辫触锛侊紒");
-                }
-            } catch (JsonProcessingException e) {
-                throw new CoolException(e.getMessage());
-            }
-        }
+    /** 缁熶竴鍙栨秷閫昏緫锛�/order/add(operateType=3) 涓� /order/cancel銆�/order/del 鍧囪蛋姝ゆ柟娉曪紱8.3.3 data 浠呭惈 result */
+    private CommonResponse doCancel(ErpOpParams params) {
+        log.info("鍙栨秷鍗曟嵁锛岃姹傚弬鏁帮細 {}", JSONObject.toJSONString(params));
+        Map<String, Object> one = new HashMap<>();
+        one.put("orderNo", params.getOrderNo());
+        one.put("orderItems", params.getOrderItems() != null ? params.getOrderItems().stream()
+                .map(this::toServerOrderItemMap)
+                .collect(Collectors.toList()) : Collections.emptyList());
+        Map<String, Object> res = wmsServerFeignClient.orderDel(Collections.singletonList(one));
+        CommonResponse r = mapToCommonResponse(res);
+        r.setData(ResultData.success());
+        return r;
     }
 
     /**
@@ -192,31 +237,9 @@
         if (Objects.isNull(params.getMaktx())) {
             throw new CoolException("鐗╂枡鍚嶇О涓嶈兘涓虹┖锛侊紒");
         }
-        /**WMS鍩虹閰嶇疆閾炬帴*/
-        String rcsUrl = wmsApi.getHost() + ":" + wmsApi.getPort() + WmsConstant.UPDATE_MATNR_INFO;
-        log.info("鐗╂枡淇敼锛歿}锛� 璇锋眰鍙傛暟锛� {}", rcsUrl, JSONObject.toJSONString(params));
-        HttpHeaders headers = new HttpHeaders();
-        headers.add("Content-Type", "application/json");
-        headers.add("api-version", "v2.0");
-        HttpEntity httpEntity = new HttpEntity(params, headers);
-        ResponseEntity<String> exchange = restTemplate.exchange(rcsUrl, HttpMethod.POST, httpEntity, String.class);
-        log.info("淇敼缁撴灉锛� {}", exchange);
-        if (Objects.isNull(exchange.getBody())) {
-            throw new CoolException("淇敼澶辫触锛侊紒");
-        } else {
-            ObjectMapper objectMapper = new ObjectMapper();
-            objectMapper.coercionConfigDefaults().setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
-            try {
-                CommonResponse result = objectMapper.readValue(exchange.getBody(), CommonResponse.class);
-                if (result.getCode() == 200) {
-                    return result;
-                } else {
-                    throw new CoolException("淇敼澶辫触锛侊紒");
-                }
-            } catch (JsonProcessingException e) {
-                throw new CoolException(e.getMessage());
-            }
-        }
+        log.info("鐗╂枡淇敼锛岃姹傚弬鏁帮細 {}", JSONObject.toJSONString(params));
+        Map<String, Object> res = wmsServerFeignClient.syncMatnrs(params);
+        return mapToCommonResponse(res);
     }
 
     /**
@@ -252,6 +275,13 @@
                 throw new CoolException("涓婁紶澶辫触锛侊紒");
             }
         }
+        // Map<String, Object> res = erpReportFeignClient.report(params);
+        // if (res == null) throw new CoolException("涓婁紶澶辫触锛侊紒");
+        // Object c = res.get("code"); int code = c instanceof Number ? ((Number) c).intValue() : 500;
+        // if (code != 200) throw new CoolException("涓婁紶澶辫触锛侊紒");
+        // CommonResponse commonResponse = new CommonResponse();
+        // commonResponse.setCode(200).setMsg(String.valueOf(res.get("msg"))).setData(res.get("data"));
+        // return commonResponse;
     }
 
     /**
@@ -287,6 +317,79 @@
                 throw new CoolException("淇敼澶辫触锛侊紒");
             }
         }
+        // Map<String, Object> res = erpReportFeignClient.report(params);
+        // if (res == null) throw new CoolException("淇敼澶辫触锛侊紒");
+        // Object c = res.get("code"); int code = c instanceof Number ? ((Number) c).intValue() : 500;
+        // if (code != 200) throw new CoolException("淇敼澶辫触锛侊紒");
+        // CommonResponse commonResponse = new CommonResponse();
+        // commonResponse.setCode(200).setMsg(String.valueOf(res.get("msg"))).setData(res.get("data"));
+        // return commonResponse;
+    }
+
+    @Override
+    public CommonResponse queryLocsDetls(Map<String, Object> params) {
+        Map<String, Object> p = params == null ? new HashMap<>() : params;
+        log.info("搴撲綅淇℃伅鏌ヨ锛岃姹傚弬鏁帮細 {}", JSONObject.toJSONString(p));
+        return mapToCommonResponse(wmsServerFeignClient.queryLocsDetls(p));
+    }
+
+    /** 8.4 搴撳瓨鏄庣粏鏌ヨ锛氳繑鍥炲�� data 涓哄璞℃暟缁勶紝涓嶅寘 result 澶栧眰 */
+    @Override
+    public CommonResponse inventoryDetails(Map<String, Object> params) {
+        Map<String, Object> p = params == null ? new HashMap<>() : params;
+        log.info("搴撳瓨鏄庣粏鏌ヨ锛岃姹傚弬鏁帮細 {}", JSONObject.toJSONString(p));
+        CommonResponse r = mapToCommonResponse(wmsServerFeignClient.inventoryDetails(p));
+        unwrapDataToArray(r);
+        return r;
+    }
+
+    /** 8.5 搴撳瓨姹囨�绘煡璇細杩斿洖鍊� data 涓哄璞℃暟缁勶紝涓嶅寘 result 澶栧眰 */
+    @Override
+    public CommonResponse inventorySummary(Map<String, Object> params) {
+        Map<String, Object> p = params == null ? new HashMap<>() : params;
+        log.info("搴撳瓨姹囨�绘煡璇紝璇锋眰鍙傛暟锛� {}", JSONObject.toJSONString(p));
+        CommonResponse r = mapToCommonResponse(wmsServerFeignClient.inventorySummary(p));
+        unwrapDataToArray(r);
+        return r;
+    }
+
+    /** 8.4/8.5 瑙勮寖锛歞ata 涓哄璞℃暟缁勶紝灏� { result, data: array } 鏀逛负 data = array */
+    private void unwrapDataToArray(CommonResponse r) {
+        if (r.getData() instanceof Map) {
+            @SuppressWarnings("unchecked")
+            Map<String, Object> dataModel = (Map<String, Object>) r.getData();
+            Object inner = dataModel.get("data");
+            if (inner != null) {
+                r.setData(inner);
+            }
+        }
+    }
+
+    @Override
+    public CommonResponse forwardToWms(String wmsPath, Object body) {
+        String url = wmsApi.getHost() + ":" + wmsApi.getPort() + wmsPath;
+        Object payload = body != null ? body : new HashMap<String, Object>();
+        log.info("杞彂璇锋眰锛� {}锛� 璇锋眰浣撻暱搴︼細 {}", url, payload instanceof List ? ((List<?>) payload).size() : 1);
+        return postToWms(url, payload);
+    }
+
+    /** 缁熶竴杞彂骞惰В鏋愪负 CommonResponse */
+    private CommonResponse postToWms(String url, Object body) {
+        HttpHeaders headers = new HttpHeaders();
+        headers.add("Content-Type", "application/json");
+        headers.add("api-version", "v2.0");
+        HttpEntity<Object> httpEntity = new HttpEntity<>(body, headers);
+        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class);
+        if (Objects.isNull(exchange.getBody())) {
+            throw new CoolException("璇锋眰澶辫触锛侊紒");
+        }
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.coercionConfigDefaults().setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty);
+        try {
+            return objectMapper.readValue(exchange.getBody(), CommonResponse.class);
+        } catch (JsonProcessingException e) {
+            throw new CoolException("瑙f瀽鍝嶅簲澶辫触锛�" + e.getMessage());
+        }
     }
 
 }
diff --git a/rsf-open-api/src/main/resources/application-dev.yml b/rsf-open-api/src/main/resources/application-dev.yml
index c7644eb..5a86c98 100644
--- a/rsf-open-api/src/main/resources/application-dev.yml
+++ b/rsf-open-api/src/main/resources/application-dev.yml
@@ -5,6 +5,10 @@
 spring:
   application:
     name: @pom.artifactId@
+  cloud:
+    openfeign:
+      circuitbreaker:
+        enabled: true   # Feign 璋冪敤澶辫触鏃惰蛋 Fallback锛屽湪 Feign 鍐呯粺涓�杩斿洖閿欒
   mvc:
     static-path-pattern: /**
     path match:
@@ -12,6 +16,7 @@
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
     url: jdbc:mysql://127.0.0.1:3306/rsf_jdxaj?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
+#    url: jdbc:mysql://127.0.0.1:3306/jdxajwms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
     username: root
     password: 12345
     type: com.alibaba.druid.pool.DruidDataSource
@@ -64,6 +69,6 @@
     port: 8086
   erp:
     #閾炬帴
-    host: http://www.itsdg.cn
+    host: http://127.0.0.1
     #绔彛
     port: 3741
diff --git a/rsf-open-api/src/main/resources/application-prod.yml b/rsf-open-api/src/main/resources/application-prod.yml
index f4e2b46..5d1aee8 100644
--- a/rsf-open-api/src/main/resources/application-prod.yml
+++ b/rsf-open-api/src/main/resources/application-prod.yml
@@ -77,5 +77,5 @@
     host: http://127.0.0.1
     port: 8085
   erp:
-    host: http://www.itsdg.cn
+    host: http://127.0.0.1
     port: 3741
\ No newline at end of file
diff --git a/rsf-open-api/src/main/resources/application.yml b/rsf-open-api/src/main/resources/application.yml
index d95bde1..574f599 100644
--- a/rsf-open-api/src/main/resources/application.yml
+++ b/rsf-open-api/src/main/resources/application.yml
@@ -15,8 +15,8 @@
     :banner: false
     db-config:
       id-type: auto
-      logic-delete-value: 1
-      logic-not-delete-value: 0
+      logic-delete-value: 1     #鍒犻櫎鐘舵��
+      logic-not-delete-value: 0 #姝e父鐘舵��
 
 logging:
   file:
diff --git a/rsf-server/pom.xml b/rsf-server/pom.xml
index e1848c5..a451ff6 100644
--- a/rsf-server/pom.xml
+++ b/rsf-server/pom.xml
@@ -33,6 +33,25 @@
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-security</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>org.springframework.cloud</groupId>
+			<artifactId>spring-cloud-starter-openfeign</artifactId>
+		</dependency>
+		<!-- 鐔旀柇鍣細Feign 璋冪敤澶辫触鏃惰Е鍙� Fallback锛屽湪 Feign 鍐呯粺涓�杩斿洖閿欒鍝嶅簲 -->
+		<dependency>
+			<groupId>org.springframework.cloud</groupId>
+			<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.security</groupId>
+			<artifactId>spring-security-test</artifactId>
+			<scope>test</scope>
+		</dependency>
     </dependencies>
 
 	<build>
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ServerBoot.java b/rsf-server/src/main/java/com/vincent/rsf/server/ServerBoot.java
index 192e033..072a6e8 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ServerBoot.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/ServerBoot.java
@@ -2,10 +2,12 @@
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
 import org.springframework.context.annotation.ComponentScan;
 
 @SpringBootApplication
 @ComponentScan(basePackages = {"com.vincent.rsf.server", "com.vincent.rsf.common"})
+@EnableFeignClients(basePackages = "com.vincent.rsf.server.api.feign")
 public class ServerBoot {
 
 	public static void main(String[] args) {
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/config/RemotesInfoProperties.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/config/RemotesInfoProperties.java
index 18d5c2c..67956f6 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/api/config/RemotesInfoProperties.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/config/RemotesInfoProperties.java
@@ -30,14 +30,23 @@
      */
     private String prePath;
 
+    /**
+     * 浜戜粨鍦板潃
+     */
+    private String baseUrl;
+
     @Data
     @Configuration
     @ConfigurationProperties(prefix = "platform.erp.api")
     public class ApiInfo {
-        /**
-         * 涓�閿笂鎶ヨ川妫�鎺ュ彛
-         */
+        /** 涓�閿笂鎶ヨ川妫�鎺ュ彛 */
         private String notifyInspect;
+        /** 9.1 鍏�/鍑哄簱缁撴灉涓婃姤锛堢珛搴撲晶璇锋眰浜戜粨锛� */
+        private String inOutResultPath = "/api/report/inOutResult";
+        /** 9.2 搴撳瓨璋冩暣涓诲姩涓婃姤锛堢珛搴撲晶璇锋眰浜戜粨锛� */
+        private String inventoryAdjustPath = "/api/report/inventoryAdjust";
+        /** 鐗╂枡鍩虹淇℃伅鍚屾锛堢珛搴撲晶璇锋眰浜戜粨锛� */
+        private String matSyncPath = "/api/mat/sync";
     }
 
     @Data
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/CloudWmsMockController.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/CloudWmsMockController.java
new file mode 100644
index 0000000..4a562ba
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/CloudWmsMockController.java
@@ -0,0 +1,67 @@
+package com.vincent.rsf.server.api.controller;
+
+import com.vincent.rsf.server.api.controller.erp.params.InOutResultReportParam;
+import com.vincent.rsf.server.api.controller.erp.params.InventoryAdjustReportParam;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 浜戜粨WMS 妯℃嫙鎺ュ彛锛堝鎺ュ崗璁� 9.1銆�9.2銆佺墿鏂欏悓姝ワ級銆�
+ * 浜戜粨鏈彁渚涚湡瀹� URL 鏃讹紝鍙皢 platform.erp.base-url 鎸囧悜鏈満璇ユ湇鍔★紙濡� http://127.0.0.1:8086/rsf-server锛夛紝
+ * 绔嬪簱涓婃姤璇锋眰浼氭墦鍒版湰鎺ュ彛骞惰繑鍥炴ā鎷熸垚鍔熴��
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api")
+@Api(value = "浜戜粨妯℃嫙鎺ュ彛", tags = "浜戜粨妯℃嫙锛堟棤鐪熷疄浜戜粨URL鏃朵娇鐢級")
+public class CloudWmsMockController {
+
+    private static Map<String, Object> successResponse() {
+        Map<String, Object> data = new HashMap<>();
+        data.put("result", "SUCCESS");
+        Map<String, Object> map = new HashMap<>();
+        map.put("code", 200);
+        map.put("msg", "");
+        map.put("data", data);
+        return map;
+    }
+
+    /** 9.1 鍏�/鍑哄簱缁撴灉涓婃姤 - 妯℃嫙 */
+    @ApiOperation("鍏�/鍑哄簱缁撴灉涓婃姤锛堟ā鎷燂級")
+    @PostMapping(value = "/report/inOutResult", consumes = MediaType.APPLICATION_JSON_VALUE)
+    public Map<String, Object> mockInOutResult(@RequestBody InOutResultReportParam body) {
+        log.info("浜戜粨妯℃嫙-鍏�/鍑哄簱缁撴灉涓婃姤锛宱rderNo={}锛宭ocId={}锛宮atNr={}", 
+                body != null ? body.getOrderNo() : null, 
+                body != null ? body.getLocId() : null, 
+                body != null ? body.getMatNr() : null);
+        return successResponse();
+    }
+
+    /** 9.2 搴撳瓨璋冩暣涓诲姩涓婃姤 - 妯℃嫙 */
+    @ApiOperation("搴撳瓨璋冩暣涓诲姩涓婃姤锛堟ā鎷燂級")
+    @PostMapping(value = "/report/inventoryAdjust", consumes = MediaType.APPLICATION_JSON_VALUE)
+    public Map<String, Object> mockInventoryAdjust(@RequestBody InventoryAdjustReportParam body) {
+        log.info("浜戜粨妯℃嫙-搴撳瓨璋冩暣涓婃姤锛宑hangeType={}锛寃areHouseId={}锛宮atNr={}", 
+                body != null ? body.getChangeType() : null, 
+                body != null ? body.getWareHouseId() : null, 
+                body != null ? body.getMatNr() : null);
+        return successResponse();
+    }
+
+    /** 鐗╂枡鍩虹淇℃伅鍚屾 - 妯℃嫙 */
+    @ApiOperation("鐗╂枡鍚屾锛堟ā鎷燂級")
+    @PostMapping(value = "/mat/sync", consumes = MediaType.APPLICATION_JSON_VALUE)
+    public Map<String, Object> mockMatSync(@RequestBody Object body) {
+        log.info("浜戜粨妯℃嫙-鐗╂枡鍚屾锛宐ody={}", body != null ? body.toString() : null);
+        return successResponse();
+    }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/ErpQueryController.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/ErpQueryController.java
index 50168ab..9b86828 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/ErpQueryController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/ErpQueryController.java
@@ -104,4 +104,22 @@
         return receiveMsgService.queryTransfer(queryParams);
     }
 
+    /**
+     * 瀵规帴鍗忚 8.4 搴撳瓨鏄庣粏鏌ヨ
+     */
+    @PostMapping("/inventory/details")
+    @ApiOperation(value = "搴撳瓨鏄庣粏鏌ヨ")
+    public R inventoryDetails(@RequestBody(required = false) InventoryDetailsParam param) {
+        return receiveMsgService.inventoryDetails(param != null ? param : new InventoryDetailsParam());
+    }
+
+    /**
+     * 瀵规帴鍗忚 8.5 搴撳瓨姹囨�绘煡璇�
+     */
+    @PostMapping("/inventory/summary")
+    @ApiOperation(value = "搴撳瓨姹囨�绘煡璇�")
+    public R inventorySummary(@RequestBody(required = false) InventorySummaryParam param) {
+        return receiveMsgService.inventorySummary(param != null ? param : new InventorySummaryParam());
+    }
+
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/BaseMatParms.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/BaseMatParms.java
index ee9e813..e982f9e 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/BaseMatParms.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/BaseMatParms.java
@@ -1,20 +1,29 @@
 package com.vincent.rsf.server.api.controller.erp.params;
 
-
+import com.fasterxml.jackson.annotation.JsonAlias;
 import io.swagger.annotations.ApiModelProperty;
-import lombok.experimental.Accessors;
 import lombok.Data;
+import lombok.experimental.Accessors;
 
+/**
+ * 鐗╂枡鍩虹淇℃伅鍚屾鍏ュ弬锛堝鎺ュ崗璁� 8.2锛�
+ * 鍗忚瀛楁 matNr/makTx 涓� matnr/maktx 鍧囧彲鎺ユ敹銆�
+ */
 @Data
 @Accessors(chain = true)
 public class BaseMatParms {
 
-    @ApiModelProperty("鐗╂枡鍚嶇О")
-    private String maktx;
+    @ApiModelProperty(value = "鎿嶄綔绫诲瀷锛�1鏂板 2淇敼 3绂佺敤 4鍚敤", example = "1")
+    private Integer operateType;
 
-    @ApiModelProperty("鐗╂枡缂栫爜*")
+    @ApiModelProperty(value = "鐗╂枡缂栫爜*锛堝崗璁瓧娈� matNr 鍚屼箟锛�")
+    @JsonAlias("matNr")
     private String matnr;
 
+    @ApiModelProperty(value = "鐗╂枡鍚嶇О锛堝崗璁瓧娈� makTx 鍚屼箟锛�")
+    @JsonAlias("makTx")
+    private String maktx;
+
     @ApiModelProperty("鐗╂枡鍒嗙粍")
     private String groupName;
 
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/FlexibleDateDeserializer.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/FlexibleDateDeserializer.java
new file mode 100644
index 0000000..b396a3d
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/FlexibleDateDeserializer.java
@@ -0,0 +1,76 @@
+package com.vincent.rsf.server.api.controller.erp.params;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * 鏀寔鏃堕棿鎴筹紙绉�/姣锛変笌澶氱瀛楃涓叉牸寮忕殑 Date 鍙嶅簭鍒楀寲銆�
+ * 瀛楃涓叉敮鎸侊細ISO-8601锛堝 2024-03-03T08:00:00.000+00:00锛夈�亂yyy-MM-dd HH:mm:ss锛岀簿纭埌绉掋��
+ */
+public class FlexibleDateDeserializer extends JsonDeserializer<Date> {
+
+    private static final long TIMESTAMP_MS_THRESHOLD = 10_000_000_000L; // 绾� 1970-04-26 璧蜂负姣
+
+    private static final String PATTERN_SECONDS = "yyyy-MM-dd HH:mm:ss";
+    private static final TimeZone DEFAULT_TZ = TimeZone.getTimeZone("GMT+8");
+
+    @Override
+    public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+        JsonToken t = p.getCurrentToken();
+        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) {
+            long v = p.getLongValue();
+            long ms = v < TIMESTAMP_MS_THRESHOLD ? v * 1000 : v;
+            return new Date(ms);
+        }
+        if (t == JsonToken.VALUE_STRING) {
+                String s = p.getText().trim();
+                if (s.isEmpty()) {
+                    return null;
+                }
+                // 1) 灏濊瘯绾暟瀛楀瓧绗︿覆锛堢鎴栨绉掞級
+                try {
+                    long v = Long.parseLong(s);
+                    long ms = v < TIMESTAMP_MS_THRESHOLD ? v * 1000 : v;
+                    return new Date(ms);
+                } catch (NumberFormatException ignored) {
+                }
+                // 2) ISO-8601锛堝惈 T 鍜屾椂鍖猴級
+                if (s.contains("T")) {
+                    try {
+                        return java.util.Date.from(java.time.Instant.parse(s));
+                    } catch (Exception ignored) {
+                    }
+                }
+                // 3) yyyy-MM-dd HH:mm:ss
+                try {
+                    SimpleDateFormat sdf = new SimpleDateFormat(PATTERN_SECONDS);
+                    sdf.setTimeZone(DEFAULT_TZ);
+                    sdf.setLenient(false);
+                    return sdf.parse(s);
+                } catch (Exception ignored) {
+                }
+                // 4) 浠呮棩鏈� yyyy-MM-dd
+                if (s.length() == 10 && s.charAt(4) == '-' && s.charAt(7) == '-') {
+                    try {
+                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+                        sdf.setTimeZone(DEFAULT_TZ);
+                        sdf.setLenient(false);
+                        return sdf.parse(s);
+                    } catch (Exception ignored) {
+                    }
+                }
+                throw new IOException("Cannot parse date: " + s + ", support: timestamp(seconds/ms), ISO-8601, yyyy-MM-dd HH:mm:ss");
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return null;
+        }
+        return (Date) ctxt.handleUnexpectedToken(Date.class, p);
+    }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InOutResultReportParam.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InOutResultReportParam.java
new file mode 100644
index 0000000..5cebeba
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InOutResultReportParam.java
@@ -0,0 +1,42 @@
+package com.vincent.rsf.server.api.controller.erp.params;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 瀵规帴鍗忚 9.1 鍏�/鍑哄簱缁撴灉涓婃姤 - 璇锋眰浣擄紙绔嬪簱WMS 鐩存帴璇锋眰 浜戜粨WMS锛�
+ */
+@Data
+@Accessors(chain = true)
+@ApiModel(value = "InOutResultReportParam", description = "鍏�/鍑哄簱缁撴灉涓婃姤")
+public class InOutResultReportParam {
+
+    @ApiModelProperty(value = "璁㈠崟缂栫爜", required = true)
+    private String orderNo;
+
+    @ApiModelProperty("璁″垝璺熻釜鍙�")
+    private String planNo;
+
+    @ApiModelProperty("琛屽唴鐮�")
+    private String lineId;
+
+    @ApiModelProperty(value = "浠撳簱缂栫爜", required = true)
+    private String wareHouseId;
+
+    @ApiModelProperty(value = "搴撲綅鍙�", required = true)
+    private String locId;
+
+    @ApiModelProperty(value = "鐗╂枡缂栫爜", required = true)
+    private String matNr;
+
+    @ApiModelProperty(value = "鏈鍑�/鍏ユ暟閲�", required = true)
+    private String qty;
+
+    @ApiModelProperty("鎵樼洏鍙�")
+    private String palletId;
+
+    @ApiModelProperty("鎵规")
+    private String batch;
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventoryAdjustReportParam.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventoryAdjustReportParam.java
new file mode 100644
index 0000000..1f152ee
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventoryAdjustReportParam.java
@@ -0,0 +1,37 @@
+package com.vincent.rsf.server.api.controller.erp.params;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 瀵规帴鍗忚 9.2 搴撳瓨璋冩暣涓诲姩涓婃姤 - 璇锋眰浣�
+ * 绔嬪簱渚ц皟鐢ㄤ簯浠撻�氱煡锛氱珛搴揥MS 涓诲姩璋冩暣搴撳瓨鍚庡悜浜戜粨WMS 涓婃姤銆�
+ */
+@Data
+@Accessors(chain = true)
+@ApiModel(value = "InventoryAdjustReportParam", description = "搴撳瓨璋冩暣涓诲姩涓婃姤")
+public class InventoryAdjustReportParam {
+
+    @ApiModelProperty(value = "璋冩暣绫诲瀷锛�1 鍏ュ簱锛�2 鍑哄簱锛�3 绉诲簱", required = true)
+    private Integer changeType;
+
+    @ApiModelProperty(value = "浠撳簱缂栫爜", required = true)
+    private String wareHouseId;
+
+    @ApiModelProperty("婧愬簱浣嶅彿")
+    private String sourceLocId;
+
+    @ApiModelProperty("鐩爣搴撲綅鍙凤紙绉诲簱鏃舵湁锛�")
+    private String targetLocId;
+
+    @ApiModelProperty(value = "鐗╂枡缂栫爜", required = true)
+    private String matNr;
+
+    @ApiModelProperty(value = "璋冩暣鏁伴噺", required = true)
+    private String qty;
+
+    @ApiModelProperty("鎵樼洏鍙�")
+    private String palletId;
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventoryDetailsParam.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventoryDetailsParam.java
new file mode 100644
index 0000000..58daeec
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventoryDetailsParam.java
@@ -0,0 +1,38 @@
+package com.vincent.rsf.server.api.controller.erp.params;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * 瀵规帴鍗忚 8.4 搴撳瓨鏄庣粏鏌ヨ 璇锋眰鍙傛暟
+ */
+@Data
+@Accessors(chain = true)
+@ApiModel(value = "InventoryDetailsParam", description = "搴撳瓨鏄庣粏鏌ヨ")
+public class InventoryDetailsParam implements Serializable {
+
+    @ApiModelProperty("浠撳簱缂栫爜")
+    private String wareHouseId;
+
+    @ApiModelProperty("搴撲綅缂栫爜")
+    private String locId;
+
+    @ApiModelProperty("鐗╂枡缂栫爜")
+    private String matNr;
+
+    @ApiModelProperty("璁㈠崟鍙�/宸ュ崟鍙�")
+    private String orderNo;
+
+    @ApiModelProperty("璁″垝璺熻釜鍙�")
+    private String planNo;
+
+    @ApiModelProperty("鎵规鍙�")
+    private String batch;
+
+    @ApiModelProperty("鐗╂枡缁�")
+    private String matGroup;
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventorySummaryParam.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventorySummaryParam.java
new file mode 100644
index 0000000..b2f0136
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventorySummaryParam.java
@@ -0,0 +1,23 @@
+package com.vincent.rsf.server.api.controller.erp.params;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * 瀵规帴鍗忚 8.5 搴撳瓨姹囨�绘煡璇� 璇锋眰鍙傛暟
+ */
+@Data
+@Accessors(chain = true)
+@ApiModel(value = "InventorySummaryParam", description = "搴撳瓨姹囨�绘煡璇�")
+public class InventorySummaryParam implements Serializable {
+
+    @ApiModelProperty("浠撳簱缂栫爜")
+    private String wareHouseId;
+
+    @ApiModelProperty("鐗╂枡缂栫爜锛屽涓互鑻辨枃閫楀彿鍒嗛殧")
+    private String matNr;
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/SyncOrderParams.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/SyncOrderParams.java
index 5d8cc09..71a0c16 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/SyncOrderParams.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/SyncOrderParams.java
@@ -1,6 +1,7 @@
 package com.vincent.rsf.server.api.controller.erp.params;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -36,8 +37,9 @@
     @ApiModelProperty("鏁伴噺")
     private Double anfme;
 
-    @DateTimeFormat(pattern = "yyyy-MM-dd HH:ss:mm")
-    @JsonFormat(pattern = "yyyy-MM-dd HH:ss:mm")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @JsonDeserialize(using = FlexibleDateDeserializer.class)
     private Date arrTime;
 
     @ApiModelProperty("鍗曟嵁鏄庣粏淇℃伅")
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/feign/CloudWmsErpFeignClient.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/feign/CloudWmsErpFeignClient.java
new file mode 100644
index 0000000..097fc15
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/feign/CloudWmsErpFeignClient.java
@@ -0,0 +1,35 @@
+package com.vincent.rsf.server.api.feign;
+
+import com.vincent.rsf.server.api.controller.erp.params.InOutResultReportParam;
+import com.vincent.rsf.server.api.controller.erp.params.InventoryAdjustReportParam;
+import com.vincent.rsf.server.api.feign.fallback.CloudWmsErpFeignClientFallbackFactory;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import java.util.Map;
+
+/**
+ * 绔嬪簱渚ч�氳繃 OpenFeign 璋冪敤浜戜粨WMS锛氬叆/鍑哄簱缁撴灉涓婃姤锛�9.1锛夈�佸簱瀛樿皟鏁翠笂鎶ワ紙9.2锛夈�佺墿鏂欏悓姝ャ��
+ * 浣跨敤 platform.erp.base-url 浣滀负鏍瑰湴鍧�锛涘け璐ユ椂璧� Fallback锛岀粺涓�杩斿洖閿欒鍝嶅簲锛堜笉鎶涘紓甯革級銆�
+ */
+@FeignClient(
+    name = "cloudWmsErp",
+    url = "${platform.erp.base-url:http://127.0.0.1:8080}",
+    fallbackFactory = CloudWmsErpFeignClientFallbackFactory.class
+)
+public interface CloudWmsErpFeignClient {
+
+    /** 9.1 鍏�/鍑哄簱缁撴灉涓婃姤 */
+    @PostMapping(value = "/api/report/inOutResult", consumes = MediaType.APPLICATION_JSON_VALUE)
+    Map<String, Object> reportInOutResult(@RequestBody InOutResultReportParam body);
+
+    /** 9.2 搴撳瓨璋冩暣涓诲姩涓婃姤 */
+    @PostMapping(value = "/api/report/inventoryAdjust", consumes = MediaType.APPLICATION_JSON_VALUE)
+    Map<String, Object> reportInventoryAdjust(@RequestBody InventoryAdjustReportParam body);
+
+    /** 鐗╂枡鍩虹淇℃伅鍚屾 */
+    @PostMapping(value = "/api/mat/sync", consumes = MediaType.APPLICATION_JSON_VALUE)
+    Map<String, Object> syncMatnrs(@RequestBody Object body);
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/feign/fallback/CloudWmsErpFeignClientFallback.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/feign/fallback/CloudWmsErpFeignClientFallback.java
new file mode 100644
index 0000000..14ff707
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/feign/fallback/CloudWmsErpFeignClientFallback.java
@@ -0,0 +1,95 @@
+package com.vincent.rsf.server.api.feign.fallback;
+
+import com.vincent.rsf.server.api.controller.erp.params.InOutResultReportParam;
+import com.vincent.rsf.server.api.controller.erp.params.InventoryAdjustReportParam;
+import com.vincent.rsf.server.api.feign.CloudWmsErpFeignClient;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 浜戜粨WMS Feign 瀹㈡埛绔檷绾у鐞嗭紝鍦� Feign 鍐呯粺涓�杩斿洖閿欒鍝嶅簲锛堜笉鎶涘紓甯革級銆�
+ * 鐢� CloudWmsErpFeignClientFallbackFactory 鍒涘缓骞朵紶鍏ュ紓甯� cause銆�
+ */
+@Slf4j
+@Component
+public class CloudWmsErpFeignClientFallback implements CloudWmsErpFeignClient {
+
+    private final Throwable cause;
+
+    public CloudWmsErpFeignClientFallback() {
+        this.cause = null;
+    }
+
+    public CloudWmsErpFeignClientFallback(Throwable cause) {
+        this.cause = cause;
+    }
+
+    private Map<String, Object> errorResponse() {
+        return resultMap(500, filterErrorMessage(cause), dataFail());
+    }
+
+    private static Map<String, Object> dataFail() {
+        Map<String, Object> data = new HashMap<>();
+        data.put("result", "FAIL");
+        return data;
+    }
+
+    private static Map<String, Object> resultMap(int code, String msg, Map<String, Object> data) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("code", code);
+        map.put("msg", msg);
+        map.put("data", data);
+        return map;
+    }
+
+    /**
+     * 杩囨护閿欒娑堟伅涓殑 URL锛屽彧淇濈暀閿欒绫诲瀷
+     */
+    public static String filterErrorMessage(Throwable throwable) {
+        if (throwable == null) {
+            return "璇锋眰澶辫触锛氭湇鍔¤皟鐢ㄥけ璐ワ紝璇风◢鍚庨噸璇�";
+        }
+        return filterErrorMessage(throwable.getMessage());
+    }
+
+    public static String filterErrorMessage(String errorMessage) {
+        if (errorMessage == null || errorMessage.isEmpty()) {
+            return "璇锋眰澶辫触锛氭湭鐭ラ敊璇�";
+        }
+        String filteredMessage = errorMessage;
+        if (filteredMessage.contains("executing")) {
+            int i = filteredMessage.indexOf("executing");
+            filteredMessage = i > 0 ? filteredMessage.substring(0, i).trim() : "璇锋眰瓒呮椂";
+        } else if (filteredMessage.contains("http://") || filteredMessage.contains("https://")) {
+            filteredMessage = filteredMessage.replaceAll("https?://[^\\s]+", "").trim();
+            if (filteredMessage.isEmpty()) {
+                filteredMessage = "璇锋眰澶辫触";
+            }
+        }
+        if (filteredMessage.isEmpty()) {
+            filteredMessage = "鏈煡閿欒";
+        }
+        return "璇锋眰澶辫触锛�" + filteredMessage;
+    }
+
+    @Override
+    public Map<String, Object> reportInOutResult(InOutResultReportParam body) {
+        log.error("璋冪敤浜戜粨WMS 鍏�/鍑哄簱缁撴灉涓婃姤鎺ュ彛澶辫触锛岃Е鍙戦檷绾�", cause);
+        return errorResponse();
+    }
+
+    @Override
+    public Map<String, Object> reportInventoryAdjust(InventoryAdjustReportParam body) {
+        log.error("璋冪敤浜戜粨WMS 搴撳瓨璋冩暣涓婃姤鎺ュ彛澶辫触锛岃Е鍙戦檷绾�", cause);
+        return errorResponse();
+    }
+
+    @Override
+    public Map<String, Object> syncMatnrs(Object body) {
+        log.error("璋冪敤浜戜粨WMS 鐗╂枡鍚屾鎺ュ彛澶辫触锛岃Е鍙戦檷绾�", cause);
+        return errorResponse();
+    }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/feign/fallback/CloudWmsErpFeignClientFallbackFactory.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/feign/fallback/CloudWmsErpFeignClientFallbackFactory.java
new file mode 100644
index 0000000..63c46fa
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/feign/fallback/CloudWmsErpFeignClientFallbackFactory.java
@@ -0,0 +1,17 @@
+package com.vincent.rsf.server.api.feign.fallback;
+
+import com.vincent.rsf.server.api.feign.CloudWmsErpFeignClient;
+import org.springframework.cloud.openfeign.FallbackFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * Feign 璋冪敤浜戜粨澶辫触鏃跺垱寤哄甫寮傚父淇℃伅鐨� Fallback锛屽湪 Feign 鍐呯粺涓�杩斿洖閿欒鍝嶅簲銆�
+ */
+@Component
+public class CloudWmsErpFeignClientFallbackFactory implements FallbackFactory<CloudWmsErpFeignClient> {
+
+    @Override
+    public CloudWmsErpFeignClient create(Throwable cause) {
+        return new CloudWmsErpFeignClientFallback(cause);
+    }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/service/CloudWmsReportService.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/CloudWmsReportService.java
new file mode 100644
index 0000000..e8fa3a7
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/CloudWmsReportService.java
@@ -0,0 +1,33 @@
+package com.vincent.rsf.server.api.service;
+
+import com.vincent.rsf.server.api.controller.erp.params.InOutResultReportParam;
+import com.vincent.rsf.server.api.controller.erp.params.InventoryAdjustReportParam;
+
+import java.util.Map;
+
+/**
+ * 绔嬪簱渚ц姹備簯浠揥MS锛氫笂鎶ャ�佺墿鏂欏悓姝ョ瓑
+ */
+public interface CloudWmsReportService {
+
+    /**
+     * 鐗╂枡鍩虹淇℃伅鍚屾锛堢珛搴撲晶璇锋眰浜戜粨锛�
+     * @param body 鐗╂枡鏁版嵁锛屽彲涓哄崟鏉℃垨鍒楄〃锛屽叿浣撶粨鏋勪互浜戜粨鎺ュ彛涓哄噯
+     * @return 浜戜粨杩斿洖缁撴瀯 Map锛歝ode, msg, data
+     */
+    Map<String, Object> syncMatnrsToCloud(Object body);
+
+    /**
+     * 9.1 鍏�/鍑哄簱缁撴灉涓婃姤
+     * @param param 涓婃姤鍙傛暟
+     * @return 浜戜粨杩斿洖缁撴瀯 Map锛歝ode, msg, data锛坉ata.result 涓� SUCCESS/FAIL锛�
+     */
+    Map<String, Object> reportInOutResult(InOutResultReportParam param);
+
+    /**
+     * 9.2 搴撳瓨璋冩暣涓诲姩涓婃姤锛堢珛搴撲晶璋冪敤浜戜粨閫氱煡锛�
+     * @param param 涓婃姤鍙傛暟
+     * @return 浜戜粨杩斿洖缁撴瀯 Map锛歝ode, msg, data锛坉ata.result 涓� SUCCESS/FAIL锛�
+     */
+    Map<String, Object> reportInventoryAdjust(InventoryAdjustReportParam param);
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/service/ReceiveMsgService.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/ReceiveMsgService.java
index 14c8691..afb7a38 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/api/service/ReceiveMsgService.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/ReceiveMsgService.java
@@ -152,4 +152,14 @@
      * @return
      */
     R matUpdate(BaseMatParms baseMatParms);
+
+    /**
+     * 瀵规帴鍗忚 8.4 搴撳瓨鏄庣粏鏌ヨ
+     */
+    R inventoryDetails(InventoryDetailsParam param);
+
+    /**
+     * 瀵规帴鍗忚 8.5 搴撳瓨姹囨�绘煡璇�
+     */
+    R inventorySummary(InventorySummaryParam param);
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/CloudWmsReportServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/CloudWmsReportServiceImpl.java
new file mode 100644
index 0000000..ca1da5c
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/CloudWmsReportServiceImpl.java
@@ -0,0 +1,99 @@
+package com.vincent.rsf.server.api.service.impl;
+
+import com.vincent.rsf.server.api.config.RemotesInfoProperties;
+import com.vincent.rsf.server.api.controller.erp.params.InOutResultReportParam;
+import com.vincent.rsf.server.api.controller.erp.params.InventoryAdjustReportParam;
+import com.vincent.rsf.server.api.feign.CloudWmsErpFeignClient;
+import com.vincent.rsf.server.api.service.CloudWmsReportService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 绔嬪簱渚ц姹備簯浠擄細鍏�/鍑哄簱缁撴灉涓婃姤锛�9.1锛夈�佸簱瀛樿皟鏁翠富鍔ㄤ笂鎶ワ紙9.2锛夈�佺墿鏂欏熀纭�淇℃伅鍚屾銆�
+ * 浣跨敤 OpenFeign 璋冪敤锛涘彲閫� HttpEntity(RestTemplate) 鏂瑰紡宸叉敞閲婁繚鐣欍��
+ */
+@Slf4j
+@Service
+public class CloudWmsReportServiceImpl implements CloudWmsReportService {
+
+    @Autowired
+    private RemotesInfoProperties erpApi;
+
+    @Autowired
+    private RemotesInfoProperties.ApiInfo erpApiInfo;
+
+    @Autowired
+    private CloudWmsErpFeignClient cloudWmsErpFeignClient;
+
+    /**
+     * 鍙�夛細鏀圭敤 HttpEntity(RestTemplate) 璋冪敤浜戜粨鏃跺惎鐢ㄣ��
+     */
+    // @Autowired
+    // private RestTemplate restTemplate;
+
+    @Override
+    public Map<String, Object> syncMatnrsToCloud(Object body) {
+        if (!isCloudWmsConfigured()) {
+            log.warn("ErpApi(浜戜粨WMS) 鏈厤缃� host锛岃烦杩囩墿鏂欏熀纭�淇℃伅鍚屾");
+            return stubSuccess("浜戜粨鍦板潃鏈厤缃紝鏈疄闄呭悓姝�");
+        }
+        return cloudWmsErpFeignClient.syncMatnrs(body != null ? body : new HashMap<>());
+    }
+
+    @Override
+    public Map<String, Object> reportInOutResult(InOutResultReportParam param) {
+        if (param == null) {
+            return resultMap(400, "鍙傛暟涓嶈兘涓虹┖", null);
+        }
+        if (!isCloudWmsConfigured()) {
+            log.warn("ErpApi(浜戜粨WMS) 鏈厤缃� host锛岃烦杩� 9.1 鍏�/鍑哄簱缁撴灉涓婃姤锛岃鍗曪細{}", param.getOrderNo());
+            return stubSuccess("浜戜粨鍦板潃鏈厤缃紝鏈疄闄呬笂鎶�");
+        }
+        return cloudWmsErpFeignClient.reportInOutResult(param);
+    }
+
+    @Override
+    public Map<String, Object> reportInventoryAdjust(InventoryAdjustReportParam param) {
+        if (param == null) {
+            return resultMap(400, "鍙傛暟涓嶈兘涓虹┖", null);
+        }
+        if (!isCloudWmsConfigured()) {
+            log.warn("ErpApi(浜戜粨WMS) 鏈厤缃� host锛岃烦杩� 9.2 搴撳瓨璋冩暣涓婃姤锛岀墿鏂欙細{}", param.getMatNr());
+            return stubSuccess("浜戜粨鍦板潃鏈厤缃紝鏈疄闄呬笂鎶�");
+        }
+        return cloudWmsErpFeignClient.reportInventoryAdjust(param);
+    }
+
+    private boolean isCloudWmsConfigured() {
+        String host = erpApi.getHost();
+        return host != null && !host.trim().isEmpty();
+    }
+
+    private Map<String, Object> stubSuccess(String msg) {
+        Map<String, Object> data = new HashMap<>();
+        data.put("result", "SUCCESS");
+        return resultMap(200, msg, data);
+    }
+
+    private Map<String, Object> resultMap(int code, String msg, Map<String, Object> data) {
+        Map<String, Object> map = new HashMap<>();
+        map.put("code", code);
+        map.put("msg", msg);
+        map.put("data", data);
+        return map;
+    }
+
+    // ========== 鍙�夛細HttpEntity(RestTemplate) 鏂瑰紡锛堝綋鍓嶆湭浣跨敤锛� ==========
+    // 鍚敤姝ラ锛�1锛夊彇娑堜笂鏂� restTemplate 鐨� @Autowired 娉ㄥ叆锛�
+    // 2锛夊彇娑堜笅闈㈡暣娈垫敞閲婏紝鎭㈠ buildUrl銆乸ostToCloudWms銆乸arseResponse 鏂规硶鍙� OBJECT_MAPPER锛�
+    // 3锛夊湪 syncMatnrsToCloud/reportInOutResult/reportInventoryAdjust 涓敼涓猴細String url = buildUrl(erpApiInfo.getXxxPath()); if (url == null) return stubSuccess(...); return postToCloudWms(url, body);
+    //
+    // private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+    // private String buildUrl(String path) { ... }
+    // private Map<String, Object> postToCloudWms(String url, Object body) { HttpHeaders headers = ...; HttpEntity<Object> entity = new HttpEntity<>(body, headers); ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class); return parseResponse(response.getBody()); }
+    // private Map<String, Object> parseResponse(String json) { ... }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/MobileServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/MobileServiceImpl.java
index 7141848..5c52b70 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/MobileServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/MobileServiceImpl.java
@@ -579,7 +579,7 @@
                 || !Objects.isNull(fieldIndex) || !Cools.isEmpty(matnrCode) || !Cools.isEmpty(asnCode);
         
         if (!hasValidCondition) {
-            throw new CoolException("璇疯嚦灏戣緭鍏ヤ竴涓煡璇㈡潯浠讹細鐗╂枡缂栫爜銆丄SN鍗曞彿銆佽窡韪爜銆佹壒娆℃垨绁ㄥ彿");
+            throw new CoolException("璇疯嚦灏戣緭鍏ヤ竴涓煡璇㈡潯浠讹細鐗╂枡缂栫爜銆乄MS鍗曞彿銆佹壒鍙�");/*銆佽窡韪爜銆佹壒娆℃垨绁ㄥ彿*/
         }
         
         // 濡傛灉鎵弿鐗╂枡缂栫爜涓擜SN鍗曞彿涓虹┖锛岀洿鎺ヤ粠鐗╂枡淇℃伅琛ㄨ幏鍙栵紝涓嶆煡璇㈡敹璐у尯
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/ReceiveMsgServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/ReceiveMsgServiceImpl.java
index dcf690b..1c2e00b 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/ReceiveMsgServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/ReceiveMsgServiceImpl.java
@@ -9,6 +9,7 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.vincent.rsf.framework.common.Cools;
 import com.vincent.rsf.framework.common.R;
 import com.vincent.rsf.framework.exception.CoolException;
 import com.vincent.rsf.server.api.controller.erp.params.*;
@@ -101,6 +102,8 @@
     private DictDataService dictDataService;
     @Autowired
     private DictTypeService dictTypeService;
+    @Autowired
+    private LocItemService locItemService;
 
 
     /**
@@ -773,9 +776,8 @@
     }
 
     /**
-     * 鍩虹鐗╂枡淇℃伅鍙樻洿
-     * @param baseMatParms
-     * @return
+     * 鍩虹鐗╂枡淇℃伅鍙樻洿锛堝鎺ュ崗璁� 8.2锛�
+     * operateType锛�1鏂板 2淇敼 3绂佺敤 4鍚敤锛涗笉浼犳垨 1/2 鏃舵寜鏈夊垯鏇存柊銆佹棤鍒欐柊澧炪��
      */
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -783,6 +785,21 @@
         if (StringUtils.isBlank(baseMatParms.getMatnr())) {
             throw new CoolException("鐗╂枡缂栫爜涓嶈兘涓虹┖锛侊紒");
         }
+        Integer operateType = baseMatParms.getOperateType();
+        // 3 绂佺敤 / 4 鍚敤锛氫粎鏇存柊鐘舵�侊紙status 1 姝e父 0 鍐荤粨锛�
+        if (Integer.valueOf(3).equals(operateType) || Integer.valueOf(4).equals(operateType)) {
+            Matnr matnr = matnrService.getOne(new LambdaQueryWrapper<Matnr>().eq(Matnr::getCode, baseMatParms.getMatnr()));
+            if (matnr == null) {
+                throw new CoolException("鐗╂枡涓嶅瓨鍦紝鏃犳硶鎵ц绂佺敤/鍚敤锛侊紒");
+            }
+            int status = Integer.valueOf(4).equals(operateType) ? 1 : 0; // 4 鍚敤=1 姝e父锛�3 绂佺敤=0 鍐荤粨
+            matnr.setStatus(status);
+            if (!matnrService.updateById(matnr)) {
+                throw new CoolException(operateType == 4 ? "鐗╂枡鍚敤澶辫触锛侊紒" : "鐗╂枡绂佺敤澶辫触锛侊紒");
+            }
+            return R.ok();
+        }
+        // 1 鏂板 / 2 淇敼 / 涓嶄紶锛氭湁鍒欐洿鏂般�佹棤鍒欐柊澧�
         Matnr matnr = matnrService.getOne(new LambdaQueryWrapper<Matnr>().eq(Matnr::getCode, baseMatParms.getMatnr()));
         if (Objects.isNull(matnr)) {
             Matnr matnr1 = new Matnr();
@@ -819,4 +836,118 @@
 
         return R.ok();
     }
+
+    @Override
+    public R inventoryDetails(InventoryDetailsParam param) {
+        LambdaQueryWrapper<LocItem> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(LocItem::getDeleted, 0);
+        if (!Cools.isEmpty(param.getLocId())) {
+            wrapper.eq(LocItem::getLocCode, param.getLocId());
+        }
+        if (!Cools.isEmpty(param.getMatNr())) {
+            wrapper.eq(LocItem::getMatnrCode, param.getMatNr());
+        }
+        if (!Cools.isEmpty(param.getBatch())) {
+            wrapper.eq(LocItem::getBatch, param.getBatch());
+        }
+        if (!Cools.isEmpty(param.getOrderNo())) {
+            wrapper.and(w -> w.eq(LocItem::getPlatOrderCode, param.getOrderNo()).or().eq(LocItem::getPlatWorkCode, param.getOrderNo()));
+        }
+        if (!Cools.isEmpty(param.getPlanNo())) {
+            wrapper.eq(LocItem::getPlatWorkCode, param.getPlanNo());
+        }
+        if (!Cools.isEmpty(param.getWareHouseId())) {
+            Warehouse wh = warehouseService.getOne(new LambdaQueryWrapper<Warehouse>().eq(Warehouse::getCode, param.getWareHouseId()));
+            if (wh != null) {
+                List<Loc> locs = locService.list(new LambdaQueryWrapper<Loc>().eq(Loc::getWarehouseId, wh.getId()));
+                if (!locs.isEmpty()) {
+                    wrapper.in(LocItem::getLocId, locs.stream().map(Loc::getId).collect(Collectors.toList()));
+                } else {
+                    return R.ok().add(Collections.emptyList());
+                }
+            } else {
+                return R.ok().add(Collections.emptyList());
+            }
+        }
+        List<LocItem> list = locItemService.list(wrapper);
+        List<Map<String, Object>> result = new ArrayList<>();
+        for (LocItem item : list) {
+            Map<String, Object> row = new LinkedHashMap<>();
+            row.put("locId", item.getLocCode());
+            Loc loc = locService.getById(item.getLocId());
+            if (loc != null && loc.getWarehouseId() != null) {
+                Warehouse w = warehouseService.getById(loc.getWarehouseId());
+                row.put("wareHouseId", w != null ? w.getCode() : null);
+                row.put("wareHouseName", w != null ? w.getName() : null);
+            } else {
+                row.put("wareHouseId", null);
+                row.put("wareHouseName", null);
+            }
+            row.put("palletId", item.getTrackCode());
+            row.put("matNr", item.getMatnrCode());
+            row.put("makTx", item.getMaktx());
+            row.put("anfme", item.getAnfme() != null ? item.getAnfme() : 0);
+            row.put("unit", item.getUnit());
+            row.put("status", item.getStatus() != null ? item.getStatus() : 1);
+            row.put("orderType", item.getWkType());
+            row.put("orderNo", item.getPlatOrderCode());
+            row.put("planNo", item.getPlatWorkCode());
+            row.put("batch", item.getBatch());
+            result.add(row);
+        }
+        return R.ok().add(result);
+    }
+
+    @Override
+    public R inventorySummary(InventorySummaryParam param) {
+        LambdaQueryWrapper<LocItem> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(LocItem::getDeleted, 0).select(LocItem::getLocId, LocItem::getMatnrCode, LocItem::getMaktx, LocItem::getAnfme, LocItem::getUnit);
+        if (!Cools.isEmpty(param.getWareHouseId())) {
+            Warehouse wh = warehouseService.getOne(new LambdaQueryWrapper<Warehouse>().eq(Warehouse::getCode, param.getWareHouseId()));
+            if (wh != null) {
+                List<Loc> locs = locService.list(new LambdaQueryWrapper<Loc>().eq(Loc::getWarehouseId, wh.getId()));
+                if (!locs.isEmpty()) {
+                    wrapper.in(LocItem::getLocId, locs.stream().map(Loc::getId).collect(Collectors.toList()));
+                } else {
+                    return R.ok().add(Collections.emptyList());
+                }
+            } else {
+                return R.ok().add(Collections.emptyList());
+            }
+        }
+        if (!Cools.isEmpty(param.getMatNr())) {
+            List<String> matNrs = Arrays.asList(param.getMatNr().split(","));
+            wrapper.in(LocItem::getMatnrCode, matNrs.stream().map(String::trim).collect(Collectors.toList()));
+        }
+        List<LocItem> list = locItemService.list(wrapper);
+        Map<String, Map<String, Object>> sumMap = new LinkedHashMap<>();
+        for (LocItem item : list) {
+            Loc loc = locService.getById(item.getLocId());
+            String whId = null;
+            String whName = null;
+            if (loc != null && loc.getWarehouseId() != null) {
+                Warehouse w = warehouseService.getById(loc.getWarehouseId());
+                whId = w != null ? w.getCode() : null;
+                whName = w != null ? w.getName() : null;
+            }
+            String key = (whId != null ? whId : "") + "|" + (item.getMatnrCode() != null ? item.getMatnrCode() : "");
+            final String finalWhId = whId;
+            final String finalWhName = whName;
+            sumMap.compute(key, (k, v) -> {
+                if (v == null) {
+                    v = new LinkedHashMap<>();
+                    v.put("wareHouseId", finalWhId);
+                    v.put("wareHouseName", finalWhName);
+                    v.put("matNr", item.getMatnrCode());
+                    v.put("matTx", item.getMaktx());
+                    v.put("anfme", (item.getAnfme() != null ? item.getAnfme() : 0));
+                    v.put("unit", item.getUnit());
+                } else {
+                    v.put("anfme", ((Number) v.get("anfme")).doubleValue() + (item.getAnfme() != null ? item.getAnfme() : 0));
+                }
+                return v;
+            });
+        }
+        return R.ok().add(new ArrayList<>(sumMap.values()));
+    }
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/ScheduleTriggerController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/ScheduleTriggerController.java
new file mode 100644
index 0000000..c191b1a
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/ScheduleTriggerController.java
@@ -0,0 +1,78 @@
+package com.vincent.rsf.server.manager.controller;
+
+import com.vincent.rsf.framework.common.R;
+import com.vincent.rsf.server.manager.schedules.AsnOrderLogSchedule;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 瀹氭椂浠诲姟鎵嬪姩瑙﹀彂鎺ュ彛锛屼粎鍏佽鏈満璇锋眰锛�127.0.0.1 / ::1锛夈��
+ */
+@Slf4j
+@RestController
+@RequestMapping("/schedule")
+@Api(value = "瀹氭椂浠诲姟瑙﹀彂", tags = "瀹氭椂浠诲姟鎵嬪姩瑙﹀彂锛堜粎鏈湴锛�")
+public class ScheduleTriggerController {
+
+    private static final List<String> LOCALHOST_IPS = Arrays.asList("127.0.0.1", "0:0:0:0:0:0:0:1", "::1");
+
+    private final AsnOrderLogSchedule asnOrderLogSchedule;
+
+    public ScheduleTriggerController(AsnOrderLogSchedule asnOrderLogSchedule) {
+        this.asnOrderLogSchedule = asnOrderLogSchedule;
+    }
+
+    private static boolean isLocalRequest(HttpServletRequest request) {
+        String remote = request.getRemoteAddr();
+        if (remote != null && LOCALHOST_IPS.contains(remote)) {
+            return true;
+        }
+        String forwarded = request.getHeader("X-Forwarded-For");
+        if (forwarded != null && !forwarded.isEmpty()) {
+            remote = forwarded.split(",")[0].trim();
+        }
+        return remote != null && LOCALHOST_IPS.contains(remote);
+    }
+
+    @ApiOperation("鎵嬪姩鎵ц鍏ュ簱杞巻鍙诧紙InStockToLog锛夛紝浠呭厑璁告湰鍦拌姹�")
+    @PostMapping("/trigger/inStockToLog")
+    public ResponseEntity<R> triggerInStockToLog(HttpServletRequest request) {
+        if (!isLocalRequest(request)) {
+            log.warn("鎷掔粷闈炴湰鍦拌姹傝Е鍙� InStockToLog锛宺emote={}", request.getRemoteAddr());
+            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(R.error("浠呭厑璁告湰鍦拌姹�"));
+        }
+        try {
+            asnOrderLogSchedule.InStockToLog();
+            return ResponseEntity.ok(R.ok("鎵ц瀹屾垚"));
+        } catch (Exception e) {
+            log.error("InStockToLog 鎵ц澶辫触", e);
+            return ResponseEntity.ok(R.error(e.getMessage()));
+        }
+    }
+
+    @ApiOperation("鎵嬪姩鎵ц鐗╃悊鍒犻櫎涓婁笂涓湀涔嬪墠宸查�昏緫鍒犻櫎鐨勫師鍗曞強鏄庣粏锛屼粎鍏佽鏈湴璇锋眰")
+    @PostMapping("/trigger/physicalDeleteLogicDeletedOrders")
+    public ResponseEntity<R> triggerPhysicalDeleteLogicDeletedOrders(HttpServletRequest request) {
+        if (!isLocalRequest(request)) {
+            log.warn("鎷掔粷闈炴湰鍦拌姹傝Е鍙� physicalDeleteLogicDeletedOrders锛宺emote={}", request.getRemoteAddr());
+            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(R.error("浠呭厑璁告湰鍦拌姹�"));
+        }
+        try {
+            asnOrderLogSchedule.physicalDeleteLogicDeletedOrders();
+            return ResponseEntity.ok(R.ok("鎵ц瀹屾垚"));
+        } catch (Exception e) {
+            log.error("physicalDeleteLogicDeletedOrders 鎵ц澶辫触", e);
+            return ResponseEntity.ok(R.error(e.getMessage()));
+        }
+    }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/CheckOrderParams.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/CheckOrderParams.java
index d2d9e86..3639413 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/CheckOrderParams.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/CheckOrderParams.java
@@ -18,7 +18,7 @@
     @ApiModelProperty(value= "绉�鐐瑰崟ID")
     private Long orderId;
 
-    @ApiModelProperty(value= "ASN鍗曞彿")
+    @ApiModelProperty(value= "WMS鍗曞彿")
     private String orderCode;
 
     @ApiModelProperty(value= "鐗╂枡鏍囪瘑")
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/AsnOrderItemLog.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/AsnOrderItemLog.java
index c2bc085..8cd0317 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/AsnOrderItemLog.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/AsnOrderItemLog.java
@@ -50,9 +50,9 @@
     private Long asnId;
 
     /**
-     * ASN鍗曞彿
+     * WMS鍗曞彿
      */
-    @ApiModelProperty(value= "ASN鍗曞彿")
+    @ApiModelProperty(value= "WMS鍗曞彿")
     private String asnCode;
 
     /**
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/CloudWmsNotifyLog.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/CloudWmsNotifyLog.java
new file mode 100644
index 0000000..578b8da
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/CloudWmsNotifyLog.java
@@ -0,0 +1,88 @@
+package com.vincent.rsf.server.manager.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/** 浜戜粨涓婃姤寰呭姙璁板綍 */
+@Data
+@Accessors(chain = true)
+@TableName("man_cloud_wms_notify_log")
+@ApiModel(value = "CloudWmsNotifyLog", description = "浜戜粨涓婃姤寰呭姙璁板綍")
+public class CloudWmsNotifyLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    public static final String REPORT_TYPE_IN_OUT_RESULT = "IN_OUT_RESULT";
+    public static final String REPORT_TYPE_INVENTORY_ADJUST = "INVENTORY_ADJUST";
+
+    /** 閫氱煡鐘舵�侊細寰呴�氱煡 */
+    public static final int NOTIFY_STATUS_PENDING = 0;
+    /** 閫氱煡鐘舵�侊細宸叉垚鍔� */
+    public static final int NOTIFY_STATUS_SUCCESS = 1;
+    /** 閫氱煡鐘舵�侊細澶辫触锛堝惈瓒呰繃閲嶈瘯娆℃暟锛� */
+    public static final int NOTIFY_STATUS_FAIL = 2;
+
+    @ApiModelProperty("涓婚敭")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("涓婃姤绫诲瀷锛欼N_OUT_RESULT-鍏ュ嚭搴撶粨鏋滐紝INVENTORY_ADJUST-搴撳瓨璋冩暣")
+    private String reportType;
+
+    @ApiModelProperty("璇锋眰浣� JSON")
+    private String requestBody;
+
+    @ApiModelProperty("鏄惁宸查�氱煡鍒颁簯浠擄細0 寰呴�氱煡 1 鎴愬姛 2 澶辫触")
+    private Integer notifyStatus;
+
+    @ApiModelProperty("宸查�氱煡娆℃暟锛堥噸璇曠疮璁★級")
+    private Integer retryCount;
+
+    @ApiModelProperty("鏈�澶ч噸璇曟鏁�")
+    private Integer maxRetryCount;
+
+    @ApiModelProperty("閲嶈瘯闂撮殧绉掓暟")
+    private Integer retryIntervalSeconds;
+
+    @ApiModelProperty("鏈�杩戜竴娆¤姹備綋锛堥噸璇曟椂鍙兘涓庡師 requestBody 涓�鑷达級")
+    private String lastRequestBody;
+
+    @ApiModelProperty("鏈�杩戜竴娆¤繑鍥炵粨鏋� JSON")
+    private String lastResponseBody;
+
+    @ApiModelProperty("鏈�杩戜竴娆¤姹傛椂闂�")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date lastNotifyTime;
+
+    @ApiModelProperty("涓氬姟鍏宠仈锛堝 taskId銆乺eviseLogId锛屼究浜庢帓鏌ワ級")
+    private String bizRef;
+
+    @ApiModelProperty("绉熸埛")
+    private Integer tenantId;
+
+    @ApiModelProperty("鏄惁鍒犻櫎 0 鍚� 1 鏄�")
+    @TableLogic
+    private Integer deleted;
+
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createTime;
+
+    @ApiModelProperty("鏇存柊鏃堕棿")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updateTime;
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WkOrderItem.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WkOrderItem.java
index 6ffd890..b2d2680 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WkOrderItem.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WkOrderItem.java
@@ -75,9 +75,9 @@
     private Double workQty;
 
     /**
-     * ASN鍗曞彿
+     * WMS鍗曞彿
      */
-    @ApiModelProperty(value= "ASN鍗曞彿")
+    @ApiModelProperty(value= "WMS鍗曞彿")
     private String orderCode;
 
     /**
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/AsnOrderTemplate.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/AsnOrderTemplate.java
index dbcc443..e60b1b6 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/AsnOrderTemplate.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/AsnOrderTemplate.java
@@ -28,8 +28,8 @@
     /**
      * 缂栧彿
      */
-    @Excel(name = "*ASN鍗曞彿")
-    @ApiModelProperty(value = "*ASN鍗曞彿")
+    @Excel(name = "*WMS鍗曞彿")
+    @ApiModelProperty(value = "*WMS鍗曞彿")
     @ExcelComment(value = "code", example = "ASN5945272236")
     private String code;
 
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/TransferTemplate.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/TransferTemplate.java
index 6b7a013..7702547 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/TransferTemplate.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/excel/TransferTemplate.java
@@ -8,8 +8,8 @@
     /**
      * 缂栧彿
      */
-    @Excel(name = "*ASN鍗曞彿")
-    @ApiModelProperty(value = "*ASN鍗曞彿")
+    @Excel(name = "*WMS鍗曞彿")
+    @ApiModelProperty(value = "*WMS鍗曞彿")
     @ExcelComment(value = "code", example = "ASN5945272236")
     private String code;
 
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/AsnOrderItemMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/AsnOrderItemMapper.java
index 71bfff3..0f3ef13 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/AsnOrderItemMapper.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/AsnOrderItemMapper.java
@@ -12,6 +12,7 @@
 import org.apache.ibatis.annotations.Param;
 import org.springframework.stereotype.Repository;
 
+import java.util.List;
 import java.util.Map;
 
 @Mapper
@@ -22,4 +23,7 @@
 
     WkOrderItem resultById(@Param(Constants.WRAPPER) LambdaQueryWrapper<WkOrderItem> buildWrapper);
 
+    /** 鎸夎鍗� id 鐗╃悊鍒犻櫎宸查�昏緫鍒犻櫎鐨勬槑缁� */
+    int physicalDeleteByOrderIds(@Param("orderIds") List<Long> orderIds);
+
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/AsnOrderMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/AsnOrderMapper.java
index 1b0f90e..74c570f 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/AsnOrderMapper.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/AsnOrderMapper.java
@@ -11,6 +11,7 @@
 import org.apache.ibatis.annotations.Param;
 import org.springframework.stereotype.Repository;
 
+import java.util.Date;
 import java.util.List;
 
 @Mapper
@@ -20,4 +21,10 @@
     DashboardDto getDashbord(@Param("type") String type, @Param("taskType") String taskType);
 
     List<StockTransItemDto> getStockTrand(@Param(Constants.WRAPPER) LambdaQueryWrapper<StockStatistic> queryWrapper);
+
+    /** 鏌ヨ鍦ㄦ寚瀹氭椂闂翠箣鍓嶈閫昏緫鍒犻櫎鐨勮鍗� id锛堢敤浜庣墿鐞嗘竻鐞嗭紝琛ュ垹鍘嗗彶锛� */
+    List<Long> selectLogicDeletedOrderIdsBefore(@Param("before") Date before);
+
+    /** 鎸� id 鐗╃悊鍒犻櫎宸查�昏緫鍒犻櫎鐨勮鍗� */
+    int physicalDeleteByIds(@Param("ids") List<Long> ids);
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/CloudWmsNotifyLogMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/CloudWmsNotifyLogMapper.java
new file mode 100644
index 0000000..fce731e
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/CloudWmsNotifyLogMapper.java
@@ -0,0 +1,11 @@
+package com.vincent.rsf.server.manager.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.vincent.rsf.server.manager.entity.CloudWmsNotifyLog;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+@Mapper
+@Repository
+public interface CloudWmsNotifyLogMapper extends BaseMapper<CloudWmsNotifyLog> {
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/AsnOrderLogSchedule.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/AsnOrderLogSchedule.java
index 0bdd761..ebd6656 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/AsnOrderLogSchedule.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/AsnOrderLogSchedule.java
@@ -10,6 +10,8 @@
 import com.vincent.rsf.server.common.utils.FieldsUtils;
 import com.vincent.rsf.server.manager.entity.*;
 import com.vincent.rsf.server.manager.enums.*;
+import com.vincent.rsf.server.manager.mapper.AsnOrderItemMapper;
+import com.vincent.rsf.server.manager.mapper.AsnOrderMapper;
 import com.vincent.rsf.server.manager.service.*;
 import com.vincent.rsf.server.manager.service.impl.StockItemServiceImpl;
 import com.vincent.rsf.server.manager.service.impl.StockServiceImpl;
@@ -23,6 +25,8 @@
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.time.LocalDate;
+import java.time.ZoneId;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -56,6 +60,10 @@
     @Autowired
     private ReportMsgService reportMsgService;
 
+    @Autowired
+    private AsnOrderMapper asnOrderMapper;
+    @Autowired
+    private AsnOrderItemMapper asnOrderItemMapper;
 
     /**
      * @param
@@ -64,7 +72,7 @@
      * @description 鍒犻櫎宸插畬鎴愯鍗曞姞鍏og琛�
      * @time 2025/3/19 19:09
      */
-    @Scheduled(cron = "0 0 2 1 * ?")
+    @Scheduled(cron = "0 0 5 * * ?")
     @Transactional(rollbackFor = Exception.class)
     public void InStockToLog() {
         List<WkOrder> wkOrders = asnOrderService.list(new LambdaQueryWrapper<WkOrder>()
@@ -89,7 +97,7 @@
      * @description 鍑哄簱鍗曞畬鎴愬悗锛岀姸鎬佷慨鏀�
      * @time 2025/6/16 08:35
      */
-    @Scheduled(cron = "0/15 * * * * ?  ")
+    @Scheduled(cron = "0/25 * * * * ?  ")
 //    @Scheduled(cron = "0 0 2 1 * ?")
     @Transactional(rollbackFor = Exception.class)
     public void outStockComplete() {
@@ -144,8 +152,9 @@
             }
 //            if (order.getType().equals(OrderType.ORDER_OUT.type) &&  order.getReportOnce() >= 4) {
                 AsnOrderLog one = asnOrderLogService.getOne(new LambdaQueryWrapper<AsnOrderLog>().eq(AsnOrderLog::getCode, order.getCode()), false);
+                AsnOrderLog orderLog;
                 if (Objects.isNull(one)) {
-                    AsnOrderLog orderLog = new AsnOrderLog();
+                    orderLog = new AsnOrderLog();
                     if (type.equals(OrderType.ORDER_OUT.type)) {
                         order.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_TASK_DONE.val);
                         order.setQty(order.getWorkQty());
@@ -153,28 +162,41 @@
                     BeanUtils.copyProperties(order, orderLog);
                     orderLog.setId(null);
                     orderLog.setAsnId(order.getId());
-
                     if (!asnOrderLogService.save(orderLog)) {
                         throw new CoolException("涓诲崟鍘嗗彶妗f坊鍔犲け璐ワ紒锛�");
                     }
-
-                    List<AsnOrderItemLog> logs = new ArrayList<>();
-                    List<WkOrderItem> items = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>()
-                            .eq(WkOrderItem::getOrderId, order.getId()));
-                    items.forEach(item -> {
-                        AsnOrderItemLog itemLog = new AsnOrderItemLog();
-                        BeanUtils.copyProperties(item, itemLog);
-                        itemLog.setAsnItemId(itemLog.getId())
-                                .setId(null)
-                                .setMatnrId(item.getMatnrId())
-                                .setLogId(orderLog.getId())
-                                .setAsnId(item.getOrderId());
-                        logs.add(itemLog);
-                    });
-
-                    if (!asnOrderItemLogService.saveBatch(logs)) {
-                        throw new CoolException("鍗曟嵁鏄庣粏鍘嗗彶妗d繚瀛樺け璐ワ紒锛�");
+                } else {
+                    if (type.equals(OrderType.ORDER_OUT.type)) {
+                        order.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_TASK_DONE.val);
+                        order.setQty(order.getWorkQty());
                     }
+                    long existingLogId = one.getId();
+                    BeanUtils.copyProperties(order, one);
+                    one.setId(existingLogId);
+                    one.setAsnId(order.getId());
+                    if (!asnOrderLogService.updateById(one)) {
+                        throw new CoolException("涓诲崟鍘嗗彶妗f洿鏂板け璐ワ紒锛�");
+                    }
+                    orderLog = one;
+                    asnOrderItemLogService.remove(new LambdaQueryWrapper<AsnOrderItemLog>().eq(AsnOrderItemLog::getLogId, existingLogId));
+                }
+
+                List<AsnOrderItemLog> logs = new ArrayList<>();
+                List<WkOrderItem> items = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>()
+                        .eq(WkOrderItem::getOrderId, order.getId()));
+                items.forEach(item -> {
+                    AsnOrderItemLog itemLog = new AsnOrderItemLog();
+                    BeanUtils.copyProperties(item, itemLog);
+                    itemLog.setAsnItemId(item.getId())
+                            .setId(null)
+                            .setMatnrId(item.getMatnrId())
+                            .setLogId(orderLog.getId())
+                            .setAsnId(item.getOrderId());
+                    logs.add(itemLog);
+                });
+                if (!asnOrderItemLogService.saveBatch(logs)) {
+                    throw new CoolException("鍗曟嵁鏄庣粏鍘嗗彶妗d繚瀛樺け璐ワ紒锛�");
+                }
 
                     //鏇存柊PO/DO鍗曟墽琛岀姸鎬�
                     if (type.equals(OrderType.ORDER_IN.type)) {
@@ -200,7 +222,8 @@
                                     .set(Transfer::getExceStatus, AsnExceStatus.ASN_EXCE_STATUS_TASK_DONE.val))) {
                                 throw new CoolException("璋冩嫈鍗曠姸鎬佷慨鏀瑰け璐ワ紒锛�");
                             }
-                            return;
+                            removeOriginalOrderAndItems(order);
+                            continue;
                         } else {
                             if (!Objects.isNull(order.getPoId())) {
                                 purchaseService.update(new LambdaUpdateWrapper<Purchase>()
@@ -215,7 +238,8 @@
                                 throw new CoolException("鍗曟嵁鐘舵�佹洿鏂板け璐ワ紒锛�");
                             }
                             //濡傛灉涓鸿皟鎷斿崟鎹繚鐣�
-                            return;
+                            removeOriginalOrderAndItems(order);
+                            continue;
                         } else {
                             if (!Objects.isNull(order.getPoId())) {
                                 deliveryService.update(new LambdaUpdateWrapper<Delivery>()
@@ -224,16 +248,38 @@
                             }
                         }
                     }
-                }
-
-//                if (!asnOrderItemService.remove(new LambdaQueryWrapper<WkOrderItem>()
-//                        .eq(WkOrderItem::getOrderId, order.getId()))) {
-//                    throw new CoolException("鍘熷崟鎹槑缁嗗垹闄ゅけ璐ワ紒锛�");
-//                }
-//                if (!this.asnOrderService.removeById(order.getId())) {
-//                    throw new CoolException("鍘熷崟鎹垹闄ゅけ璐ワ紒锛�");
-//                }
-//            }
+                    // 杞叆鍘嗗彶鍚庡垹闄ゅ師鍗曞強鏄庣粏
+                    removeOriginalOrderAndItems(order);
         }
     }
+
+    /** 鍒犻櫎鍘熷叆搴�/鍑哄簱閫氱煡鍗曞強鏄庣粏锛堣浆鍏ュ巻鍙插悗璋冪敤锛� */
+    private void removeOriginalOrderAndItems(WkOrder order) {
+        if (!asnOrderItemService.remove(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, order.getId()))) {
+            throw new CoolException("鍘熷崟鎹槑缁嗗垹闄ゅけ璐ワ紒锛�");
+        }
+        if (!asnOrderService.removeById(order.getId())) {
+            throw new CoolException("鍘熷崟鎹垹闄ゅけ璐ワ紒锛�");
+        }
+    }
+
+    /** 姣忔湀1鍙峰噷鏅ㄦ墽琛岋細鐗╃悊鍒犻櫎涓婁笂涓湀涔嬪墠宸茶閫昏緫鍒犻櫎鐨勫叆搴�/鍑哄簱閫氱煡鍗曞強鏄庣粏 */
+    @Scheduled(cron = "0 0 0 1 * ?")
+    @Transactional(rollbackFor = Exception.class)
+    public void physicalDeleteLogicDeletedOrders() {
+        LocalDate startOfTwoMonthsAgo = LocalDate.now().minusMonths(2).withDayOfMonth(1);
+        Date before = Date.from(startOfTwoMonthsAgo.atStartOfDay(ZoneId.systemDefault()).toInstant());
+        List<Long> ids = asnOrderMapper.selectLogicDeletedOrderIdsBefore(before);
+        if (ids == null || ids.isEmpty()) {
+            return;
+        }
+        final int batchSize = 500;
+        for (int i = 0; i < ids.size(); i += batchSize) {
+            int to = Math.min(i + batchSize, ids.size());
+            List<Long> batch = ids.subList(i, to);
+            asnOrderItemMapper.physicalDeleteByOrderIds(batch);
+            asnOrderMapper.physicalDeleteByIds(batch);
+        }
+        log.info("鐗╃悊鍒犻櫎涓婁笂涓湀涔嬪墠宸查�昏緫鍒犻櫎鐨勫師鍗曞強鏄庣粏锛岃鍗曟暟锛歿}", ids.size());
+    }
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/CloudWmsNotifySchedule.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/CloudWmsNotifySchedule.java
new file mode 100644
index 0000000..1cd040a
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/CloudWmsNotifySchedule.java
@@ -0,0 +1,123 @@
+package com.vincent.rsf.server.manager.schedules;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.vincent.rsf.server.api.controller.erp.params.InOutResultReportParam;
+import com.vincent.rsf.server.api.controller.erp.params.InventoryAdjustReportParam;
+import com.vincent.rsf.server.api.service.CloudWmsReportService;
+import com.vincent.rsf.server.manager.entity.CloudWmsNotifyLog;
+import com.vincent.rsf.server.manager.service.CloudWmsNotifyLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/** 浜戜粨涓婃姤瀹氭椂浠诲姟 */
+@Slf4j
+@Component
+public class CloudWmsNotifySchedule {
+
+    private static final int BATCH_LIMIT = 50;
+
+    @Autowired
+    private CloudWmsNotifyLogService cloudWmsNotifyLogService;
+    @Autowired
+    private CloudWmsReportService cloudWmsReportService;
+    @Autowired
+    private ObjectMapper objectMapper;
+
+    @Scheduled(cron = "0/30 * * * * ?")
+    public void syncCloudWmsNotify() {
+        List<CloudWmsNotifyLog> pending = cloudWmsNotifyLogService.listPending(BATCH_LIMIT, 999);
+        if (pending.isEmpty()) {
+            return;
+        }
+        long nowMs = System.currentTimeMillis();
+        for (CloudWmsNotifyLog logRecord : pending) {
+            try {
+                Integer maxRetry = logRecord.getMaxRetryCount();
+                Integer intervalSeconds = logRecord.getRetryIntervalSeconds();
+                if (maxRetry == null || intervalSeconds == null || intervalSeconds <= 0) {
+                    continue;
+                }
+                if (logRecord.getRetryCount() != null && logRecord.getRetryCount() >= maxRetry) {
+                    continue;
+                }
+                if (logRecord.getLastNotifyTime() != null) {
+                    long elapsed = (nowMs - logRecord.getLastNotifyTime().getTime()) / 1000;
+                    if (elapsed < intervalSeconds) {
+                        continue;
+                    }
+                }
+                processOne(logRecord);
+            } catch (Exception e) {
+                log.warn("浜戜粨涓婃姤瀹氭椂浠诲姟澶勭悊鍗曟潯寮傚父锛宨d={}锛宐izRef={}锛歿}", logRecord.getId(), logRecord.getBizRef(), e.getMessage());
+            }
+        }
+    }
+
+    private void processOne(CloudWmsNotifyLog logRecord) {
+        String reportType = logRecord.getReportType();
+        String requestBody = logRecord.getRequestBody();
+        Date now = new Date();
+        int nextRetry = (logRecord.getRetryCount() == null ? 0 : logRecord.getRetryCount()) + 1;
+        int effectiveMaxRetry = logRecord.getMaxRetryCount();
+
+        try {
+            if (cloudWmsNotifyLogService.getReportTypeInOutResult().equals(reportType)) {
+                InOutResultReportParam param = objectMapper.readValue(requestBody, InOutResultReportParam.class);
+                Map<String, Object> res = cloudWmsReportService.reportInOutResult(param);
+                updateAfterNotify(logRecord, requestBody, res, nextRetry, now, effectiveMaxRetry);
+            } else if (cloudWmsNotifyLogService.getReportTypeInventoryAdjust().equals(reportType)) {
+                InventoryAdjustReportParam param = objectMapper.readValue(requestBody, InventoryAdjustReportParam.class);
+                Map<String, Object> res = cloudWmsReportService.reportInventoryAdjust(param);
+                updateAfterNotify(logRecord, requestBody, res, nextRetry, now, effectiveMaxRetry);
+            } else {
+                log.warn("鏈煡涓婃姤绫诲瀷锛宨d={}锛宺eportType={}", logRecord.getId(), reportType);
+                return;
+            }
+        } catch (JsonProcessingException e) {
+            log.warn("浜戜粨涓婃姤璇锋眰浣撳弽搴忓垪鍖栧け璐ワ紝id={}锛歿}", logRecord.getId(), e.getMessage());
+            setFailResult(logRecord, requestBody, "鍙嶅簭鍒楀寲澶辫触: " + e.getMessage(), nextRetry, now, effectiveMaxRetry);
+        } catch (Exception e) {
+            log.warn("浜戜粨涓婃姤璇锋眰澶辫触锛宨d={}锛宐izRef={}锛歿}", logRecord.getId(), logRecord.getBizRef(), e.getMessage());
+            setFailResult(logRecord, requestBody, "璇锋眰寮傚父: " + e.getMessage(), nextRetry, now, effectiveMaxRetry);
+        }
+    }
+
+    private void updateAfterNotify(CloudWmsNotifyLog logRecord, String requestBody, Map<String, Object> res, int nextRetry, Date now, int effectiveMaxRetry) {
+        String responseJson;
+        try {
+            responseJson = res != null ? objectMapper.writeValueAsString(res) : "null";
+        } catch (JsonProcessingException e) {
+            responseJson = String.valueOf(res);
+        }
+        Object codeObj = res != null ? res.get("code") : null;
+        boolean success = Integer.valueOf(200).equals(codeObj);
+        int status = success ? cloudWmsNotifyLogService.getNotifyStatusSuccess() : cloudWmsNotifyLogService.getNotifyStatusPending();
+        if (!success && nextRetry >= effectiveMaxRetry) {
+            status = cloudWmsNotifyLogService.getNotifyStatusFail();
+        }
+        logRecord.setLastRequestBody(requestBody);
+        logRecord.setLastResponseBody(responseJson);
+        logRecord.setLastNotifyTime(now);
+        logRecord.setRetryCount(nextRetry);
+        logRecord.setNotifyStatus(status);
+        logRecord.setUpdateTime(now);
+        cloudWmsNotifyLogService.updateById(logRecord);
+    }
+
+    private void setFailResult(CloudWmsNotifyLog logRecord, String requestBody, String errorMsg, int nextRetry, Date now, int effectiveMaxRetry) {
+        logRecord.setLastRequestBody(requestBody);
+        logRecord.setLastResponseBody(errorMsg);
+        logRecord.setLastNotifyTime(now);
+        logRecord.setRetryCount(nextRetry);
+        logRecord.setNotifyStatus(nextRetry >= effectiveMaxRetry ? cloudWmsNotifyLogService.getNotifyStatusFail() : cloudWmsNotifyLogService.getNotifyStatusPending());
+        logRecord.setUpdateTime(now);
+        cloudWmsNotifyLogService.updateById(logRecord);
+    }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java
index 84953c7..6ce0261 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/schedules/TaskSchedules.java
@@ -648,6 +648,9 @@
                                         if (!Boolean.parseBoolean(allowChang.getVal())) {
                                             if (order.getAnfme().compareTo(order.getQty()) == 0) {
                                                 order.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_DONE.val);
+                                                if (order.getQty() == null || order.getQty().compareTo(0.0) == 0) {
+                                                    order.setQty(order.getWorkQty() != null ? order.getWorkQty() : 0.0);
+                                                }
                                                 if (!asnOrderService.updateById(order)) {
                                                     logger.error("鍑哄簱鍗曟洿鏂扮姸鎬佸け璐ャ�傝鍗旾D锛歿}锛岃鍗曠紪鐮侊細{}", order.getId(), order.getCode());
                                                 }
@@ -655,6 +658,9 @@
                                         } else {
                                             if (order.getAnfme().compareTo(order.getQty()) <= 0) {
                                                 order.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_DONE.val);
+                                                if (order.getQty() == null || order.getQty().compareTo(0.0) == 0) {
+                                                    order.setQty(order.getWorkQty() != null ? order.getWorkQty() : 0.0);
+                                                }
                                                 if (!asnOrderService.updateById(order)) {
                                                     logger.error("鍑哄簱鍗曟洿鏂扮姸鎬佸け璐ャ�傝鍗旾D锛歿}锛岃鍗曠紪鐮侊細{}", order.getId(), order.getCode());
                                                 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/CloudWmsNotifyLogService.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/CloudWmsNotifyLogService.java
new file mode 100644
index 0000000..a2d4180
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/CloudWmsNotifyLogService.java
@@ -0,0 +1,29 @@
+package com.vincent.rsf.server.manager.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.vincent.rsf.server.manager.entity.CloudWmsNotifyLog;
+
+import java.util.List;
+
+/** 浜戜粨涓婃姤寰呭姙 */
+public interface CloudWmsNotifyLogService extends IService<CloudWmsNotifyLog> {
+
+    List<CloudWmsNotifyLog> listPending(int limit, int maxRetry);
+
+    void fillFromConfig(CloudWmsNotifyLog log);
+
+    /** 涓婃姤绫诲瀷锛氬叆鍑哄簱缁撴灉锛堢郴缁熼厤缃紭鍏堬紝缂虹渷 IN_OUT_RESULT锛� */
+    String getReportTypeInOutResult();
+
+    /** 涓婃姤绫诲瀷锛氬簱瀛樿皟鏁达紙绯荤粺閰嶇疆浼樺厛锛岀己鐪� INVENTORY_ADJUST锛� */
+    String getReportTypeInventoryAdjust();
+
+    /** 閫氱煡鐘舵�侊細寰呴�氱煡锛堢郴缁熼厤缃紭鍏堬紝缂虹渷 0锛� */
+    int getNotifyStatusPending();
+
+    /** 閫氱煡鐘舵�侊細宸叉垚鍔燂紙绯荤粺閰嶇疆浼樺厛锛岀己鐪� 1锛� */
+    int getNotifyStatusSuccess();
+
+    /** 閫氱煡鐘舵�侊細澶辫触锛堢郴缁熼厤缃紭鍏堬紝缂虹渷 2锛� */
+    int getNotifyStatusFail();
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/AsnOrderServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/AsnOrderServiceImpl.java
index d0f6cab..45e209c 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/AsnOrderServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/AsnOrderServiceImpl.java
@@ -510,30 +510,38 @@
 //            throw new CoolException("鏀惰揣鏁伴噺涓嶈兘涓洪浂锛侊紒");
 //        }
         WkOrder order = this.getById(asrder.getId());
-        AsnOrderLog orderLog = new AsnOrderLog();
-//        order.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_TASK_DONE.val);
-        BeanUtils.copyProperties(order, orderLog);
-        orderLog.setId(null);
-        orderLog.setAsnId(order.getId());
-
-//        if (!this.saveOrUpdate(order)) {
-//            throw new CoolException("鐘舵�佷慨鏀瑰け璐ワ紒锛�");
-//        }
-//        orderLog.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_TASK_CLOSE.val);
-        if (!asnOrderLogService.save(orderLog)) {
-            throw new CoolException("涓诲崟鍘嗗彶妗f坊鍔犲け璐ワ紒锛�");
+        AsnOrderLog one = asnOrderLogService.getOne(new LambdaQueryWrapper<AsnOrderLog>().eq(AsnOrderLog::getCode, order.getCode()), false);
+        AsnOrderLog orderLog;
+        if (Objects.isNull(one)) {
+            orderLog = new AsnOrderLog();
+            BeanUtils.copyProperties(order, orderLog);
+            orderLog.setId(null);
+            orderLog.setAsnId(order.getId());
+            if (!asnOrderLogService.save(orderLog)) {
+                throw new CoolException("涓诲崟鍘嗗彶妗f坊鍔犲け璐ワ紒锛�");
+            }
+        } else {
+            long existingLogId = one.getId();
+            BeanUtils.copyProperties(order, one);
+            one.setId(existingLogId);
+            one.setAsnId(order.getId());
+            if (!asnOrderLogService.updateById(one)) {
+                throw new CoolException("涓诲崟鍘嗗彶妗f洿鏂板け璐ワ紒锛�");
+            }
+            orderLog = one;
+            asnOrderItemLogService.remove(new LambdaQueryWrapper<AsnOrderItemLog>().eq(AsnOrderItemLog::getLogId, existingLogId));
         }
         List<AsnOrderItemLog> logs = new ArrayList<>();
         List<WkOrderItem> items = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, order.getId()));
         items.forEach(item -> {
             AsnOrderItemLog itemLog = new AsnOrderItemLog();
             BeanUtils.copyProperties(item, itemLog);
-            itemLog.setAsnItemId(itemLog.getId())
+            itemLog.setAsnItemId(item.getId())
+                    .setId(null)
                     .setLogId(orderLog.getId())
                     .setAsnId(item.getOrderId());
             logs.add(itemLog);
         });
-
         if (!asnOrderItemLogService.saveBatch(logs)) {
             throw new CoolException("閫氱煡鍗曟槑缁嗗巻鍙叉。淇濆瓨澶辫触锛侊紒");
         }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CloudWmsNotifyLogServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CloudWmsNotifyLogServiceImpl.java
new file mode 100644
index 0000000..ab82ef8
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/CloudWmsNotifyLogServiceImpl.java
@@ -0,0 +1,90 @@
+package com.vincent.rsf.server.manager.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.vincent.rsf.server.manager.entity.CloudWmsNotifyLog;
+import com.vincent.rsf.server.manager.mapper.CloudWmsNotifyLogMapper;
+import com.vincent.rsf.server.manager.service.CloudWmsNotifyLogService;
+import com.vincent.rsf.server.system.constant.GlobalConfigCode;
+import com.vincent.rsf.server.system.entity.Config;
+import com.vincent.rsf.server.system.service.ConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class CloudWmsNotifyLogServiceImpl extends ServiceImpl<CloudWmsNotifyLogMapper, CloudWmsNotifyLog> implements CloudWmsNotifyLogService {
+
+    @Autowired
+    private ConfigService configService;
+
+    @Override
+    public List<CloudWmsNotifyLog> listPending(int limit, int maxRetry) {
+        Page<CloudWmsNotifyLog> page = new Page<>(1, Math.max(1, limit));
+        LambdaQueryWrapper<CloudWmsNotifyLog> wrapper = new LambdaQueryWrapper<CloudWmsNotifyLog>()
+                .eq(CloudWmsNotifyLog::getNotifyStatus, getNotifyStatusPending())
+                .lt(CloudWmsNotifyLog::getRetryCount, maxRetry)
+                .orderByAsc(CloudWmsNotifyLog::getId);
+        return page(page, wrapper).getRecords();
+    }
+
+    @Override
+    public String getReportTypeInOutResult() {
+        return getConfigString(GlobalConfigCode.CLOUD_WMS_REPORT_TYPE_IN_OUT_RESULT, CloudWmsNotifyLog.REPORT_TYPE_IN_OUT_RESULT);
+    }
+
+    @Override
+    public String getReportTypeInventoryAdjust() {
+        return getConfigString(GlobalConfigCode.CLOUD_WMS_REPORT_TYPE_INVENTORY_ADJUST, CloudWmsNotifyLog.REPORT_TYPE_INVENTORY_ADJUST);
+    }
+
+    @Override
+    public int getNotifyStatusPending() {
+        return getConfigInt(GlobalConfigCode.CLOUD_WMS_NOTIFY_STATUS_PENDING, CloudWmsNotifyLog.NOTIFY_STATUS_PENDING);
+    }
+
+    @Override
+    public int getNotifyStatusSuccess() {
+        return getConfigInt(GlobalConfigCode.CLOUD_WMS_NOTIFY_STATUS_SUCCESS, CloudWmsNotifyLog.NOTIFY_STATUS_SUCCESS);
+    }
+
+    @Override
+    public int getNotifyStatusFail() {
+        return getConfigInt(GlobalConfigCode.CLOUD_WMS_NOTIFY_STATUS_FAIL, CloudWmsNotifyLog.NOTIFY_STATUS_FAIL);
+    }
+
+    private String getConfigString(String flag, String defaultVal) {
+        Config c = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, flag).last("LIMIT 1"));
+        if (c != null && c.getVal() != null && !c.getVal().isEmpty()) {
+            return c.getVal().trim();
+        }
+        return defaultVal;
+    }
+
+    private int getConfigInt(String flag, int defaultVal) {
+        Integer v = getConfigInt(flag);
+        return v != null ? v : defaultVal;
+    }
+
+    @Override
+    public void fillFromConfig(CloudWmsNotifyLog log) {
+        Integer maxRetry = getConfigInt(GlobalConfigCode.CLOUD_WMS_NOTIFY_MAX_RETRY);
+        Integer interval = getConfigInt(GlobalConfigCode.CLOUD_WMS_NOTIFY_RETRY_INTERVAL_SECONDS);
+        log.setMaxRetryCount(maxRetry);
+        log.setRetryIntervalSeconds(interval);
+    }
+
+    /** 杩斿洖 null 琛ㄧず鏈厤缃垨瑙f瀽澶辫触 */
+    private Integer getConfigInt(String flag) {
+        try {
+            Config c = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, flag).last("LIMIT 1"));
+            if (c != null && c.getVal() != null && !c.getVal().isEmpty()) {
+                return Integer.parseInt(c.getVal().trim());
+            }
+        } catch (Exception ignored) {
+        }
+        return null;
+    }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
index e2cf9c4..b3f87b7 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -1066,6 +1066,9 @@
             return R.error("鍑哄簱鍗曚笉瀛樺湪锛侊紒");
         }
         order.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_DONE.val);
+        if (order.getQty() == null || order.getQty().compareTo(0.0) == 0) {
+            order.setQty(order.getWorkQty() != null ? order.getWorkQty() : 0.0);
+        }
         if (!this.updateById(order)) {
             throw new CoolException("瀹屾垚鍑哄簱鍗曞け璐ワ紒锛�");
         }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/ReviseLogServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/ReviseLogServiceImpl.java
index 1d8a278..79b31c8 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/ReviseLogServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/ReviseLogServiceImpl.java
@@ -4,6 +4,8 @@
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.vincent.rsf.framework.common.R;
 import com.vincent.rsf.framework.exception.CoolException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.vincent.rsf.server.api.controller.erp.params.InventoryAdjustReportParam;
 import com.vincent.rsf.server.manager.controller.params.ReviseLogParams;
 import com.vincent.rsf.server.manager.entity.*;
 import com.vincent.rsf.server.manager.enums.AsnExceStatus;
@@ -13,6 +15,7 @@
 import com.vincent.rsf.server.manager.mapper.ReviseLogMapper;
 import com.vincent.rsf.server.manager.service.*;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -27,6 +30,7 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 
+@Slf4j
 @Service("reviseLogService")
 public class ReviseLogServiceImpl extends ServiceImpl<ReviseLogMapper, ReviseLog> implements ReviseLogService {
 
@@ -50,6 +54,15 @@
 
     @Autowired
     private OutStockItemService outStockItemService;
+
+    @Autowired
+    private WarehouseService warehouseService;
+
+    @Autowired
+    private CloudWmsNotifyLogService cloudWmsNotifyLogService;
+
+    @Autowired
+    private ObjectMapper objectMapper;
 
     /**
      * 搴撳瓨璋冩暣鍗曟槑缁嗘坊鍔�
@@ -203,6 +216,7 @@
             // 鍒犻櫎鍘熷簱浣嶇殑搴撳瓨鏄庣粏锛堝鏋滃瓨鍦級
             locItemService.remove(new LambdaQueryWrapper<LocItem>().eq(LocItem::getLocId, loc.getId()));
 
+            final Loc sourceLoc = loc;
             Loc finalLoc = loc;
             reviseItems.forEach(logItem -> {
                 LocItem locDetl = new LocItem();
@@ -220,6 +234,39 @@
                 if (!locItemService.save(locDetl)) {
                     throw new CoolException("搴撳瓨鏄庣粏淇濆瓨澶辫触锛侊紒");
                 }
+                // 9.2 搴撳瓨璋冩暣涓诲姩涓婃姤寰呭姙
+                try {
+                    String wareHouseId = null;
+                    if (finalLoc.getWarehouseId() != null) {
+                        Warehouse wh = warehouseService.getById(finalLoc.getWarehouseId());
+                        if (wh != null) {
+                            wareHouseId = wh.getCode();
+                        }
+                    }
+                    if (wareHouseId != null && logItem.getMatnrCode() != null && sourceLoc != null) {
+                        InventoryAdjustReportParam param = new InventoryAdjustReportParam()
+                                .setChangeType(3) // 3 绉诲簱
+                                .setWareHouseId(wareHouseId)
+                                .setSourceLocId(sourceLoc.getCode())
+                                .setTargetLocId(finalLoc.getCode())
+                                .setMatNr(logItem.getMatnrCode())
+                                .setQty(logItem.getReviseQty() != null ? String.valueOf(logItem.getReviseQty()) : "0");
+                        String requestBody = objectMapper.writeValueAsString(param);
+                        Date now = new Date();
+                        CloudWmsNotifyLog notifyLog = new CloudWmsNotifyLog()
+                                .setReportType(cloudWmsNotifyLogService.getReportTypeInventoryAdjust())
+                                .setRequestBody(requestBody)
+                                .setNotifyStatus(cloudWmsNotifyLogService.getNotifyStatusPending())
+                                .setRetryCount(0)
+                                .setBizRef("reviseId=" + revise.getId() + ",reviseLogItemId=" + logItem.getId())
+                                .setCreateTime(now)
+                                .setUpdateTime(now);
+                        cloudWmsNotifyLogService.fillFromConfig(notifyLog);
+                        cloudWmsNotifyLogService.save(notifyLog);
+                    }
+                } catch (Exception e) {
+                    log.warn("搴撳瓨璋冩暣涓婃姤寰呭姙钀藉簱澶辫触锛堜笉褰卞搷搴撳瓨淇濆瓨锛夛紝matNr={}锛歿}", logItem.getMatnrCode(), e.getMessage());
+                }
 
                 // 涓哄簱瀛樿皟鏁翠骇鐢熺殑搴撳瓨鍒涘缓瀵瑰簲鐨刉kOrderItem
                 // 閬嶅巻鎵�鏈夋湭瀹屾垚鐨勫嚭搴撳崟锛屾鏌ユ槸鍚﹂渶瑕佽繖浜涚墿鏂�
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
index 970c770..4e4640d 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
@@ -4,12 +4,14 @@
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.cfg.CoercionAction;
 import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
 import com.vincent.rsf.framework.common.Cools;
 import com.vincent.rsf.framework.common.DateUtils;
 import com.vincent.rsf.server.api.config.RemotesInfoProperties;
+import com.vincent.rsf.server.api.controller.erp.params.InOutResultReportParam;
 import com.vincent.rsf.server.api.controller.erp.params.TaskInParam;
 import com.vincent.rsf.server.api.entity.CommonResponse;
 import com.vincent.rsf.server.api.entity.constant.RcsConstant;
@@ -26,6 +28,7 @@
 import com.vincent.rsf.framework.exception.CoolException;
 import com.vincent.rsf.server.api.utils.LocUtils;
 import com.vincent.rsf.server.manager.controller.params.GenerateTaskParams;
+import com.vincent.rsf.server.manager.entity.CloudWmsNotifyLog;
 import com.vincent.rsf.server.manager.entity.*;
 import com.vincent.rsf.server.manager.mapper.TaskMapper;
 import com.vincent.rsf.server.manager.service.*;
@@ -124,6 +127,10 @@
     private RestTemplate restTemplate;
     @Autowired
     private RemotesInfoProperties.RcsApi rcsApi;
+    @Autowired
+    private CloudWmsNotifyLogService cloudWmsNotifyLogService;
+    @Autowired
+    private WarehouseService warehouseService;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -1627,6 +1634,8 @@
                 .set(Task::getTaskStatus, TaskStsType.WAVE_SEED.id))) {
             throw new CoolException("搴撳瓨鐘舵�佹洿鏂板け璐ワ紒锛�");
         }
+        // 9.1 鍏�/鍑哄簱缁撴灉涓婃姤锛氬嚭搴撳畬鎴愬悗閫氱煡浜戜粨
+        reportInOutResultToCloud(task, loc, taskItems, null, false);
 
 //        if (task.getTaskType().equals(TaskType.TASK_TYPE_PICK_AGAIN_OUT.type) || task.getTaskType().equals(TaskType.TASK_TYPE_CHECK_OUT.type)) {
 //            if (!this.update(new LambdaUpdateWrapper<Task>()
@@ -2240,6 +2249,8 @@
         if (!this.update(new LambdaUpdateWrapper<Task>().eq(Task::getId, task.getId()).set(Task::getTaskStatus, TaskStsType.UPDATED_IN.id))) {
             throw new CoolException("浠诲姟鐘舵�佷慨鏀瑰け璐ワ紒锛�");
         }
+        // 9.1 鍏�/鍑哄簱缁撴灉涓婃姤锛氬叆搴撳畬鎴愬悗閫氱煡浜戜粨
+        reportInOutResultToCloud(task, loc, taskItems, pkinItemIds, true);
     }
 
     /**
@@ -2408,4 +2419,69 @@
             }
         }
     }
+
+    /**
+     * 9.1 鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙
+     * @param isInbound true 鍏ュ簱瀹屾垚锛宖alse 鍑哄簱瀹屾垚
+     * @param pkinItemIds 鍏ュ簱鏃剁粍鎵樻槑缁� ID 闆嗗悎锛岀敤浜庢煡 asnCode 浣滀负 orderNo锛涘嚭搴撴椂浼� null锛岀敤 taskItem.platOrderCode
+     */
+    private void reportInOutResultToCloud(Task task, Loc loc, List<TaskItem> taskItems, Set<Long> pkinItemIds, boolean isInbound) {
+        try {
+            String locId = isInbound ? task.getTargLoc() : task.getOrgLoc();
+            String wareHouseId = null;
+            if (loc.getWarehouseId() != null) {
+                Warehouse wh = warehouseService.getById(loc.getWarehouseId());
+                if (wh != null) {
+                    wareHouseId = wh.getCode();
+                }
+            }
+            if (wareHouseId == null) {
+                log.warn("鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙璺宠繃锛氫粨搴撶紪鐮佷负绌猴紝taskId={}", task.getId());
+                return;
+            }
+            Map<Long, String> sourceToOrderNo = new HashMap<>();
+            if (isInbound && pkinItemIds != null && !pkinItemIds.isEmpty()) {
+                List<WaitPakinItem> pakinItems = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>().in(WaitPakinItem::getId, pkinItemIds));
+                for (WaitPakinItem p : pakinItems) {
+                    if (p.getAsnCode() != null) {
+                        sourceToOrderNo.put(p.getId(), p.getAsnCode());
+                    }
+                }
+            }
+            ObjectMapper om = new ObjectMapper();
+            Date now = new Date();
+            for (TaskItem item : taskItems) {
+                String orderNo = isInbound ? sourceToOrderNo.get(item.getSource()) : (item.getPlatOrderCode() != null ? item.getPlatOrderCode() : item.getPlatWorkCode());
+                if (orderNo == null || item.getMatnrCode() == null) {
+                    continue;
+                }
+                InOutResultReportParam param = new InOutResultReportParam()
+                        .setOrderNo(orderNo)
+                        .setPlanNo(item.getPlatWorkCode())
+                        .setLineId(item.getPlatItemId())
+                        .setWareHouseId(wareHouseId)
+                        .setLocId(locId)
+                        .setMatNr(item.getMatnrCode())
+                        .setQty(item.getAnfme() != null ? String.valueOf(item.getAnfme()) : "0")
+                        .setBatch(item.getBatch());
+                try {
+                    String requestBody = om.writeValueAsString(param);
+                    CloudWmsNotifyLog notifyLog = new CloudWmsNotifyLog()
+                            .setReportType(cloudWmsNotifyLogService.getReportTypeInOutResult())
+                            .setRequestBody(requestBody)
+                            .setNotifyStatus(cloudWmsNotifyLogService.getNotifyStatusPending())
+                            .setRetryCount(0)
+                            .setBizRef("taskId=" + task.getId() + ",orderNo=" + orderNo)
+                            .setCreateTime(now)
+                            .setUpdateTime(now);
+                    cloudWmsNotifyLogService.fillFromConfig(notifyLog);
+                    cloudWmsNotifyLogService.save(notifyLog);
+                } catch (JsonProcessingException e) {
+                    log.warn("鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙钀藉簱澶辫触锛堜笉褰卞搷涓绘祦绋嬶級锛宼askId={}锛宱rderNo={}锛歿}", task.getId(), orderNo, e.getMessage());
+                }
+            }
+        } catch (Exception e) {
+            log.warn("鍏�/鍑哄簱缁撴灉涓婃姤寰呭姙澶辫触锛宼askId={}锛宨sInbound={}锛歿}", task.getId(), isInbound, e.getMessage());
+        }
+    }
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/constant/GlobalConfigCode.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/constant/GlobalConfigCode.java
index a373bea..12e22dc 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/system/constant/GlobalConfigCode.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/constant/GlobalConfigCode.java
@@ -27,4 +27,19 @@
 
     public final static String ALLOW_PUB_TASK = "AllowPubTask";
 
+    /** 浜戜粨涓婃姤鏈�澶ч噸璇曟鏁� */
+    public final static String CLOUD_WMS_NOTIFY_MAX_RETRY = "CLOUD_WMS_NOTIFY_MAX_RETRY";
+    /** 浜戜粨涓婃姤閲嶈瘯闂撮殧绉掓暟 */
+    public final static String CLOUD_WMS_NOTIFY_RETRY_INTERVAL_SECONDS = "CLOUD_WMS_NOTIFY_RETRY_INTERVAL_SECONDS";
+    /** 浜戜粨涓婃姤绫诲瀷锛氬叆鍑哄簱缁撴灉 */
+    public final static String CLOUD_WMS_REPORT_TYPE_IN_OUT_RESULT = "CLOUD_WMS_REPORT_TYPE_IN_OUT_RESULT";
+    /** 浜戜粨涓婃姤绫诲瀷锛氬簱瀛樿皟鏁� */
+    public final static String CLOUD_WMS_REPORT_TYPE_INVENTORY_ADJUST = "CLOUD_WMS_REPORT_TYPE_INVENTORY_ADJUST";
+    /** 浜戜粨閫氱煡鐘舵�侊細寰呴�氱煡 */
+    public final static String CLOUD_WMS_NOTIFY_STATUS_PENDING = "CLOUD_WMS_NOTIFY_STATUS_PENDING";
+    /** 浜戜粨閫氱煡鐘舵�侊細宸叉垚鍔� */
+    public final static String CLOUD_WMS_NOTIFY_STATUS_SUCCESS = "CLOUD_WMS_NOTIFY_STATUS_SUCCESS";
+    /** 浜戜粨閫氱煡鐘舵�侊細澶辫触 */
+    public final static String CLOUD_WMS_NOTIFY_STATUS_FAIL = "CLOUD_WMS_NOTIFY_STATUS_FAIL";
+
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/OpenApiAppController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/OpenApiAppController.java
new file mode 100644
index 0000000..a4bddc0
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/OpenApiAppController.java
@@ -0,0 +1,78 @@
+package com.vincent.rsf.server.system.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.vincent.rsf.framework.common.R;
+import com.vincent.rsf.server.common.domain.BaseParam;
+import com.vincent.rsf.server.common.domain.PageParam;
+import com.vincent.rsf.server.system.entity.OpenApiApp;
+import com.vincent.rsf.server.system.service.OpenApiAppService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+import java.util.Map;
+
+@RestController
+public class OpenApiAppController extends BaseController {
+
+    @Autowired
+    private OpenApiAppService openApiAppService;
+
+    @PreAuthorize("hasAuthority('system:openApiApp:list')")
+    @PostMapping("/openApiApp/page")
+    public R page(@RequestBody Map<String, Object> map) {
+        BaseParam baseParam = buildParam(map, BaseParam.class);
+        PageParam<OpenApiApp, BaseParam> pageParam = new PageParam<>(baseParam, OpenApiApp.class);
+        LambdaQueryWrapper<OpenApiApp> wrapper = new LambdaQueryWrapper<>();
+        wrapper.orderByDesc(OpenApiApp::getId);
+        Page<OpenApiApp> page = openApiAppService.page(pageParam, wrapper);
+        return R.ok().add(page);
+    }
+
+    @PreAuthorize("hasAuthority('system:openApiApp:list')")
+    @PostMapping("/openApiApp/list")
+    public R list(@RequestBody Map<String, Object> map) {
+        return R.ok().add(openApiAppService.list(new LambdaQueryWrapper<OpenApiApp>().orderByDesc(OpenApiApp::getId)));
+    }
+
+    @PreAuthorize("hasAuthority('system:openApiApp:list')")
+    @PostMapping("/openApiApp/many/{ids}")
+    public R many(@PathVariable String[] ids) {
+        return R.ok().add(openApiAppService.listByIds(Arrays.asList(ids)));
+    }
+
+    @PreAuthorize("hasAuthority('system:openApiApp:list')")
+    @GetMapping("/openApiApp/{id}")
+    public R get(@PathVariable String id) {
+        return R.ok().add(openApiAppService.getById(id));
+    }
+
+    @PreAuthorize("hasAuthority('system:openApiApp:save')")
+    @PostMapping("/openApiApp/save")
+    public R save(@RequestBody OpenApiApp app) {
+        if (openApiAppService.save(app)) {
+            return R.ok("Save Success").add(app);
+        }
+        return R.error("Save Fail");
+    }
+
+    @PreAuthorize("hasAuthority('system:openApiApp:update')")
+    @PostMapping("/openApiApp/update")
+    public R update(@RequestBody OpenApiApp app) {
+        if (openApiAppService.updateById(app)) {
+            return R.ok("Update Success").add(app);
+        }
+        return R.error("Update Fail");
+    }
+
+    @PreAuthorize("hasAuthority('system:openApiApp:remove')")
+    @PostMapping("/openApiApp/remove/{ids}")
+    public R remove(@PathVariable String[] ids) {
+        if (openApiAppService.removeByIds(Arrays.asList(ids))) {
+            return R.ok("Remove Success");
+        }
+        return R.error("Remove Fail");
+    }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/OpenApiApp.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/OpenApiApp.java
new file mode 100644
index 0000000..3c434d0
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/OpenApiApp.java
@@ -0,0 +1,34 @@
+package com.vincent.rsf.server.system.entity;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@TableName("open_api_app")
+public class OpenApiApp implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("搴旂敤ID")
+    @TableId(value = "id")
+    private String id;
+
+    @ApiModelProperty("搴旂敤瀵嗛挜")
+    private String screct;
+
+    @ApiModelProperty("搴旂敤鍚嶇О")
+    private String name;
+
+    @ApiModelProperty("搴旂敤URL")
+    private String url;
+
+    @ApiModelProperty("鏄惁鍚敤 0鏈惎鐢� 1鍚敤")
+    private Integer enable;
+
+    @ApiModelProperty("绉熸埛id")
+    private Long tenantId;
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/OpenApiAppMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/OpenApiAppMapper.java
new file mode 100644
index 0000000..c50dafb
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/OpenApiAppMapper.java
@@ -0,0 +1,11 @@
+package com.vincent.rsf.server.system.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.vincent.rsf.server.system.entity.OpenApiApp;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+@Mapper
+@Repository
+public interface OpenApiAppMapper extends BaseMapper<OpenApiApp> {
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/OpenApiAppService.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/OpenApiAppService.java
new file mode 100644
index 0000000..bdf7c32
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/OpenApiAppService.java
@@ -0,0 +1,7 @@
+package com.vincent.rsf.server.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.vincent.rsf.server.system.entity.OpenApiApp;
+
+public interface OpenApiAppService extends IService<OpenApiApp> {
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/OpenApiAppServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/OpenApiAppServiceImpl.java
new file mode 100644
index 0000000..7ba3aed
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/OpenApiAppServiceImpl.java
@@ -0,0 +1,41 @@
+package com.vincent.rsf.server.system.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.vincent.rsf.server.system.entity.OpenApiApp;
+import com.vincent.rsf.server.system.mapper.OpenApiAppMapper;
+import com.vincent.rsf.server.system.service.OpenApiAppService;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+@Service
+public class OpenApiAppServiceImpl extends ServiceImpl<OpenApiAppMapper, OpenApiApp> implements OpenApiAppService {
+
+    @Resource
+    private PasswordEncoder passwordEncoder;
+
+    @Override
+    public boolean save(OpenApiApp entity) {
+        encodeScrectIfPlain(entity);
+        return super.save(entity);
+    }
+
+    @Override
+    public boolean updateById(OpenApiApp entity) {
+        encodeScrectIfPlain(entity);
+        return super.updateById(entity);
+    }
+
+    /** 鑻ヤ负鏄庢枃鍒欐敼涓� BCrypt 鍐嶅瓨搴擄紝渚夸簬 getToken 鏃剁敤 BCrypt 鏍¢獙 */
+    private void encodeScrectIfPlain(OpenApiApp entity) {
+        if (entity == null) {
+            return;
+        }
+        String s = entity.getScrect();
+        if (s == null || s.isEmpty() || s.startsWith("$2")) {
+            return;
+        }
+        entity.setScrect(passwordEncoder.encode(s));
+    }
+}
diff --git a/rsf-server/src/main/resources/application-dev.yml b/rsf-server/src/main/resources/application-dev.yml
index 93b2a83..4a1f465 100644
--- a/rsf-server/src/main/resources/application-dev.yml
+++ b/rsf-server/src/main/resources/application-dev.yml
@@ -5,6 +5,10 @@
 spring:
   application:
     name: @pom.artifactId@
+  cloud:
+    openfeign:
+      circuitbreaker:
+        enabled: true   # Feign 璋冪敤澶辫触鏃惰蛋 Fallback锛屽湪 Feign 鍐呯粺涓�杩斿洖閿欒鍝嶅簲
   mvc:
     static-path-pattern: /**
     path match:
@@ -13,6 +17,7 @@
     driver-class-name: com.mysql.cj.jdbc.Driver
     username: root
     url: jdbc:mysql://127.0.0.1:3306/rsf_jdxaj?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
+#    url: jdbc:mysql://127.0.0.1:3306/jdxajwms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
     password: 12345
     type: com.alibaba.druid.pool.DruidDataSource
     druid:
@@ -75,17 +80,20 @@
     #绔彛鍙�
     port: 8080
     #鎺ュ搧閾炬帴鍓嶇紑
-    pre-path: rsf-server
-    #鎺ュ彛鏄庣粏
+    pre-path: ""
+    # Feign 璋冪敤浜戜粨鏃剁殑鏍瑰湴鍧�銆備簯浠撴湭鎻愪緵 URL 鏃跺彲鐢ㄦ湰鏈烘ā鎷燂細http://127.0.0.1:8086/rsf-server锛堟湰鏈嶅姟鎻愪緵鐨� CloudWmsMockController锛�
+    base-url: http://127.0.0.1:8086/rsf-server
+    #鎺ュ彛鏄庣粏锛堢珛搴撲晶璇锋眰浜戜粨鏃朵娇鐢ㄧ殑璺緞锛�
     api:
-      #璐ㄦ涓婃姤鎺ュ彛
       notify-inspect: /report/inspect
+      in-out-result-path: /api/report/inOutResult
+      inventory-adjust-path: /api/report/inventoryAdjust
+      mat-sync-path: /api/mat/sync
   rcs:
     #閾炬帴
     host: http://10.10.10.200
     #绔彛
     port: 8088
-
 #浠撳簱鍔熻兘鍙傛暟閰嶇疆
 stock:
   #鏄惁鍏佽鎵撳嵃璐х墿鏍囩锛� 榛樿鍏佽鎵撳嵃锛屼篃鍙敱渚涘簲鍟嗘彁渚涙爣绛�
diff --git a/rsf-server/src/main/resources/mapper/manager/AsnOrderItemMapper.xml b/rsf-server/src/main/resources/mapper/manager/AsnOrderItemMapper.xml
index ec21369..367e9b1 100644
--- a/rsf-server/src/main/resources/mapper/manager/AsnOrderItemMapper.xml
+++ b/rsf-server/src/main/resources/mapper/manager/AsnOrderItemMapper.xml
@@ -134,4 +134,11 @@
             ) t
             ${ew.customSqlSegment}
     </select>
+
+    <delete id="physicalDeleteByOrderIds">
+        DELETE FROM man_asn_order_item
+        WHERE deleted = 1
+        AND order_id IN
+        <foreach collection="orderIds" item="id" open="(" separator="," close=")">#{id}</foreach>
+    </delete>
 </mapper>
diff --git a/rsf-server/src/main/resources/mapper/manager/AsnOrderMapper.xml b/rsf-server/src/main/resources/mapper/manager/AsnOrderMapper.xml
index fe6b5bd..cc2ce54 100644
--- a/rsf-server/src/main/resources/mapper/manager/AsnOrderMapper.xml
+++ b/rsf-server/src/main/resources/mapper/manager/AsnOrderMapper.xml
@@ -30,6 +30,19 @@
                 GROUP BY
                     `day_time`, task_type
             ) t
-        ${ew.customSqlSegment}
+            ${ew.customSqlSegment}
     </select>
+
+    <select id="selectLogicDeletedOrderIdsBefore" resultType="long">
+        SELECT id FROM man_asn_order
+        WHERE deleted = 1
+          AND update_time &lt; #{before}
+    </select>
+
+    <delete id="physicalDeleteByIds">
+        DELETE FROM man_asn_order
+        WHERE deleted = 1
+        AND id IN
+        <foreach collection="ids" item="id" open="(" separator="," close=")">#{id}</foreach>
+    </delete>
 </mapper>
diff --git a/rsf-server/src/test/java/com/vincent/rsf/server/common/security/SecurityDemoControllerTest.java b/rsf-server/src/test/java/com/vincent/rsf/server/common/security/SecurityDemoControllerTest.java
new file mode 100644
index 0000000..fecbfe4
--- /dev/null
+++ b/rsf-server/src/test/java/com/vincent/rsf/server/common/security/SecurityDemoControllerTest.java
@@ -0,0 +1,29 @@
+package com.vincent.rsf.server.common.security;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+/**
+ * 瀛楃涓插姞瀵嗕笌楠岃瘉銆�
+ *
+ */
+class SecurityDemoControllerTest {
+
+    @Test
+    void encryptAndVerify() {
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+        String raw = "wms001";
+
+        // 鍔犲瘑
+        String encoded = encoder.encode(raw);
+        System.out.println("瀵嗘枃: " + encoded);
+
+        // 楠岃瘉锛氬師鏂囦笌鍔犲瘑缁撴灉鍖归厤
+        boolean ok = encoder.matches(raw, encoded);
+        System.out.println("鍔犲瘑鍚庨獙璇�: " + ok);
+
+        // 楠岃瘉锛氶敊璇師鏂囦笉鍖归厤
+        boolean bad = encoder.matches("wrong", encoded);
+        System.out.println("閿欒鍘熸枃楠岃瘉: " + bad);
+    }
+}
diff --git a/version/db/cloud_wms_notify_config.sql b/version/db/cloud_wms_notify_config.sql
new file mode 100644
index 0000000..b926eb0
--- /dev/null
+++ b/version/db/cloud_wms_notify_config.sql
@@ -0,0 +1,7 @@
+INSERT INTO `sys_config` (`uuid`, `name`, `flag`, `type`, `val`, `content`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`)
+VALUES
+(UPPER(UUID()), '浜戜粨涓婃姤绫诲瀷-鍏ュ嚭搴撶粨鏋�', 'CLOUD_WMS_REPORT_TYPE_IN_OUT_RESULT', 3, 'IN_OUT_RESULT', '鍏ュ嚭搴撶粨鏋滀笂鎶ョ被鍨嬫爣璇�', 1, 0, 1, NULL, NOW(), NULL, NOW(), '涓� man_cloud_wms_notify_log.report_type 瀵瑰簲'),
+(UPPER(UUID()), '浜戜粨涓婃姤绫诲瀷-搴撳瓨璋冩暣', 'CLOUD_WMS_REPORT_TYPE_INVENTORY_ADJUST', 3, 'INVENTORY_ADJUST', '搴撳瓨璋冩暣涓婃姤绫诲瀷鏍囪瘑', 1, 0, 1, NULL, NOW(), NULL, NOW(), '涓� man_cloud_wms_notify_log.report_type 瀵瑰簲'),
+(UPPER(UUID()), '浜戜粨閫氱煡鐘舵��-寰呴�氱煡', 'CLOUD_WMS_NOTIFY_STATUS_PENDING', 2, '0', '寰呴�氱煡', 1, 0, 1, NULL, NOW(), NULL, NOW(), '涓� man_cloud_wms_notify_log.notify_status 瀵瑰簲'),
+(UPPER(UUID()), '浜戜粨閫氱煡鐘舵��-宸叉垚鍔�', 'CLOUD_WMS_NOTIFY_STATUS_SUCCESS', 2, '1', '宸叉垚鍔�', 1, 0, 1, NULL, NOW(), NULL, NOW(), '涓� man_cloud_wms_notify_log.notify_status 瀵瑰簲'),
+(UPPER(UUID()), '浜戜粨閫氱煡鐘舵��-澶辫触', 'CLOUD_WMS_NOTIFY_STATUS_FAIL', 2, '2', '澶辫触锛堝惈瓒呰繃閲嶈瘯娆℃暟锛�', 1, 0, 1, NULL, NOW(), NULL, NOW(), '涓� man_cloud_wms_notify_log.notify_status 瀵瑰簲');
diff --git a/version/db/man_cloud_wms_notify_log.sql b/version/db/man_cloud_wms_notify_log.sql
new file mode 100644
index 0000000..8379578
--- /dev/null
+++ b/version/db/man_cloud_wms_notify_log.sql
@@ -0,0 +1,25 @@
+-- 浜戜粨涓婃姤寰呭姙琛細涓氬姟浜嬪姟鍐呭彧钀藉簱锛岀敱瀹氭椂浠诲姟寮傛璇锋眰浜戜粨骞舵洿鏂伴�氱煡缁撴灉
+-- 9.1 鍏ュ嚭搴撶粨鏋滀笂鎶ャ��9.2 搴撳瓨璋冩暣涓诲姩涓婃姤
+SET NAMES utf8mb4;
+
+DROP TABLE IF EXISTS `man_cloud_wms_notify_log`;
+CREATE TABLE `man_cloud_wms_notify_log` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+  `report_type` varchar(32) NOT NULL COMMENT '涓婃姤绫诲瀷锛欼N_OUT_RESULT-鍏ュ嚭搴撶粨鏋滐紝INVENTORY_ADJUST-搴撳瓨璋冩暣',
+  `request_body` text COMMENT '璇锋眰浣揓SON锛堜笌鍗忚涓�鑷达紝渚涘畾鏃朵换鍔¢噸鏀撅級',
+  `notify_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '鏄惁宸查�氱煡鍒颁簯浠擄細0寰呴�氱煡 1鎴愬姛 2澶辫触',
+  `retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '宸查�氱煡娆℃暟锛堥噸璇曠疮璁★級',
+  `max_retry_count` int(11) DEFAULT NULL COMMENT '鏈�澶ч噸璇曟鏁帮紙涓虹┖鍒欑敤绯荤粺閰嶇疆锛�',
+  `retry_interval_seconds` int(11) DEFAULT NULL COMMENT '閲嶈瘯棰戠巼/闂撮殧绉掓暟锛堜负绌哄垯鐢ㄧ郴缁熼厤缃級',
+  `last_request_body` text COMMENT '鏈�杩戜竴娆¤姹備綋',
+  `last_response_body` text COMMENT '鏈�杩戜竴娆¤繑鍥炵粨鏋淛SON',
+  `last_notify_time` datetime DEFAULT NULL COMMENT '鏈�杩戜竴娆¤姹傛椂闂�',
+  `biz_ref` varchar(255) DEFAULT NULL COMMENT '涓氬姟鍏宠仈锛堝taskId銆乺eviseLogId锛�',
+  `tenant_id` int(11) DEFAULT NULL COMMENT '绉熸埛',
+  `deleted` tinyint(4) NOT NULL DEFAULT '0' COMMENT '鏄惁鍒犻櫎 0鍚� 1鏄�',
+  `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  PRIMARY KEY (`id`),
+  KEY `idx_notify_status_retry` (`notify_status`, `retry_count`),
+  KEY `idx_create_time` (`create_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='浜戜粨涓婃姤寰呭姙璁板綍';
diff --git a/version/db/open_api_app.sql b/version/db/open_api_app.sql
new file mode 100644
index 0000000..417d56d
--- /dev/null
+++ b/version/db/open_api_app.sql
@@ -0,0 +1,16 @@
+-- 8.1 Token 閴存潈锛氬簲鐢ㄨ〃锛実etToken 鏃剁敤 appId+appSecret 鍦ㄦ琛ㄦ牎楠�
+SET NAMES utf8mb4;
+
+DROP TABLE IF EXISTS `open_api_app`;
+CREATE TABLE `open_api_app` (
+  `id` varchar(64) NOT NULL COMMENT 'appId',
+  `screct` varchar(255) NOT NULL COMMENT 'appSecret',
+  `name` varchar(128) DEFAULT NULL COMMENT '搴旂敤鍚嶇О',
+  `url` varchar(512) DEFAULT NULL COMMENT '搴旂敤URL',
+  `enable` tinyint(4) NOT NULL DEFAULT '1' COMMENT '鏄惁鍚敤 0鏈惎鐢� 1鍚敤',
+  `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛id',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='寮�鏀炬帴鍙e簲鐢�';
+
+-- 绀轰緥鏁版嵁锛堝彲閫夛紝鐢ㄤ簬浜戜粨瀵规帴锛�
+-- INSERT INTO `open_api_app` (`id`, `screct`, `name`, `url`, `enable`, `tenant_id`) VALUES ('cloud_wms', 'your_secret', '浜戜粨WMS', NULL, 1, NULL);
diff --git a/version/db/open_api_app_menu.sql b/version/db/open_api_app_menu.sql
new file mode 100644
index 0000000..bd44ed8
--- /dev/null
+++ b/version/db/open_api_app_menu.sql
@@ -0,0 +1,7 @@
+-- 搴旂敤绠$悊鑿滃崟锛堝紑鏀炬帴鍙� Token 閴存潈搴旂敤锛岄渶鍦� sys_menu 宸叉湁鏁版嵁涔嬪悗鎵ц锛�
+-- 鐖惰彍鍗� id=47锛屽瓙鑿滃崟 48-51锛沺arent_id=1 琛ㄧず鎸傚湪銆岀郴缁熴�嶄笅
+INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (47, '搴旂敤绠$悊', 1, 'menu.system', '1', 'menu.system', '/system/openApiApp', 'openApiApp', NULL, NULL, 0, NULL, 'Apps', 11, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
+INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (48, 'Query App', 47, NULL, '1,47', NULL, NULL, NULL, NULL, NULL, 1, 'system:openApiApp:list', NULL, 0, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
+INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (49, 'Create App', 47, NULL, '1,47', NULL, NULL, NULL, NULL, NULL, 1, 'system:openApiApp:save', NULL, 1, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
+INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (50, 'Update App', 47, NULL, '1,47', NULL, NULL, NULL, NULL, NULL, 1, 'system:openApiApp:update', NULL, 2, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
+INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`) VALUES (51, 'Delete App', 47, NULL, '1,47', NULL, NULL, NULL, NULL, NULL, 1, 'system:openApiApp:remove', NULL, 3, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL);
diff --git a/version/db/out_stock_order_log_menu.sql b/version/db/out_stock_order_log_menu.sql
new file mode 100644
index 0000000..787f70e
--- /dev/null
+++ b/version/db/out_stock_order_log_menu.sql
@@ -0,0 +1,10 @@
+-- 鍑哄簱鍘嗗彶鍗曡彍鍗曪細涓庛�屽叆搴撳巻鍙插崟銆嶅悓绾э紝鍏辩敤 asnOrderLog 鎺ュ彛锛屼粎鍓嶇 resource=outStockOrderLog銆佸浐瀹� type=out
+-- 鎵ц鍓嶉渶宸插瓨鍦� component='asnOrderLog' 鐨勮彍鍗曪紝鍚﹀垯鏈� INSERT 涓嶄細鎻掑叆浠讳綍琛�
+INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
+SELECT 52, 'menu.outStockOrderLog', m.parent_id, m.parent_name, CONCAT(IFNULL(m.path,''), ',52'), 'menu.outStockOrderLog', '/histories/outStockOrderLog', 'outStockOrderLog', NULL, NULL, 0, NULL, 'Outbox', 2, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL
+FROM `sys_menu` m WHERE m.component = 'asnOrderLog' LIMIT 1;
+
+-- 鍑哄簱鍘嗗彶鍗曞垪琛ㄦ潈闄愶紙涓庡叆搴撳巻鍙插崟鍏辩敤 manager:asnOrderLog:list锛屾湁璇ユ潈闄愬嵆鍙闂袱涓彍鍗曪級
+INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `parent_name`, `path`, `path_name`, `route`, `component`, `brief`, `code`, `type`, `authority`, `icon`, `sort`, `meta`, `tenant_id`, `status`, `deleted`, `create_time`, `create_by`, `update_time`, `update_by`, `memo`)
+SELECT 53, 'Query OutStockOrderLog', 52, NULL, CONCAT(IFNULL(m.path,''), ',53'), NULL, NULL, NULL, NULL, NULL, 1, 'manager:asnOrderLog:list', NULL, 0, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL
+FROM `sys_menu` m WHERE m.id = 52 LIMIT 1;

--
Gitblit v1.9.1