From 52e09a6b7b7054fc51b9d4bf5f1fbec0a57e60f1 Mon Sep 17 00:00:00 2001
From: cl <1442464845@qq.com>
Date: 星期三, 08 四月 2026 11:37:39 +0800
Subject: [PATCH] 云仓回报调整

---
 rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/support/HttpAuditSupport.java                      |  122 +++++
 rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/mapper/HttpAuditLogMapper.java                     |    9 
 rsf-admin/src/page/system/httpAuditLog/index.jsx                                                          |    8 
 rsf-http-audit/src/main/resources/META-INF/spring.factories                                               |    2 
 rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/service/HttpAuditAsyncRecorder.java                |   57 ++
 pom.xml                                                                                                   |    1 
 rsf-open-api/pom.xml                                                                                      |    5 
 rsf-server/src/main/java/com/vincent/rsf/server/api/feign/CloudWmsErpFeignClient.java                     |   19 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java                 |    4 
 rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/CloudWmsReportServiceImpl.java           |  175 ++++++-
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InOutResultReportParam.java     |    6 
 rsf-admin/src/i18n/zh.js                                                                                  |   19 
 rsf-admin/src/i18n/en.js                                                                                  |   19 
 rsf-admin/src/page/ResourceContent.js                                                                     |    3 
 rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/HttpAuditLogServiceImpl.java          |   11 
 rsf-server/src/main/resources/application-dev.yml                                                         |   26 
 rsf-server/pom.xml                                                                                        |    5 
 rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/entity/HttpAuditLog.java                           |   67 ++
 rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/props/HttpAuditProperties.java                     |   63 ++
 rsf-admin/src/page/system/httpAuditLog/HttpAuditLogList.jsx                                               |   82 +++
 rsf-http-audit/pom.xml                                                                                    |   45 +
 rsf-open-api/src/main/resources/application-dev.yml                                                       |    5 
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/CloudWmsMockController.java                |   75 ++-
 rsf-server/src/main/java/com/vincent/rsf/server/api/integration/dap/DapIlcwmsResponseNormalizer.java      |   57 ++
 rsf-admin/src/page/system/httpAuditLog/HttpAuditLogShow.jsx                                               |   59 ++
 rsf-server/src/main/java/com/vincent/rsf/server/api/feign/fallback/CloudWmsErpFeignClientFallback.java    |   17 
 rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/config/HttpAuditAutoConfiguration.java             |   50 ++
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/DapIlcwmsCompletionRequest.java |   20 
 rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/web/HttpAuditFilter.java                           |  126 +++++
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/DapIlcwmsCompletionLine.java    |   30 +
 version/db/sys_http_audit_log.sql                                                                         |   27 +
 rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/InventoryAdjustReportParam.java |   18 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/ReviseLogServiceImpl.java            |    8 
 rsf-server/src/main/java/com/vincent/rsf/server/system/service/HttpAuditLogService.java                   |    7 
 version/db/http_audit_menu.sql                                                                            |   22 
 rsf-server/src/main/java/com/vincent/rsf/server/api/config/RemotesInfoProperties.java                     |   21 
 rsf-server/src/main/java/com/vincent/rsf/server/system/controller/HttpAuditLogController.java             |   43 +
 37 files changed, 1,259 insertions(+), 74 deletions(-)

diff --git a/pom.xml b/pom.xml
index 722b109..dab0ee9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,6 +19,7 @@
     <modules>
         <module>rsf-common</module>
         <module>rsf-framework</module>
+        <module>rsf-http-audit</module>
         <module>rsf-server</module>
         <module>rsf-open-api</module>
     </modules>
diff --git a/rsf-admin/src/i18n/en.js b/rsf-admin/src/i18n/en.js
index 11e16ef..02b05c1 100644
--- a/rsf-admin/src/i18n/en.js
+++ b/rsf-admin/src/i18n/en.js
@@ -3,6 +3,8 @@
 const customEnglishMessages = {
     ...englishMessages,
     hello: 'Hello World',
+    'menu.httpAuditLog': 'HTTP audit',
+    'resources.httpAuditLog.name': 'HTTP audit',
     common: {
         response: {
             success: "Success",
@@ -148,6 +150,7 @@
         department: 'Department',
         token: 'Token',
         operation: 'Operation',
+        httpAuditLog: 'HTTP audit',
         config: 'Config',
         tenant: 'Tenant',
         userLogin: 'Token',
@@ -336,6 +339,22 @@
                     unknown: 'Unknown',
                 }
             },
+            httpAuditLog: {
+                serviceName: "service",
+                scopeType: "scope",
+                uri: "uri",
+                method: "method",
+                functionDesc: "description",
+                queryString: "query string",
+                requestBody: "request JSON",
+                responseBody: "response JSON",
+                responseTruncated: "response truncated",
+                httpStatus: "HTTP status",
+                okFlag: "ok / error",
+                spendMs: "spend ms",
+                clientIp: "client IP",
+                errorMessage: "error",
+            },
             operationRecord: {
                 namespace: "namespace",
                 url: "url",
diff --git a/rsf-admin/src/i18n/zh.js b/rsf-admin/src/i18n/zh.js
index 140789d..7749482 100644
--- a/rsf-admin/src/i18n/zh.js
+++ b/rsf-admin/src/i18n/zh.js
@@ -3,8 +3,10 @@
 const customChineseMessages = {
     ...chineseMessages,
     hello: '浣犲ソ涓栫晫',
+    'menu.httpAuditLog': 'HTTP鎺ュ彛瀹¤',
     resources: {
         config: { name: '閰嶇疆鍙傛暟' },
+        httpAuditLog: { name: 'HTTP鎺ュ彛瀹¤' },
         asnOrderItem: { name: '鏀惰揣鏄庣粏' },
         outStockItem: { name: '鍑哄簱鍗曟槑缁�' },
     },
@@ -157,6 +159,7 @@
         department: '閮ㄩ棬绠$悊',
         token: '鐧诲綍鏃ュ織',
         operation: '鎿嶄綔鏃ュ織',
+        httpAuditLog: 'HTTP鎺ュ彛瀹¤',
         config: '閰嶇疆鍙傛暟',
         tenant: '绉熸埛绠$悊',
         userLogin: '鐧诲綍鏃ュ織',
@@ -368,6 +371,22 @@
                     unknown: '鏈煡',
                 }
             },
+            httpAuditLog: {
+                serviceName: "搴旂敤",
+                scopeType: "鍐呭閮�",
+                uri: "鎺ュ彛璺緞",
+                method: "鏂规硶",
+                functionDesc: "鍔熻兘鎻忚堪",
+                queryString: "鏌ヨ涓�",
+                requestBody: "璇锋眰鍐呭(JSON)",
+                responseBody: "鍝嶅簲鍐呭(JSON)",
+                responseTruncated: "鍝嶅簲宸叉埅鏂�",
+                httpStatus: "HTTP鐘舵��",
+                okFlag: "姝e父/寮傚父",
+                spendMs: "鑰楁椂(ms)",
+                clientIp: "璇锋眰IP",
+                errorMessage: "寮傚父淇℃伅",
+            },
             operationRecord: {
                 namespace: "鍛藉悕绌洪棿",
                 url: "url",
diff --git a/rsf-admin/src/page/ResourceContent.js b/rsf-admin/src/page/ResourceContent.js
index 969a2e6..3583d21 100644
--- a/rsf-admin/src/page/ResourceContent.js
+++ b/rsf-admin/src/page/ResourceContent.js
@@ -68,6 +68,7 @@
 import statisticCount from './statistics/stockStatisticNum';
 import rcsTest from './rcsTest';
 import openApiApp from './system/openApiApp';
+import httpAuditLog from './system/httpAuditLog';
 
 const ResourceContent = (node) => {
   switch (node.component) {
@@ -199,6 +200,8 @@
       return rcsTest;
     case "openApiApp":
       return openApiApp;
+    case "httpAuditLog":
+      return httpAuditLog;
     default:
       return {
         list: ListGuesser,
diff --git a/rsf-admin/src/page/system/httpAuditLog/HttpAuditLogList.jsx b/rsf-admin/src/page/system/httpAuditLog/HttpAuditLogList.jsx
new file mode 100644
index 0000000..58fe66d
--- /dev/null
+++ b/rsf-admin/src/page/system/httpAuditLog/HttpAuditLogList.jsx
@@ -0,0 +1,82 @@
+import React from "react";
+import {
+    List,
+    Datagrid,
+    TextField,
+    DateField,
+    TopToolbar,
+    FilterButton,
+    TextInput,
+    SelectInput,
+    ShowButton,
+    BulkDeleteButton,
+    FunctionField,
+} from "react-admin";
+import { Chip } from "@mui/material";
+import EmptyData from "@/page/components/EmptyData";
+import { DEFAULT_PAGE_SIZE } from "@/config/setting";
+
+const filters = [
+    <TextInput source="uri" label="table.field.httpAuditLog.uri" alwaysOn />,
+    <TextInput source="clientIp" label="table.field.httpAuditLog.clientIp" />,
+    <SelectInput
+        source="okFlag"
+        label="table.field.httpAuditLog.okFlag"
+        choices={[
+            { id: 1, name: "姝e父" },
+            { id: 0, name: "寮傚父" },
+        ]}
+    />,
+    <TextInput source="serviceName" label="table.field.httpAuditLog.serviceName" />,
+    <SelectInput
+        source="scopeType"
+        label="table.field.httpAuditLog.scopeType"
+        choices={[
+            { id: "EXTERNAL", name: "澶栭儴" },
+            { id: "INTERNAL", name: "鍐呴儴" },
+        ]}
+    />,
+    <TextInput source="functionDesc" label="table.field.httpAuditLog.functionDesc" />,
+    <TextInput source="method" label="table.field.httpAuditLog.method" />,
+];
+
+const HttpAuditLogList = () => (
+    <List
+        title="menu.httpAuditLog"
+        filters={filters}
+        sort={{ field: "create_time", order: "DESC" }}
+        perPage={DEFAULT_PAGE_SIZE}
+        empty={<EmptyData />}
+        actions={
+            <TopToolbar>
+                <FilterButton />
+            </TopToolbar>
+        }
+    >
+        <Datagrid bulkActionButtons={<BulkDeleteButton />}>
+            <TextField source="id" />
+            <TextField source="serviceName" label="table.field.httpAuditLog.serviceName" />
+            <TextField source="scopeType" label="table.field.httpAuditLog.scopeType" />
+            <TextField source="uri" label="table.field.httpAuditLog.uri" />
+            <TextField source="method" label="table.field.httpAuditLog.method" />
+            <TextField source="functionDesc" label="table.field.httpAuditLog.functionDesc" />
+            <TextField source="clientIp" label="table.field.httpAuditLog.clientIp" />
+            <FunctionField
+                label="table.field.httpAuditLog.okFlag"
+                render={(record) =>
+                    record.okFlag === 1 ? (
+                        <Chip label="姝e父" color="success" size="small" variant="outlined" />
+                    ) : (
+                        <Chip label="寮傚父" color="error" size="small" variant="outlined" />
+                    )
+                }
+            />
+            <TextField source="httpStatus" label="table.field.httpAuditLog.httpStatus" />
+            <TextField source="spendMs" label="table.field.httpAuditLog.spendMs" />
+            <DateField source="createTime" label="common.field.createTime" showTime />
+            <ShowButton />
+        </Datagrid>
+    </List>
+);
+
+export default HttpAuditLogList;
diff --git a/rsf-admin/src/page/system/httpAuditLog/HttpAuditLogShow.jsx b/rsf-admin/src/page/system/httpAuditLog/HttpAuditLogShow.jsx
new file mode 100644
index 0000000..1b19d5c
--- /dev/null
+++ b/rsf-admin/src/page/system/httpAuditLog/HttpAuditLogShow.jsx
@@ -0,0 +1,59 @@
+import React from "react";
+import { Show, SimpleShowLayout, TextField, DateField, FunctionField } from "react-admin";
+import { Box, Chip } from "@mui/material";
+
+const JsonBlock = ({ text }) => (
+    <Box component="pre" sx={{ whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0, fontSize: 12 }}>
+        {text ?? ""}
+    </Box>
+);
+
+const HttpAuditLogShow = () => (
+    <Show>
+        <SimpleShowLayout>
+            <TextField source="id" />
+            <TextField source="serviceName" label="table.field.httpAuditLog.serviceName" />
+            <TextField source="scopeType" label="table.field.httpAuditLog.scopeType" />
+            <TextField source="uri" label="table.field.httpAuditLog.uri" />
+            <TextField source="method" label="table.field.httpAuditLog.method" />
+            <TextField source="functionDesc" label="table.field.httpAuditLog.functionDesc" />
+            <TextField source="clientIp" label="table.field.httpAuditLog.clientIp" />
+            <FunctionField
+                label="table.field.httpAuditLog.okFlag"
+                render={(record) =>
+                    record.okFlag === 1 ? (
+                        <Chip label="姝e父" color="success" size="small" variant="outlined" />
+                    ) : (
+                        <Chip label="寮傚父" color="error" size="small" variant="outlined" />
+                    )
+                }
+            />
+            <TextField source="httpStatus" label="table.field.httpAuditLog.httpStatus" />
+            <TextField source="spendMs" label="table.field.httpAuditLog.spendMs" />
+            <TextField source="responseTruncated" label="table.field.httpAuditLog.responseTruncated" />
+            <DateField source="createTime" label="common.field.createTime" showTime />
+            <FunctionField
+                source="queryString"
+                label="table.field.httpAuditLog.queryString"
+                render={(record) => <JsonBlock text={record.queryString} />}
+            />
+            <FunctionField
+                source="requestBody"
+                label="table.field.httpAuditLog.requestBody"
+                render={(record) => <JsonBlock text={record.requestBody} />}
+            />
+            <FunctionField
+                source="responseBody"
+                label="table.field.httpAuditLog.responseBody"
+                render={(record) => <JsonBlock text={record.responseBody} />}
+            />
+            <FunctionField
+                source="errorMessage"
+                label="table.field.httpAuditLog.errorMessage"
+                render={(record) => <JsonBlock text={record.errorMessage} />}
+            />
+        </SimpleShowLayout>
+    </Show>
+);
+
+export default HttpAuditLogShow;
diff --git a/rsf-admin/src/page/system/httpAuditLog/index.jsx b/rsf-admin/src/page/system/httpAuditLog/index.jsx
new file mode 100644
index 0000000..1dc8217
--- /dev/null
+++ b/rsf-admin/src/page/system/httpAuditLog/index.jsx
@@ -0,0 +1,8 @@
+import HttpAuditLogList from "./HttpAuditLogList";
+import HttpAuditLogShow from "./HttpAuditLogShow";
+
+export default {
+    list: HttpAuditLogList,
+    show: HttpAuditLogShow,
+    recordRepresentation: (record) => `${record.uri || record.id}`,
+};
diff --git a/rsf-http-audit/pom.xml b/rsf-http-audit/pom.xml
new file mode 100644
index 0000000..fef4495
--- /dev/null
+++ b/rsf-http-audit/pom.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.vincent</groupId>
+        <artifactId>rsf</artifactId>
+        <version>1.0.0</version>
+    </parent>
+    <artifactId>rsf-http-audit</artifactId>
+    <packaging>jar</packaging>
+    <name>rsf-http-audit</name>
+    <description>HTTP 鎺ュ彛瀹¤锛堝紩鍏ュ嵆娉ㄥ唽 Filter 寮傛钀藉簱锛屼笉褰卞搷涓氬姟锛�</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-autoconfigure</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+            <version>3.4.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/config/HttpAuditAutoConfiguration.java b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/config/HttpAuditAutoConfiguration.java
new file mode 100644
index 0000000..c0ff688
--- /dev/null
+++ b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/config/HttpAuditAutoConfiguration.java
@@ -0,0 +1,50 @@
+package com.vincent.rsf.httpaudit.config;
+
+import com.vincent.rsf.httpaudit.props.HttpAuditProperties;
+import com.vincent.rsf.httpaudit.service.HttpAuditAsyncRecorder;
+import com.vincent.rsf.httpaudit.web.HttpAuditFilter;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.env.Environment;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+/**
+ * 寮曞叆 rsf-http-audit 鍗崇敓鏁堬紙鍙� http-audit.enabled=false 鍏抽棴锛�
+ */
+@Configuration
+@EnableAsync
+@EnableConfigurationProperties(HttpAuditProperties.class)
+@ConditionalOnProperty(prefix = "http-audit", name = "enabled", havingValue = "true", matchIfMissing = true)
+@MapperScan("com.vincent.rsf.httpaudit.mapper")
+public class HttpAuditAutoConfiguration {
+
+    @Bean(name = "httpAuditExecutor")
+    public Executor httpAuditExecutor() {
+        ThreadPoolTaskExecutor ex = new ThreadPoolTaskExecutor();
+        ex.setCorePoolSize(2);
+        ex.setMaxPoolSize(8);
+        ex.setQueueCapacity(1000);
+        ex.setThreadNamePrefix("http-audit-");
+        ex.initialize();
+        return ex;
+    }
+
+    @Bean
+    public FilterRegistrationBean<HttpAuditFilter> httpAuditFilterRegistration(
+            HttpAuditAsyncRecorder recorder, HttpAuditProperties props, Environment env) {
+        HttpAuditFilter filter = new HttpAuditFilter(recorder, props, env);
+        FilterRegistrationBean<HttpAuditFilter> reg = new FilterRegistrationBean<>();
+        reg.setFilter(filter);
+        reg.addUrlPatterns("/*");
+        reg.setOrder(Ordered.HIGHEST_PRECEDENCE + 50);
+        return reg;
+    }
+}
diff --git a/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/entity/HttpAuditLog.java b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/entity/HttpAuditLog.java
new file mode 100644
index 0000000..6338006
--- /dev/null
+++ b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/entity/HttpAuditLog.java
@@ -0,0 +1,67 @@
+package com.vincent.rsf.httpaudit.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 lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * HTTP 鎺ュ彛瀹¤璁板綍
+ */
+@Data
+@Accessors(chain = true)
+@TableName("sys_http_audit_log")
+public class HttpAuditLog implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 搴旂敤鍚嶏紝濡� spring.application.name */
+    private String serviceName;
+
+    /** EXTERNAL-澶栭儴锛汭NTERNAL-鍐呴儴 */
+    private String scopeType;
+
+    /** 璇锋眰璺緞锛堜笉鍚煙鍚嶏級 */
+    private String uri;
+
+    private String method;
+
+    /** 鍔熻兘璇存槑锛堟潵鑷厤缃渶闀垮墠缂�鍖归厤锛� */
+    private String functionDesc;
+
+    private String queryString;
+
+    /** 璇锋眰浣� JSON/鏂囨湰锛屽叏閲� */
+    private String requestBody;
+
+    /** 鍝嶅簲浣擄紝鏌ヨ绫绘垨瓒呴暱鏃舵埅鏂� */
+    private String responseBody;
+
+    /** 1 琛ㄧず鍝嶅簲浣撳凡鎸夎鍒欐埅鏂� */
+    private Integer responseTruncated;
+
+    private Integer httpStatus;
+
+    /** 1 姝e父锛�2xx 涓旀棤鏈崟鑾峰紓甯革級锛�0 寮傚父 */
+    private Integer okFlag;
+
+    private Integer spendMs;
+
+    private String clientIp;
+
+    /** 閾捐矾涓婂紓甯告憳瑕� */
+    private String errorMessage;
+
+    private Date createTime;
+
+    @TableLogic
+    private Integer deleted;
+}
diff --git a/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/mapper/HttpAuditLogMapper.java b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/mapper/HttpAuditLogMapper.java
new file mode 100644
index 0000000..3c006e4
--- /dev/null
+++ b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/mapper/HttpAuditLogMapper.java
@@ -0,0 +1,9 @@
+package com.vincent.rsf.httpaudit.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.vincent.rsf.httpaudit.entity.HttpAuditLog;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface HttpAuditLogMapper extends BaseMapper<HttpAuditLog> {
+}
diff --git a/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/props/HttpAuditProperties.java b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/props/HttpAuditProperties.java
new file mode 100644
index 0000000..a553c23
--- /dev/null
+++ b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/props/HttpAuditProperties.java
@@ -0,0 +1,63 @@
+package com.vincent.rsf.httpaudit.props;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * HTTP 瀹¤閰嶇疆
+ */
+@Data
+@ConfigurationProperties(prefix = "http-audit")
+public class HttpAuditProperties {
+
+    private boolean enabled = true;
+
+    /** 鏌ヨ绫诲搷搴旀渶澶氫繚鐣欏瓧绗︽暟 */
+    private int queryResponseMaxChars = 500;
+
+    /** 闈炴煡璇㈢被鍝嶅簲鏈�澶氬叆搴撳瓧鑺傦紙瓒呭嚭鎴柇骞舵爣璁帮級 */
+    private int maxResponseStoreChars = 65535;
+
+    /** 璇锋眰浣撶紦瀛樹笂闄愶紙瀛楄妭锛� */
+    private int maxRequestCacheBytes = 2 * 1024 * 1024;
+
+    /** 鍝嶅簲浣撶紦瀛樹笂闄愶紙瀛楄妭锛� */
+    private int maxResponseCacheBytes = 2 * 1024 * 1024;
+
+    /** 涓嶈惤搴撶殑璺緞鍓嶇紑 */
+    private List<String> excludePathPrefixes = defaultExcludes();
+
+    /** 瑙嗕负澶栭儴璋冪敤鐨勮矾寰勫墠缂�锛堝叾浣欎负鍐呴儴锛� */
+    private List<String> externalPathPrefixes = defaultExternal();
+
+    /** 璺緞 -> 鍔熻兘鎻忚堪锛堟寜鏈�闀胯矾寰勫墠缂�鍖归厤锛� */
+    private Map<String, String> pathDescriptions = new LinkedHashMap<>();
+
+    private static List<String> defaultExcludes() {
+        List<String> list = new ArrayList<>();
+        list.add("/actuator");
+        list.add("/swagger");
+        list.add("/webjars");
+        list.add("/v2/api-docs");
+        list.add("/v3/api-docs");
+        list.add("/doc.html");
+        list.add("/druid");
+        list.add("/error");
+        list.add("/favicon.ico");
+        list.add("/static/");
+        list.add("/httpAuditLog");
+        return list;
+    }
+
+    private static List<String> defaultExternal() {
+        List<String> list = new ArrayList<>();
+        list.add("/erp");
+        list.add("/cloudwms");
+        return list;
+    }
+}
diff --git a/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/service/HttpAuditAsyncRecorder.java b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/service/HttpAuditAsyncRecorder.java
new file mode 100644
index 0000000..1c99d59
--- /dev/null
+++ b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/service/HttpAuditAsyncRecorder.java
@@ -0,0 +1,57 @@
+package com.vincent.rsf.httpaudit.service;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.vincent.rsf.httpaudit.entity.HttpAuditLog;
+import com.vincent.rsf.httpaudit.mapper.HttpAuditLogMapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 寮傛钀藉簱锛涘け璐ユ椂鎵撳叏閲忔棩蹇楋紝涓嶅悜涓氬姟鎶涢敊
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class HttpAuditAsyncRecorder {
+
+    private final HttpAuditLogMapper httpAuditLogMapper;
+    private final ObjectMapper objectMapper = new ObjectMapper();
+
+    @Async("httpAuditExecutor")
+    public void save(HttpAuditLog entity) {
+        try {
+            httpAuditLogMapper.insert(entity);
+        } catch (Throwable t) {
+            try {
+                Map<String, Object> dump = new LinkedHashMap<>();
+                dump.put("serviceName", entity.getServiceName());
+                dump.put("scopeType", entity.getScopeType());
+                dump.put("uri", entity.getUri());
+                dump.put("method", entity.getMethod());
+                dump.put("functionDesc", entity.getFunctionDesc());
+                dump.put("queryString", entity.getQueryString());
+                dump.put("requestBody", entity.getRequestBody());
+                dump.put("responseBody", entity.getResponseBody());
+                dump.put("httpStatus", entity.getHttpStatus());
+                dump.put("okFlag", entity.getOkFlag());
+                dump.put("spendMs", entity.getSpendMs());
+                dump.put("clientIp", entity.getClientIp());
+                dump.put("errorMessage", entity.getErrorMessage());
+                String json = objectMapper.writeValueAsString(dump);
+                log.error("http-audit 钀藉簱澶辫触锛屽叏閲廕SON濡備笅锛歿}", json, t);
+            } catch (JsonProcessingException je) {
+                log.error("http-audit 钀藉簱澶辫触涓斿簭鍒楀寲瀹¤鍐呭澶辫触锛宺equestBody.length={}, responseBody.length={}",
+                        entity.getRequestBody() == null ? -1 : entity.getRequestBody().length(),
+                        entity.getResponseBody() == null ? -1 : entity.getResponseBody().length(),
+                        t);
+                log.error("搴忓垪鍖栧紓甯�", je);
+            }
+        }
+    }
+}
diff --git a/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/support/HttpAuditSupport.java b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/support/HttpAuditSupport.java
new file mode 100644
index 0000000..67ac455
--- /dev/null
+++ b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/support/HttpAuditSupport.java
@@ -0,0 +1,122 @@
+package com.vincent.rsf.httpaudit.support;
+
+import com.vincent.rsf.httpaudit.props.HttpAuditProperties;
+
+import javax.servlet.http.HttpServletRequest;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鍐呭閮ㄥ垽瀹氥�佽矾寰勮鏄庛�佸搷搴旀埅鏂�
+ */
+public final class HttpAuditSupport {
+
+    private HttpAuditSupport() {
+    }
+
+    public static String resolveScope(HttpServletRequest request, HttpAuditProperties props) {
+        String path = safePath(request);
+        for (String p : props.getExternalPathPrefixes()) {
+            if (path.startsWith(p)) {
+                return "EXTERNAL";
+            }
+        }
+        return "INTERNAL";
+    }
+
+    public static String resolveFunctionDesc(HttpServletRequest request, HttpAuditProperties props) {
+        String path = safePath(request);
+        Map<String, String> map = props.getPathDescriptions();
+        if (map == null || map.isEmpty()) {
+            return null;
+        }
+        List<String> keys = new ArrayList<>(map.keySet());
+        keys.sort(Comparator.comparingInt(String::length).reversed());
+        for (String k : keys) {
+            if (path.startsWith(k)) {
+                return map.get(k);
+            }
+        }
+        return null;
+    }
+
+    public static String safePath(HttpServletRequest request) {
+        String ctx = request.getContextPath();
+        String uri = request.getRequestURI();
+        if (ctx != null && !ctx.isEmpty() && uri.startsWith(ctx)) {
+            return uri.substring(ctx.length());
+        }
+        return uri != null ? uri : "";
+    }
+
+    public static boolean shouldExclude(HttpServletRequest request, HttpAuditProperties props) {
+        String path = safePath(request);
+        for (String p : props.getExcludePathPrefixes()) {
+            if (p != null && !p.isEmpty() && path.startsWith(p)) {
+                return true;
+            }
+        }
+        String lower = path.toLowerCase();
+        if (lower.endsWith(".js") || lower.endsWith(".css") || lower.endsWith(".ico")
+                || lower.endsWith(".png") || lower.endsWith(".jpg") || lower.endsWith(".gif")
+                || lower.endsWith(".woff") || lower.endsWith(".woff2") || lower.endsWith(".map")) {
+            return true;
+        }
+        return false;
+    }
+
+    public static boolean isQueryLike(HttpServletRequest request) {
+        String m = request.getMethod();
+        if ("GET".equalsIgnoreCase(m)) {
+            return true;
+        }
+        String path = safePath(request).toLowerCase();
+        return path.contains("/page") || path.contains("/list") || path.contains("/query");
+    }
+
+    public static String clientIp(HttpServletRequest request) {
+        String xff = request.getHeader("X-Forwarded-For");
+        if (xff != null && !xff.isEmpty()) {
+            int i = xff.indexOf(',');
+            return i > 0 ? xff.substring(0, i).trim() : xff.trim();
+        }
+        String real = request.getHeader("X-Real-IP");
+        if (real != null && !real.isEmpty()) {
+            return real.trim();
+        }
+        return request.getRemoteAddr();
+    }
+
+    public static Charset resolveCharset(HttpServletRequest request) {
+        String enc = request.getCharacterEncoding();
+        if (enc == null || enc.isEmpty()) {
+            return StandardCharsets.UTF_8;
+        }
+        try {
+            return Charset.forName(enc);
+        } catch (Exception e) {
+            return StandardCharsets.UTF_8;
+        }
+    }
+
+    public static String bytesToString(byte[] buf, Charset charset) {
+        if (buf == null || buf.length == 0) {
+            return "";
+        }
+        return new String(buf, charset);
+    }
+
+    public static String truncateForStore(String s, int maxChars) {
+        if (s == null) {
+            return null;
+        }
+        if (s.length() <= maxChars) {
+            return s;
+        }
+        return s.substring(0, maxChars) + "...(truncated,len=" + s.length() + ")";
+    }
+}
diff --git a/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/web/HttpAuditFilter.java b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/web/HttpAuditFilter.java
new file mode 100644
index 0000000..1abd8f1
--- /dev/null
+++ b/rsf-http-audit/src/main/java/com/vincent/rsf/httpaudit/web/HttpAuditFilter.java
@@ -0,0 +1,126 @@
+package com.vincent.rsf.httpaudit.web;
+
+import com.vincent.rsf.httpaudit.entity.HttpAuditLog;
+import com.vincent.rsf.httpaudit.props.HttpAuditProperties;
+import com.vincent.rsf.httpaudit.service.HttpAuditAsyncRecorder;
+import com.vincent.rsf.httpaudit.support.HttpAuditSupport;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.env.Environment;
+import org.springframework.web.filter.OncePerRequestFilter;
+import org.springframework.web.util.ContentCachingRequestWrapper;
+import org.springframework.web.util.ContentCachingResponseWrapper;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Date;
+
+/**
+ * 缂撳瓨璇锋眰/鍝嶅簲浣撳苟寮傛鍐欏璁¤〃
+ */
+@Slf4j
+@RequiredArgsConstructor
+public class HttpAuditFilter extends OncePerRequestFilter {
+
+    private final HttpAuditAsyncRecorder recorder;
+    private final HttpAuditProperties props;
+    private final Environment environment;
+
+    @Override
+    protected boolean shouldNotFilter(HttpServletRequest request) {
+        return !props.isEnabled() || HttpAuditSupport.shouldExclude(request, props);
+    }
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+            throws ServletException, IOException {
+        ContentCachingRequestWrapper reqWrapper = new ContentCachingRequestWrapper(request, props.getMaxRequestCacheBytes());
+        ContentCachingResponseWrapper resWrapper = new ContentCachingResponseWrapper(response);
+        long t0 = System.currentTimeMillis();
+        Exception chainError = null;
+        try {
+            filterChain.doFilter(reqWrapper, resWrapper);
+        } catch (IOException | ServletException e) {
+            chainError = e;
+            throw e;
+        } catch (RuntimeException e) {
+            chainError = e;
+            throw e;
+        } finally {
+            try {
+                record(reqWrapper, resWrapper, t0, chainError);
+            } catch (Throwable ignore) {
+                log.warn("http-audit record 寮傚父宸插悶鎺夛細{}", ignore.getMessage());
+            }
+            try {
+                resWrapper.copyBodyToResponse();
+            } catch (IOException io) {
+                log.debug("copyBodyToResponse: {}", io.getMessage());
+            }
+        }
+    }
+
+    private void record(ContentCachingRequestWrapper req, ContentCachingResponseWrapper res, long t0, Exception chainError) {
+        Charset charset = HttpAuditSupport.resolveCharset(req);
+        String ctReq = req.getContentType();
+        String reqBody;
+        if (ctReq != null && ctReq.toLowerCase().startsWith("multipart/")) {
+            reqBody = "[multipart omitted]";
+        } else {
+            reqBody = HttpAuditSupport.bytesToString(req.getContentAsByteArray(), charset);
+        }
+
+        String respCt = res.getContentType();
+        String resBodyRaw = HttpAuditSupport.bytesToString(res.getContentAsByteArray(), charset);
+        String resBodyToStore;
+        int truncated = 0;
+        if (respCt != null && (respCt.contains("octet-stream") || respCt.contains("application/pdf"))) {
+            resBodyToStore = "[binary response omitted]";
+            truncated = 1;
+        } else if (HttpAuditSupport.isQueryLike(req)) {
+            resBodyToStore = HttpAuditSupport.truncateForStore(resBodyRaw, props.getQueryResponseMaxChars());
+            if (resBodyRaw != null && resBodyRaw.length() > props.getQueryResponseMaxChars()) {
+                truncated = 1;
+            }
+        } else {
+            resBodyToStore = HttpAuditSupport.truncateForStore(resBodyRaw, props.getMaxResponseStoreChars());
+            if (resBodyRaw != null && resBodyRaw.length() > props.getMaxResponseStoreChars()) {
+                truncated = 1;
+            }
+        }
+
+        int status = res.getStatus();
+        int ok = (chainError == null && status >= 200 && status < 400) ? 1 : 0;
+        String errMsg = null;
+        if (chainError != null) {
+            String s = chainError.toString();
+            errMsg = s.length() > 4000 ? s.substring(0, 4000) + "..." : s;
+        }
+
+        String appName = environment.getProperty("spring.application.name", "unknown");
+
+        HttpAuditLog logEntity = new HttpAuditLog()
+                .setServiceName(appName)
+                .setScopeType(HttpAuditSupport.resolveScope(req, props))
+                .setUri(HttpAuditSupport.safePath(req))
+                .setMethod(req.getMethod())
+                .setFunctionDesc(HttpAuditSupport.resolveFunctionDesc(req, props))
+                .setQueryString(req.getQueryString())
+                .setRequestBody(reqBody)
+                .setResponseBody(resBodyToStore)
+                .setResponseTruncated(truncated)
+                .setHttpStatus(status)
+                .setOkFlag(ok)
+                .setSpendMs((int) (System.currentTimeMillis() - t0))
+                .setClientIp(HttpAuditSupport.clientIp(req))
+                .setErrorMessage(errMsg)
+                .setCreateTime(new Date())
+                .setDeleted(0);
+
+        recorder.save(logEntity);
+    }
+}
diff --git a/rsf-http-audit/src/main/resources/META-INF/spring.factories b/rsf-http-audit/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..65c20b4
--- /dev/null
+++ b/rsf-http-audit/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.vincent.rsf.httpaudit.config.HttpAuditAutoConfiguration
diff --git a/rsf-open-api/pom.xml b/rsf-open-api/pom.xml
index d8215d0..cb6f4a3 100644
--- a/rsf-open-api/pom.xml
+++ b/rsf-open-api/pom.xml
@@ -21,6 +21,11 @@
             <artifactId>rsf-common</artifactId>
             <version>1.0.0</version>
         </dependency>
+        <dependency>
+            <groupId>com.vincent</groupId>
+            <artifactId>rsf-http-audit</artifactId>
+            <version>1.0.0</version>
+        </dependency>
         <!-- OpenFeign -->
         <dependency>
             <groupId>org.springframework.cloud</groupId>
diff --git a/rsf-open-api/src/main/resources/application-dev.yml b/rsf-open-api/src/main/resources/application-dev.yml
index 5a86c98..6148deb 100644
--- a/rsf-open-api/src/main/resources/application-dev.yml
+++ b/rsf-open-api/src/main/resources/application-dev.yml
@@ -72,3 +72,8 @@
     host: http://127.0.0.1
     #绔彛
     port: 3741
+
+http-audit:
+  enabled: true
+  query-response-max-chars: 500
+  max-response-store-chars: 65535
diff --git a/rsf-server/pom.xml b/rsf-server/pom.xml
index a451ff6..1157cf1 100644
--- a/rsf-server/pom.xml
+++ b/rsf-server/pom.xml
@@ -26,6 +26,11 @@
 			<version>1.0.0</version>
 		</dependency>
 		<dependency>
+			<groupId>com.vincent</groupId>
+			<artifactId>rsf-http-audit</artifactId>
+			<version>1.0.0</version>
+		</dependency>
+		<dependency>
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-web</artifactId>
 		</dependency>
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 67956f6..7c022b6 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
@@ -35,15 +35,30 @@
      */
     private String baseUrl;
 
+    /**
+     * 榧庢嵎 ilcwmsplus 瀹屾垚鍙嶉绛夊叕鍏卞瓧娈碉紙orgNo銆佸崟鎹被鍒�佸崟浣嶏級
+     */
+    private Dap dap = new Dap();
+
+    @Data
+    public static class Dap {
+        private String orgNo = "";
+        private String docTypeIn = "";
+        private String docTypeOut = "";
+        /** 搴撳瓨璋冩暣锛�9.2锛夊崟鎹被鍒紱绉诲簱绛� */
+        private String docTypeAdj = "";
+        private String unitNo = "PCS";
+    }
+
     @Data
     @Configuration
     @ConfigurationProperties(prefix = "platform.erp.api")
     public class ApiInfo {
         /** 涓�閿笂鎶ヨ川妫�鎺ュ彛 */
         private String notifyInspect;
-        /** 9.1 鍏�/鍑哄簱缁撴灉涓婃姤锛堢珛搴撲晶璇锋眰浜戜粨锛� */
-        private String inOutResultPath = "/api/report/inOutResult";
-        /** 9.2 搴撳瓨璋冩暣涓诲姩涓婃姤锛堢珛搴撲晶璇锋眰浜戜粨锛� */
+        /** 宸叉敼涓� Feign 鍥哄畾璺緞 /dapilc/.../cusInventoryCompletionReport銆乧usOutboundCompletionReport锛屾湰椤逛粎浣滈厤缃崰浣� */
+        private String inOutResultPath = "/dapilc/restful/service/ilcwmsplus/IKWebService/cusInventoryCompletionReport";
+        /** 9.2 搴撳瓨璋冩暣锛氫粛涓� /api/report/inventoryAdjust锛屾姤鏂囦綋涓� 9.1 涓�鑷翠负 {data:[]} */
         private String inventoryAdjustPath = "/api/report/inventoryAdjust";
         /** 鐗╂枡鍩虹淇℃伅鍚屾锛堢珛搴撲晶璇锋眰浜戜粨锛� */
         private String matSyncPath = "/api/mat/sync";
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
index 4a562ba..bb3ec1d 100644
--- 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
@@ -1,31 +1,44 @@
 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 com.vincent.rsf.server.api.controller.erp.params.DapIlcwmsCompletionRequest;
 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锛夛紝
- * 绔嬪簱涓婃姤璇锋眰浼氭墦鍒版湰鎺ュ彛骞惰繑鍥炴ā鎷熸垚鍔熴��
+ * 浜戜粨妯℃嫙鎺ュ彛銆俻latform.erp.base-url 鎸囧悜鏈湇鍔℃椂锛孎eign 浼氳姹備笅鍒� DAP 椋庢牸璺緞銆�
  */
 @Slf4j
 @RestController
-@RequestMapping("/api")
 @Api(value = "浜戜粨妯℃嫙鎺ュ彛", tags = "浜戜粨妯℃嫙锛堟棤鐪熷疄浜戜粨URL鏃朵娇鐢級")
 public class CloudWmsMockController {
 
-    private static Map<String, Object> successResponse() {
+    private static Map<String, Object> dapOkEnvelope() {
+        Map<String, Object> profile = new HashMap<>();
+        profile.put("tenantSid", 1);
+        profile.put("userSid", "SYS");
+        Map<String, Object> response = new HashMap<>();
+        response.put("code", -1);
+        response.put("success", false);
+        response.put("message", "");
+        Map<String, Object> map = new HashMap<>();
+        map.put("duration", 58);
+        map.put("statusDescription", "OK");
+        map.put("response", response);
+        map.put("profile", profile);
+        map.put("uuid", "");
+        map.put("status", 200);
+        return map;
+    }
+
+    private static Map<String, Object> successResponseLegacy() {
         Map<String, Object> data = new HashMap<>();
         data.put("result", "SUCCESS");
         Map<String, Object> map = new HashMap<>();
@@ -35,33 +48,39 @@
         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();
+    @ApiOperation("榧庢嵎-鍏ュ簱瀹屾垚鍙嶉锛堟ā鎷燂級")
+    @PostMapping(value = "/dapilc/restful/service/ilcwmsplus/IKWebService/cusInventoryCompletionReport", consumes = MediaType.APPLICATION_JSON_VALUE)
+    public Map<String, Object> mockInventoryCompletion(@RequestBody DapIlcwmsCompletionRequest body) {
+        log.info("浜戜粨妯℃嫙-鍏ュ簱瀹屾垚鍙嶉锛岃鏁�={}", body != null && body.getData() != null ? body.getData().size() : 0);
+        return dapOkEnvelope();
     }
 
-    /** 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 = "/dapilc/restful/service/ilcwmsplus/IKWebService/cusOutboundCompletionReport", consumes = MediaType.APPLICATION_JSON_VALUE)
+    public Map<String, Object> mockOutboundCompletion(@RequestBody DapIlcwmsCompletionRequest body) {
+        log.info("浜戜粨妯℃嫙-鍑哄簱瀹屾垚鍙嶉锛岃鏁�={}", body != null && body.getData() != null ? body.getData().size() : 0);
+        return dapOkEnvelope();
+    }
+
+    /** 9.2 搴撳瓨璋冩暣涓诲姩涓婃姤 - 妯℃嫙锛堣矾寰勪笉鍙橈紝body 涓� {data:[]}锛� */
+    @ApiOperation("搴撳瓨璋冩暣涓婃姤锛堟ā鎷燂級")
+    @PostMapping(value = "/api/report/inventoryAdjust", consumes = MediaType.APPLICATION_JSON_VALUE)
+    public Map<String, Object> mockInventoryAdjust(@RequestBody DapIlcwmsCompletionRequest body) {
+        log.info("浜戜粨妯℃嫙-搴撳瓨璋冩暣涓婃姤锛岃鏁�={}", body != null && body.getData() != null ? body.getData().size() : 0);
+        return successResponseLegacy();
     }
 
     /** 鐗╂枡鍩虹淇℃伅鍚屾 - 妯℃嫙 */
     @ApiOperation("鐗╂枡鍚屾锛堟ā鎷燂級")
-    @PostMapping(value = "/mat/sync", consumes = MediaType.APPLICATION_JSON_VALUE)
+    @PostMapping(value = "/api/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();
+        return successResponseLegacy();
     }
+
+//    /** 9.1 鍏�/鍑哄簱缁撴灉涓婃姤 - 妯℃嫙锛堟棫璺緞锛屽凡鏀� DAP锛� */
+//    @PostMapping(value = "/api/report/inOutResult", consumes = MediaType.APPLICATION_JSON_VALUE)
+//    public Map<String, Object> mockInOutResult(@RequestBody InOutResultReportParam body) {
+//        return successResponseLegacy();
+//    }
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/DapIlcwmsCompletionLine.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/DapIlcwmsCompletionLine.java
new file mode 100644
index 0000000..72e8975
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/DapIlcwmsCompletionLine.java
@@ -0,0 +1,30 @@
+package com.vincent.rsf.server.api.controller.erp.params;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 榧庢嵎 ilcwmsplus 绔嬪簱鍏�/鍑哄簱瀹屾垚鍙嶉 - data 涓崟琛�
+ */
+@Data
+@Accessors(chain = true)
+@ApiModel(value = "DapIlcwmsCompletionLine", description = "绔嬪簱瀹屾垚鍙嶉鏄庣粏琛�")
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class DapIlcwmsCompletionLine {
+
+    private String orgNo;
+    private String docType;
+    private String docNo;
+    private String docSeqNo;
+    private String itemNo;
+    private Double qty;
+    private String unitNo;
+    private String inWarehouseNo;
+    private String inCellNo;
+    private String outWarehouseNo;
+    private String outCellNo;
+    private String combinationLotNo;
+    private String barcode;
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/DapIlcwmsCompletionRequest.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/DapIlcwmsCompletionRequest.java
new file mode 100644
index 0000000..899e545
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/controller/erp/params/DapIlcwmsCompletionRequest.java
@@ -0,0 +1,20 @@
+package com.vincent.rsf.server.api.controller.erp.params;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+/**
+ * 榧庢嵎 ilcwmsplus 绔嬪簱鍏�/鍑哄簱瀹屾垚鍙嶉 - 璇锋眰浣�
+ */
+@Data
+@Accessors(chain = true)
+@ApiModel(value = "DapIlcwmsCompletionRequest", description = "绔嬪簱瀹屾垚鍙嶉璇锋眰")
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class DapIlcwmsCompletionRequest {
+
+    private List<DapIlcwmsCompletionLine> data;
+}
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
index 5cebeba..4941d95 100644
--- 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
@@ -39,4 +39,10 @@
 
     @ApiModelProperty("鎵规")
     private String batch;
+
+    @ApiModelProperty(value = "true 鍏ュ簱瀹屾垚鍙嶉锛宖alse 鍑哄簱瀹屾垚鍙嶉", required = true)
+    private Boolean inbound;
+
+    @ApiModelProperty(value = "鏉$爜锛堜簯浠撳繀濉級")
+    private String barcode;
 }
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
index 1f152ee..10d6b64 100644
--- 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
@@ -34,4 +34,22 @@
 
     @ApiModelProperty("鎵樼洏鍙�")
     private String palletId;
+
+    @ApiModelProperty("浜戜粨鍗曞彿")
+    private String docNo;
+
+    @ApiModelProperty("椤规")
+    private String docSeqNo;
+
+    @ApiModelProperty("鍗曟嵁绫诲埆锛堢┖鍒欐寜 changeType 鍙栭厤缃� dap.doc-type-in/out/adj锛�")
+    private String docType;
+
+    @ApiModelProperty("鎵规")
+    private String batch;
+
+    @ApiModelProperty("鏉$爜锛堜簯浠撳繀濉級")
+    private String barcode;
+
+    @ApiModelProperty("鍗曚綅缂栫爜锛堢┖鍒欏彇閰嶇疆 dap.unit-no锛�")
+    private String unitNo;
 }
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
index 097fc15..2d6cd63 100644
--- 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
@@ -1,7 +1,6 @@
 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.controller.erp.params.DapIlcwmsCompletionRequest;
 import com.vincent.rsf.server.api.feign.fallback.CloudWmsErpFeignClientFallbackFactory;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.http.MediaType;
@@ -11,7 +10,7 @@
 import java.util.Map;
 
 /**
- * 绔嬪簱渚ч�氳繃 OpenFeign 璋冪敤浜戜粨WMS锛氬叆/鍑哄簱缁撴灉涓婃姤锛�9.1锛夈�佸簱瀛樿皟鏁翠笂鎶ワ紙9.2锛夈�佺墿鏂欏悓姝ャ��
+ * 绔嬪簱渚ч�氳繃 OpenFeign 璋冪敤浜戜粨锛�9.1 榧庢嵎 ilcwmsplus 鍏�/鍑哄簱瀹屾垚鍙嶉锛堜袱 URL锛夛紱9.2 浠嶄负 /api/report/inventoryAdjust锛岃姹備綋涓� 9.1 鐩稿悓 {data:[]}锛堢敱 Service 浠� InventoryAdjustReportParam 缁勮锛夈��
  * 浣跨敤 platform.erp.base-url 浣滀负鏍瑰湴鍧�锛涘け璐ユ椂璧� Fallback锛岀粺涓�杩斿洖閿欒鍝嶅簲锛堜笉鎶涘紓甯革級銆�
  */
 @FeignClient(
@@ -21,13 +20,17 @@
 )
 public interface CloudWmsErpFeignClient {
 
-    /** 9.1 鍏�/鍑哄簱缁撴灉涓婃姤 */
-    @PostMapping(value = "/api/report/inOutResult", consumes = MediaType.APPLICATION_JSON_VALUE)
-    Map<String, Object> reportInOutResult(@RequestBody InOutResultReportParam body);
+    /**9.1.1 绔嬪簱鍏ュ簱浠诲姟瀹屾垚鍙嶉 */
+    @PostMapping(value = "/dapilc/restful/service/ilcwmsplus/IKWebService/cusInventoryCompletionReport", consumes = MediaType.APPLICATION_JSON_VALUE)
+    Map<String, Object> cusInventoryCompletionReport(@RequestBody DapIlcwmsCompletionRequest body);
 
-    /** 9.2 搴撳瓨璋冩暣涓诲姩涓婃姤 */
+    /**9.1.2 绔嬪簱鍑哄簱浠诲姟瀹屾垚鍙嶉 */
+    @PostMapping(value = "/dapilc/restful/service/ilcwmsplus/IKWebService/cusOutboundCompletionReport", consumes = MediaType.APPLICATION_JSON_VALUE)
+    Map<String, Object> cusOutboundCompletionReport(@RequestBody DapIlcwmsCompletionRequest body);
+
+    /** 9.2 搴撳瓨璋冩暣涓诲姩涓婃姤锛堣矾寰勪笉鍙橈紱body 涓庡叆鍑哄簱涓�鑷寸殑 data 鏁扮粍缁撴瀯锛� */
     @PostMapping(value = "/api/report/inventoryAdjust", consumes = MediaType.APPLICATION_JSON_VALUE)
-    Map<String, Object> reportInventoryAdjust(@RequestBody InventoryAdjustReportParam body);
+    Map<String, Object> reportInventoryAdjust(@RequestBody DapIlcwmsCompletionRequest body);
 
     /** 鐗╂枡鍩虹淇℃伅鍚屾 */
     @PostMapping(value = "/api/mat/sync", consumes = MediaType.APPLICATION_JSON_VALUE)
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
index 14ff707..9845061 100644
--- 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
@@ -1,7 +1,6 @@
 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.controller.erp.params.DapIlcwmsCompletionRequest;
 import com.vincent.rsf.server.api.feign.CloudWmsErpFeignClient;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
@@ -76,14 +75,20 @@
     }
 
     @Override
-    public Map<String, Object> reportInOutResult(InOutResultReportParam body) {
-        log.error("璋冪敤浜戜粨WMS 鍏�/鍑哄簱缁撴灉涓婃姤鎺ュ彛澶辫触锛岃Е鍙戦檷绾�", cause);
+    public Map<String, Object> cusInventoryCompletionReport(DapIlcwmsCompletionRequest body) {
+        log.error("璋冪敤浜戜粨 鍏ュ簱瀹屾垚鍙嶉澶辫触锛岃Е鍙戦檷绾�", cause);
         return errorResponse();
     }
 
     @Override
-    public Map<String, Object> reportInventoryAdjust(InventoryAdjustReportParam body) {
-        log.error("璋冪敤浜戜粨WMS 搴撳瓨璋冩暣涓婃姤鎺ュ彛澶辫触锛岃Е鍙戦檷绾�", cause);
+    public Map<String, Object> cusOutboundCompletionReport(DapIlcwmsCompletionRequest body) {
+        log.error("璋冪敤浜戜粨 鍑哄簱瀹屾垚鍙嶉澶辫触锛岃Е鍙戦檷绾�", cause);
+        return errorResponse();
+    }
+
+    @Override
+    public Map<String, Object> reportInventoryAdjust(DapIlcwmsCompletionRequest body) {
+        log.error("璋冪敤浜戜粨 9.2 搴撳瓨璋冩暣涓婃姤澶辫触锛岃Е鍙戦檷绾�", cause);
         return errorResponse();
     }
 
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/api/integration/dap/DapIlcwmsResponseNormalizer.java b/rsf-server/src/main/java/com/vincent/rsf/server/api/integration/dap/DapIlcwmsResponseNormalizer.java
new file mode 100644
index 0000000..75e9e64
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/api/integration/dap/DapIlcwmsResponseNormalizer.java
@@ -0,0 +1,57 @@
+package com.vincent.rsf.server.api.integration.dap;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 榧庢嵎 DAP 杩斿洖缁撴瀯杞簯浠撲笂鎶ュ唴閮ㄧ粺涓�鏍煎紡锛堜緵閲嶈瘯浠诲姟鍒ゆ柇 code==200锛�
+ */
+public final class DapIlcwmsResponseNormalizer {
+
+    private DapIlcwmsResponseNormalizer() {
+    }
+
+    public static Map<String, Object> toNotifyFormat(Map<String, Object> dapBody) {
+        if (dapBody == null) {
+            return resultMap(500, "浜戜粨杩斿洖绌�", null);
+        }
+        Object st = dapBody.get("status");
+        int status = 0;
+        if (st instanceof Number) {
+            status = ((Number) st).intValue();
+        }
+        if (status == 200) {
+            return resultMap(200, "OK", dapBody);
+        }
+        Object err = dapBody.get("errorMessage");
+        String msg = err != null ? String.valueOf(err) : String.valueOf(dapBody.get("statusDescription"));
+        if (msg == null || "null".equals(msg)) {
+            msg = "浜戜粨杩斿洖澶辫触";
+        }
+        return resultMap(500, msg, dapBody);
+    }
+
+    /**
+     * 9.2 绛変粛璧版棫璺緞鐨勬帴鍙o細鑻ヨ繑鍥炴棤椤跺眰 status锛屽垯鎸夊師 code/msg 閫忎紶锛堝吋瀹规棫浜戜粨 JSON锛夈��
+     */
+    public static Map<String, Object> toNotifyFormatFlexible(Map<String, Object> body) {
+        if (body == null) {
+            return toNotifyFormat(null);
+        }
+        if (body.containsKey("status")) {
+            return toNotifyFormat(body);
+        }
+        if (body.containsKey("code")) {
+            return body;
+        }
+        return toNotifyFormat(body);
+    }
+
+    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;
+    }
+}
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
index ca1da5c..fbaa4ca 100644
--- 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
@@ -1,20 +1,26 @@
 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.DapIlcwmsCompletionLine;
+import com.vincent.rsf.server.api.controller.erp.params.DapIlcwmsCompletionRequest;
 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.integration.dap.DapIlcwmsResponseNormalizer;
 import com.vincent.rsf.server.api.service.CloudWmsReportService;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
- * 绔嬪簱渚ц姹備簯浠擄細鍏�/鍑哄簱缁撴灉涓婃姤锛�9.1锛夈�佸簱瀛樿皟鏁翠富鍔ㄤ笂鎶ワ紙9.2锛夈�佺墿鏂欏熀纭�淇℃伅鍚屾銆�
- * 浣跨敤 OpenFeign 璋冪敤锛涘彲閫� HttpEntity(RestTemplate) 鏂瑰紡宸叉敞閲婁繚鐣欍��
+ * 绔嬪簱渚ц姹備簯浠擄細9.1 榧庢嵎 ilcwmsplus 鍏�/鍑哄簱涓ゆ帴鍙o紱9.2 浠嶄负 /api/report/inventoryAdjust锛屾姤鏂囦笌 9.1 鍚屼负 {data:[]}銆�
  */
 @Slf4j
 @Service
@@ -24,16 +30,7 @@
     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) {
@@ -53,7 +50,17 @@
             log.warn("ErpApi(浜戜粨WMS) 鏈厤缃� host锛岃烦杩� 9.1 鍏�/鍑哄簱缁撴灉涓婃姤锛岃鍗曪細{}", param.getOrderNo());
             return stubSuccess("浜戜粨鍦板潃鏈厤缃紝鏈疄闄呬笂鎶�");
         }
-        return cloudWmsErpFeignClient.reportInOutResult(param);
+        String err = validateDapBase();
+        if (err != null) {
+            return resultMap(400, err, null);
+        }
+        boolean inbound = param.getInbound() == null || Boolean.TRUE.equals(param.getInbound());
+        DapIlcwmsCompletionRequest req = new DapIlcwmsCompletionRequest()
+                .setData(Collections.singletonList(buildInOutLine(param, inbound)));
+        Map<String, Object> raw = inbound
+                ? cloudWmsErpFeignClient.cusInventoryCompletionReport(req)
+                : cloudWmsErpFeignClient.cusOutboundCompletionReport(req);
+        return DapIlcwmsResponseNormalizer.toNotifyFormat(raw);
     }
 
     @Override
@@ -65,7 +72,139 @@
             log.warn("ErpApi(浜戜粨WMS) 鏈厤缃� host锛岃烦杩� 9.2 搴撳瓨璋冩暣涓婃姤锛岀墿鏂欙細{}", param.getMatNr());
             return stubSuccess("浜戜粨鍦板潃鏈厤缃紝鏈疄闄呬笂鎶�");
         }
-        return cloudWmsErpFeignClient.reportInventoryAdjust(param);
+        String err = validateDapBase();
+        if (err != null) {
+            return resultMap(400, err, null);
+        }
+        Integer changeType = param.getChangeType();
+        if (changeType == null) {
+            return resultMap(400, "changeType 涓嶈兘涓虹┖", null);
+        }
+        DapIlcwmsCompletionRequest req = new DapIlcwmsCompletionRequest();
+        if (changeType == 3) {
+            String baseSeq = StringUtils.isNotBlank(param.getDocSeqNo()) ? param.getDocSeqNo() : "1";
+            List<DapIlcwmsCompletionLine> lines = new ArrayList<>(2);
+            lines.add(buildAdjustLine(param, false, true, baseSeq + "-O"));
+            lines.add(buildAdjustLine(param, true, false, baseSeq + "-I"));
+            req.setData(lines);
+        } else if (changeType == 1) {
+            req.setData(Collections.singletonList(buildAdjustLine(param, true, false, null)));
+        } else if (changeType == 2) {
+            req.setData(Collections.singletonList(buildAdjustLine(param, false, true, null)));
+        } else {
+            return resultMap(400, "涓嶆敮鎸佺殑 changeType锛�" + changeType, null);
+        }
+        Map<String, Object> raw = cloudWmsErpFeignClient.reportInventoryAdjust(req);
+        return DapIlcwmsResponseNormalizer.toNotifyFormatFlexible(raw);
+    }
+
+    private DapIlcwmsCompletionLine buildInOutLine(InOutResultReportParam param, boolean inbound) {
+        RemotesInfoProperties.Dap dap = erpApi.getDap();
+        DapIlcwmsCompletionLine line = new DapIlcwmsCompletionLine()
+                .setOrgNo(dap.getOrgNo())
+                .setDocType(inbound ? dap.getDocTypeIn() : dap.getDocTypeOut())
+                .setDocNo(param.getOrderNo())
+                .setDocSeqNo(StringUtils.isNotBlank(param.getLineId()) ? param.getLineId() : "1")
+                .setItemNo(param.getMatNr())
+                .setQty(parseQty(param.getQty()))
+                .setUnitNo(dap.getUnitNo())
+                .setCombinationLotNo(param.getBatch())
+                .setBarcode(resolveBarcode(param));
+        if (inbound) {
+            line.setInWarehouseNo(param.getWareHouseId()).setInCellNo(param.getLocId());
+        } else {
+            line.setOutWarehouseNo(param.getWareHouseId()).setOutCellNo(param.getLocId());
+        }
+        return line;
+    }
+
+    /**
+     * @param fillIn 鏄惁濉叆搴撳偍浣�
+     * @param fillOut 鏄惁濉嚭搴撳偍浣�
+     * @param docSeqOverride 闈炵┖鏃剁敤浣滈」娆★紙绉诲簱绗簩琛岀瓑锛�
+     */
+    private DapIlcwmsCompletionLine buildAdjustLine(InventoryAdjustReportParam param, boolean fillIn, boolean fillOut, String docSeqOverride) {
+        RemotesInfoProperties.Dap dap = erpApi.getDap();
+        String docType = resolveAdjustDocType(param, dap);
+        String docNo = StringUtils.isNotBlank(param.getDocNo()) ? param.getDocNo() : "ADJ";
+        String docSeq = docSeqOverride != null ? docSeqOverride
+                : (StringUtils.isNotBlank(param.getDocSeqNo()) ? param.getDocSeqNo() : "1");
+        String unit = StringUtils.isNotBlank(param.getUnitNo()) ? param.getUnitNo() : dap.getUnitNo();
+        DapIlcwmsCompletionLine line = new DapIlcwmsCompletionLine()
+                .setOrgNo(dap.getOrgNo())
+                .setDocType(docType)
+                .setDocNo(docNo)
+                .setDocSeqNo(docSeq)
+                .setItemNo(param.getMatNr())
+                .setQty(parseQty(param.getQty()))
+                .setUnitNo(unit)
+                .setCombinationLotNo(param.getBatch())
+                .setBarcode(resolveAdjustBarcode(param));
+        if (fillIn) {
+            line.setInWarehouseNo(param.getWareHouseId());
+            line.setInCellNo(StringUtils.isNotBlank(param.getTargetLocId()) ? param.getTargetLocId() : param.getSourceLocId());
+        }
+        if (fillOut) {
+            line.setOutWarehouseNo(param.getWareHouseId());
+            line.setOutCellNo(StringUtils.isNotBlank(param.getSourceLocId()) ? param.getSourceLocId() : param.getTargetLocId());
+        }
+        return line;
+    }
+
+    private static String resolveAdjustDocType(InventoryAdjustReportParam param, RemotesInfoProperties.Dap dap) {
+        if (StringUtils.isNotBlank(param.getDocType())) {
+            return param.getDocType();
+        }
+        Integer ct = param.getChangeType();
+        if (ct != null && ct == 2) {
+            return dap.getDocTypeOut();
+        }
+        if (ct != null && ct == 3) {
+            return dap.getDocTypeAdj();
+        }
+        return dap.getDocTypeIn();
+    }
+
+    private static String resolveBarcode(InOutResultReportParam param) {
+        if (StringUtils.isNotBlank(param.getBarcode())) {
+            return param.getBarcode();
+        }
+        if (StringUtils.isNotBlank(param.getPalletId())) {
+            return param.getPalletId();
+        }
+        if (param.getMatNr() != null && param.getLocId() != null) {
+            return param.getMatNr() + ":" + param.getLocId();
+        }
+        return param.getMatNr();
+    }
+
+    private static String resolveAdjustBarcode(InventoryAdjustReportParam param) {
+        if (StringUtils.isNotBlank(param.getBarcode())) {
+            return param.getBarcode();
+        }
+        if (StringUtils.isNotBlank(param.getPalletId())) {
+            return param.getPalletId();
+        }
+        return param.getMatNr();
+    }
+
+    private static Double parseQty(String q) {
+        if (StringUtils.isBlank(q)) {
+            return 0d;
+        }
+        try {
+            return Double.parseDouble(q.trim());
+        } catch (NumberFormatException e) {
+            return 0d;
+        }
+    }
+
+    private String validateDapBase() {
+        RemotesInfoProperties.Dap d = erpApi.getDap();
+        if (d == null || StringUtils.isBlank(d.getOrgNo())) {
+            return "鏈厤缃� platform.erp.dap.org-no";
+        }
+        return null;
     }
 
     private boolean isCloudWmsConfigured() {
@@ -86,14 +225,4 @@
         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/manager/service/impl/ReviseLogServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/ReviseLogServiceImpl.java
index 79b31c8..01f744a 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
@@ -250,7 +250,13 @@
                                 .setSourceLocId(sourceLoc.getCode())
                                 .setTargetLocId(finalLoc.getCode())
                                 .setMatNr(logItem.getMatnrCode())
-                                .setQty(logItem.getReviseQty() != null ? String.valueOf(logItem.getReviseQty()) : "0");
+                                .setQty(logItem.getReviseQty() != null ? String.valueOf(logItem.getReviseQty()) : "0")
+                                .setDocNo(revise.getCode())
+                                .setDocSeqNo(logItem.getId() != null ? String.valueOf(logItem.getId()) : "1")
+                                .setBatch(logItem.getBatch())
+                                .setUnitNo(logItem.getUnit())
+                                .setBarcode(logItem.getMatnrCode() != null && logItem.getLocCode() != null
+                                        ? logItem.getMatnrCode() + "@" + logItem.getLocCode() : logItem.getMatnrCode());
                         String requestBody = objectMapper.writeValueAsString(param);
                         Date now = new Date();
                         CloudWmsNotifyLog notifyLog = new CloudWmsNotifyLog()
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 6423c9f..9b841c4 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
@@ -2747,7 +2747,9 @@
                         .setLocId(locId)
                         .setMatNr(item.getMatnrCode())
                         .setQty(item.getAnfme() != null ? String.valueOf(item.getAnfme()) : "0")
-                        .setBatch(item.getBatch());
+                        .setBatch(item.getBatch())
+                        .setInbound(isInbound)
+                        .setBarcode(task.getBarcode());
                 try {
                     String requestBody = om.writeValueAsString(param);
                     CloudWmsNotifyLog notifyLog = new CloudWmsNotifyLog()
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/HttpAuditLogController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/HttpAuditLogController.java
new file mode 100644
index 0000000..dc962df
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/HttpAuditLogController.java
@@ -0,0 +1,43 @@
+package com.vincent.rsf.server.system.controller;
+
+import com.vincent.rsf.framework.common.R;
+import com.vincent.rsf.httpaudit.entity.HttpAuditLog;
+import com.vincent.rsf.server.common.domain.BaseParam;
+import com.vincent.rsf.server.common.domain.PageParam;
+import com.vincent.rsf.server.system.service.HttpAuditLogService;
+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 HttpAuditLogController extends BaseController {
+
+    @Autowired
+    private HttpAuditLogService httpAuditLogService;
+
+    @PreAuthorize("hasAuthority('system:httpAuditLog:list')")
+    @PostMapping("/httpAuditLog/page")
+    public R page(@RequestBody Map<String, Object> map) {
+        BaseParam baseParam = buildParam(map, BaseParam.class);
+        PageParam<HttpAuditLog, BaseParam> pageParam = new PageParam<>(baseParam, HttpAuditLog.class);
+        return R.ok().add(httpAuditLogService.page(pageParam, pageParam.buildWrapper(true)));
+    }
+
+    @PreAuthorize("hasAuthority('system:httpAuditLog:list')")
+    @GetMapping("/httpAuditLog/{id}")
+    public R get(@PathVariable("id") Long id) {
+        return R.ok().add(httpAuditLogService.getById(id));
+    }
+
+    @PreAuthorize("hasAuthority('system:httpAuditLog:remove')")
+    @PostMapping("/httpAuditLog/remove/{ids}")
+    public R remove(@PathVariable Long[] ids) {
+        if (!httpAuditLogService.removeByIds(Arrays.asList(ids))) {
+            return R.error("Delete Fail");
+        }
+        return R.ok("Delete Success");
+    }
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/HttpAuditLogService.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/HttpAuditLogService.java
new file mode 100644
index 0000000..3c6cbdb
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/HttpAuditLogService.java
@@ -0,0 +1,7 @@
+package com.vincent.rsf.server.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.vincent.rsf.httpaudit.entity.HttpAuditLog;
+
+public interface HttpAuditLogService extends IService<HttpAuditLog> {
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/HttpAuditLogServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/HttpAuditLogServiceImpl.java
new file mode 100644
index 0000000..de929f5
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/HttpAuditLogServiceImpl.java
@@ -0,0 +1,11 @@
+package com.vincent.rsf.server.system.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.vincent.rsf.httpaudit.entity.HttpAuditLog;
+import com.vincent.rsf.httpaudit.mapper.HttpAuditLogMapper;
+import com.vincent.rsf.server.system.service.HttpAuditLogService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class HttpAuditLogServiceImpl extends ServiceImpl<HttpAuditLogMapper, HttpAuditLog> implements HttpAuditLogService {
+}
diff --git a/rsf-server/src/main/resources/application-dev.yml b/rsf-server/src/main/resources/application-dev.yml
index 4a1f465..76ca7f2 100644
--- a/rsf-server/src/main/resources/application-dev.yml
+++ b/rsf-server/src/main/resources/application-dev.yml
@@ -81,12 +81,19 @@
     port: 8080
     #鎺ュ搧閾炬帴鍓嶇紑
     pre-path: ""
-    # Feign 璋冪敤浜戜粨鏃剁殑鏍瑰湴鍧�銆備簯浠撴湭鎻愪緵 URL 鏃跺彲鐢ㄦ湰鏈烘ā鎷燂細http://127.0.0.1:8086/rsf-server锛堟湰鏈嶅姟鎻愪緵鐨� CloudWmsMockController锛�
+    # Feign 璋冪敤浜戜粨鏃剁殑鏍瑰湴鍧�銆傛湰鏈烘ā鎷燂細http://127.0.0.1:8086/rsf-server锛圕loudWmsMockController锛�/dapilc/.../cusInventoryCompletionReport 绛夛級
     base-url: http://127.0.0.1:8086/rsf-server
-    #鎺ュ彛鏄庣粏锛堢珛搴撲晶璇锋眰浜戜粨鏃朵娇鐢ㄧ殑璺緞锛�
+    # 榧庢嵎 DAP ilcwmsplus 瀹屾垚鍙嶉锛�9.1/9.2 缁勫寘鐢級
+    dap:
+      org-no: ""
+      doc-type-in: ""
+      doc-type-out: ""
+      doc-type-adj: ""
+      unit-no: PCS
+    #鎺ュ彛鏄庣粏锛堣川妫�绛夛紱鍏ュ嚭搴撳畬鎴愬凡鍥哄畾涓� /dapilc/restful/service/ilcwmsplus/IKWebService/...锛�
     api:
       notify-inspect: /report/inspect
-      in-out-result-path: /api/report/inOutResult
+      in-out-result-path: /dapilc/restful/service/ilcwmsplus/IKWebService/cusInventoryCompletionReport
       inventory-adjust-path: /api/report/inventoryAdjust
       mat-sync-path: /api/mat/sync
   rcs:
@@ -105,4 +112,15 @@
     #鍒ゆ柇鏄悗妫�楠屽悎鏍煎悗锛屾墠鍏佽涓婃灦
     flagAvailable: true
     #鍒ゆ柇鏄惁鏍¢獙鍚堟牸鍚庯紝鎵嶅厑璁告敹璐�
-    flagReceiving: false
\ No newline at end of file
+    flagReceiving: false
+
+# HTTP 鎺ュ彛瀹¤锛坮sf-http-audit锛屽紩鍏ヤ緷璧栧嵆鐢熸晥锛屽彲 enabled=false 鍏抽棴锛�
+http-audit:
+  enabled: true
+  query-response-max-chars: 500
+  max-response-store-chars: 65535
+  path-descriptions:
+    "/erp/order": "浜戜粨-璁㈠崟鏌ヨ"
+    "/erp/order/add": "浜戜粨-鍗曟嵁涓嬪彂"
+    "/erp/order/addAll": "浜戜粨-鎵归噺鍗曟嵁涓嬪彂"
+    "/erp/order/cancel": "浜戜粨-鍙栨秷鍗曟嵁"
\ No newline at end of file
diff --git a/version/db/http_audit_menu.sql b/version/db/http_audit_menu.sql
new file mode 100644
index 0000000..9ce0b4f
--- /dev/null
+++ b/version/db/http_audit_menu.sql
@@ -0,0 +1,22 @@
+-- HTTP 鎺ュ彛瀹¤鑿滃崟锛堢郴缁熺鐞嗕笅锛屼笌鎿嶄綔鏃ュ織鍚岀骇锛夛紱鎵ц鍓嶈纭 id 210-212 鏈鍗犵敤
+SET NAMES utf8mb4;
+
+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 210, 'menu.httpAuditLog', 1, 'menu.system', '1,210', 'menu.httpAuditLog', '/system/httpAuditLog', 'httpAuditLog', NULL, NULL, 0, NULL, 'Http', 6, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL
+FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `id` = 210);
+
+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 211, 'Query HttpAuditLog', 210, '', '1,210,211', NULL, NULL, NULL, NULL, NULL, 1, 'system:httpAuditLog:list', NULL, 0, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL
+FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `id` = 211);
+
+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 212, 'Delete HttpAuditLog', 210, '', '1,210,212', NULL, NULL, NULL, NULL, NULL, 1, 'system:httpAuditLog:remove', NULL, 1, NULL, 1, 1, 0, NULL, NULL, NULL, NULL, NULL
+FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `id` = 212);
+
+-- 瓒呯骇绠$悊鍛樿鑹�(role_id=1)鎺堟潈鑿滃崟锛堣嫢宸插瓨鍦ㄥ垯璺宠繃锛�
+INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
+SELECT 1, 210 FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_role_menu` WHERE `role_id` = 1 AND `menu_id` = 210);
+INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
+SELECT 1, 211 FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_role_menu` WHERE `role_id` = 1 AND `menu_id` = 211);
+INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
+SELECT 1, 212 FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_role_menu` WHERE `role_id` = 1 AND `menu_id` = 212);
diff --git a/version/db/sys_http_audit_log.sql b/version/db/sys_http_audit_log.sql
new file mode 100644
index 0000000..afa0d8b
--- /dev/null
+++ b/version/db/sys_http_audit_log.sql
@@ -0,0 +1,27 @@
+-- HTTP 鎺ュ彛瀹¤锛坮sf-http-audit 鎻掍欢鍐欏叆锛�
+SET NAMES utf8mb4;
+
+DROP TABLE IF EXISTS `sys_http_audit_log`;
+CREATE TABLE `sys_http_audit_log` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+  `service_name` varchar(64) DEFAULT NULL COMMENT '搴旂敤 spring.application.name',
+  `scope_type` varchar(16) NOT NULL COMMENT 'EXTERNAL 澶栭儴 / INTERNAL 鍐呴儴',
+  `uri` varchar(512) NOT NULL COMMENT '璇锋眰璺緞',
+  `method` varchar(16) DEFAULT NULL COMMENT 'HTTP 鏂规硶',
+  `function_desc` varchar(255) DEFAULT NULL COMMENT '鍔熻兘鎻忚堪',
+  `query_string` varchar(2048) DEFAULT NULL COMMENT 'QueryString',
+  `request_body` longtext COMMENT '璇锋眰浣擄紙鍏ㄩ噺锛�',
+  `response_body` longtext COMMENT '鍝嶅簲浣擄紙鏌ヨ绫绘垨瓒呴暱浼氭埅鏂級',
+  `response_truncated` tinyint(4) NOT NULL DEFAULT '0' COMMENT '1 鍝嶅簲宸叉埅鏂�',
+  `http_status` int(11) DEFAULT NULL COMMENT 'HTTP 鐘舵�佺爜',
+  `ok_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '1 姝e父 0 寮傚父',
+  `spend_ms` int(11) DEFAULT NULL COMMENT '鑰楁椂姣',
+  `client_ip` varchar(64) DEFAULT NULL COMMENT '瀹㈡埛绔� IP',
+  `error_message` text COMMENT '寮傚父鎽樿',
+  `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `deleted` tinyint(4) NOT NULL DEFAULT '0' COMMENT '閫昏緫鍒犻櫎',
+  PRIMARY KEY (`id`),
+  KEY `idx_create_time` (`create_time`),
+  KEY `idx_uri` (`uri`(191)),
+  KEY `idx_ok_client` (`ok_flag`,`client_ip`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='HTTP鎺ュ彛瀹¤';

--
Gitblit v1.9.1