From 70ecc717c746eef0c4503a19031ada582f5e94d1 Mon Sep 17 00:00:00 2001
From: 1 <1@123>
Date: 星期五, 09 一月 2026 15:47:04 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/devlop-phyz' into devlop-phyz

---
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/AppMapper.java                                 |   15 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/MESController.java                    |   94 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/utils/TokenUtils.java                        |  161 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/MatPreparationOrderItem.java              |   24 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Supplier.java                             |   39 
 rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuCreate.jsx                                                 |  125 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Order.java                                |   78 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/AppService.java                               |   18 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/AppController.java                |  100 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/MatPreparationOrder.java                  |   30 
 rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java                                  |    6 
 rsf-admin/src/layout/index.jsx                                                                           |    3 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/dto/CommonResponse.java                        |   32 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/WebMvcConfig.java                              |   48 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/AppAuthParam.java                              |   26 
 rsf-server/src/main/resources/mapper/system/MatnrRoleMenuMapper.xml                                      |   22 
 rsf-server/src/main/java/com/vincent/rsf/server/common/config/MybatisPlusConfig.java                     |    1 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/TaskResult.java                           |   24 
 rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuList.jsx                                                   |  154 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/ApiFunctionService.java                       |   18 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/CheckOrder.java                           |   35 
 rsf-admin/src/config/setting.js                                                                          |    2 
 rsf-admin/src/page/matnrRoleMenu/index.jsx                                                               |   18 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/ApiForeignLogService.java                     |    9 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/annotation/OperationLog.java                          |   42 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/TaskController.java                        |   59 
 rsf-server/src/main/java/com/vincent/rsf/server/system/service/MatnrRoleMenuService.java                 |   11 
 rsf-admin/src/layout/MyMenu.jsx                                                                          |  136 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/constant/Constants.java                        |   12 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Station.java                              |   39 
 rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/MatnrRoleMenuMapper.java                   |   15 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/ApiSecurityConfig.java                         |   36 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/ApiMapController.java             |  138 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/AppAuthExampleController.java      |   98 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventorySummary.java                     |   34 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/ApiMapService.java                            |   18 
 rsf-admin/src/page/basicInfo/loc/InitModal.jsx                                                           |    4 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/FieldMappingExampleController.java |  195 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/TokenAuthExampleController.java    |  182 +
 rsf-server/src/main/java/com/vincent/rsf/server/system/controller/MatnrRoleMenuController.java           |   68 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/CheckResult.java                          |   24 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/utils/ParamsMapUtils.java                             |  273 ++
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/aspect/LogAspect.java                                 |  264 ++
 rsf-admin/src/page/orders/asnOrder/AsnOrderModal.jsx                                                     |    2 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventoryDetails.java                     |   47 
 rsf-admin/src/page/work/stockTransfer/stockTransferList.jsx                                              |    2 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsErpController.java                      |    4 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/ApiForeignLogServiceImpl.java            |   18 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/ApiForeignLog.java                         |  136 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Customer.java                             |   39 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/JsonReplaceExampleController.java  |  326 +++
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Task.java                                 |   36 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsRcsController.java                      |    9 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/ApiFunctionController.java        |  100 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/AppServiceImpl.java                      |   32 
 pom.xml                                                                                                  |    5 
 rsf-open-api/pom.xml                                                                                     |   18 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventoryQueryCondition.java              |   28 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/ApiAuthExampleController.java      |   96 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/ApiFunctionMapper.java                         |   15 
 rsf-admin/src/page/system/role/AssignPermissions_matnr.jsx                                               |  380 +++
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/ApiMap.java                                |   78 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Warehouse.java                            |   33 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/ApiMapServiceImpl.java                   |   32 
 rsf-admin/src/page/components/PageDrawer.jsx                                                             |    9 
 rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuEdit.jsx                                                   |   97 
 rsf-admin/src/layout/TabsBar.jsx                                                                         |   11 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/App.java                                   |   54 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/ApiFunction.java                           |   48 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/OrderItem.java                            |   65 
 rsf-admin/src/page/orders/asnOrder/AsnOrderPanel.jsx                                                     |    2 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/SimpleProductionTask.java                 |   14 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/filter/AppIdAuthenticationFilter.java        |  121 +
 rsf-server/src/main/java/com/vincent/rsf/server/system/entity/MatnrRoleMenu.java                         |   50 
 rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/MatnrRoleMenuServiceImpl.java        |   17 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/ERPController.java                    |  284 ++
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/AuthController.java                        |  174 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/service/AppAuthService.java                  |  112 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/utils/AuthUtils.java                         |   54 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/utils/JsonReplaceTest.java                            |  192 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/ApiForeignLogMapper.java                       |    9 
 rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuPanel.jsx                                                  |   63 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/ApiMapMapper.java                              |   15 
 rsf-open-api/src/main/resources/logback-spring.xml                                                       |  215 ++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/MatnrGroup.java                           |    2 
 rsf-admin/src/page/system/role/RoleList.jsx                                                              |  167 +
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Material.java                             |   55 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Pallet.java                               |   36 
 rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/ApiFunctionServiceImpl.java              |   30 
 rsf-server/src/main/java/matnrRoleMenu.sql                                                               |   23 
 90 files changed, 5,924 insertions(+), 161 deletions(-)

diff --git a/pom.xml b/pom.xml
index 722b109..f8d401c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,6 +38,11 @@
             <artifactId>spring-boot-starter</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+            <version>1.2.21</version>
+        </dependency>
+        <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
             <version>${lombok.version}</version>
diff --git a/rsf-admin/src/config/setting.js b/rsf-admin/src/config/setting.js
index 60520aa..749c98d 100644
--- a/rsf-admin/src/config/setting.js
+++ b/rsf-admin/src/config/setting.js
@@ -25,7 +25,7 @@
 
 export const ABORT_SIGNAL = false;
 
-export const DEFAULT_PAGE_SIZE = 25;
+export const DEFAULT_PAGE_SIZE = 20;
 
 export const DEFAULT_START_PAGE = 1;
 
diff --git a/rsf-admin/src/layout/MyMenu.jsx b/rsf-admin/src/layout/MyMenu.jsx
index 9e6b379..f42ce06 100644
--- a/rsf-admin/src/layout/MyMenu.jsx
+++ b/rsf-admin/src/layout/MyMenu.jsx
@@ -84,75 +84,75 @@
   // 鍦� MyMenu 缁勪欢鐨� generateMenu 鍑芥暟涓紝纭繚 MenuItemLink 涔熷乏瀵归綈
   const generateMenu = (permissions) => {
     return permissions.map((node) => {
-        if (node.children) {
-            const selected = isSelected(node.component) || hasSelectedChild(node);
-            return (
-                <SubMenu
-                    key={node.id}
-                    handleToggle={() => handleToggle(node.route)}
-                    isOpen={state[node.route]}
-                    name={node.name}
-                    dense={dense}
-                    icon={getIcon(node.icon)}
-                    isSelected={selected}
-                >
-                    {generateMenu(node.children)}
-                </SubMenu>
-            );
-        } else {
-            if (node.component) {
-                const selected = isSelected(node.component);
-                // 鍦� generateMenu 鍑芥暟涓殑 MenuItemLink 閮ㄥ垎
-                return (
-                    <MenuItemLink
-                        key={node.id}
-                        to={node.component}
-                        state={{ _scrollToTop: true }}
-                        primaryText={translate(node.name)}
-                        leftIcon={getIcon(node.icon)}
-                        dense={dense}
-                        sx={{
-                            backgroundColor: selected ? 'rgba(25, 118, 210, 0.08) !important' : 'transparent',
-                            color: selected ? '#1976d2 !important' : 'text.secondary',
-                            '&:hover': {
-                                backgroundColor: selected ? 'rgba(25, 118, 210, 0.12) !important' : 'rgba(0, 0, 0, 0.04)',
-                            },
-                            borderLeft: 'none',
-                            borderRadius: '4px',
-                            margin: '2px 8px',
-                            width: 'calc(100% - 16px)',
-                            transition: 'all 0.2s ease-in-out',
-                            
-                            // 缂╁皬鏁翠綋闂磋窛
-                            padding: '6px 8px', // 鍑忓皯鍐呰竟璺�
-                            minHeight: '36px', // 绋嶅井鍑忓皬楂樺害
-                            
-                            '& .RaMenuItemLink-icon': {
-                                color: selected ? '#1976d2 !important' : 'text.secondary',
-                                minWidth: '32px !important', // 缂╁皬鍥炬爣鍖哄煙瀹藉害
-                                marginRight: '4px', // 缂╁皬鍥炬爣鍜屾枃瀛楅棿璺�
-                                display: 'flex',
-                                alignItems: 'center',
-                                justifyContent: 'center', // 鍥炬爣灞呬腑鏄剧ず
-                            },
-                            
-                            fontWeight: selected ? 600 : 400,
-                            
-                            // 纭繚鏂囧瓧鍐呭宸﹀榻�
-                            '& .MuiListItemText-root': {
-                                margin: 0,
-                                '& .MuiTypography-root': {
-                                    textAlign: 'left',
-                                    justifyContent: 'flex-start',
-                                    fontSize: '0.875rem', // 绋嶅井鍑忓皬瀛椾綋澶у皬
-                                    lineHeight: '1.3',
-                                }
-                            },
-                        }}
-                    />
-                );
-            }
+      if (node.children) {
+        const selected = isSelected(node.component) || hasSelectedChild(node);
+        return (
+          <SubMenu
+            key={node.id}
+            handleToggle={() => handleToggle(node.route)}
+            isOpen={state[node.route]}
+            name={node.name}
+            dense={dense}
+            icon={getIcon(node.icon)}
+            isSelected={selected}
+          >
+            {generateMenu(node.children)}
+          </SubMenu>
+        );
+      } else {
+        if (node.component) {
+          const selected = isSelected(node.component);
+          // 鍦� generateMenu 鍑芥暟涓殑 MenuItemLink 閮ㄥ垎
+          return (
+            <MenuItemLink
+              key={node.id}
+              to={node.component}
+              state={{ _scrollToTop: true }}
+              primaryText={translate(node.name)}
+              leftIcon={getIcon(node.icon)}
+              dense={dense}
+              sx={{
+                backgroundColor: selected ? 'rgba(25, 118, 210, 0.08) !important' : 'transparent',
+                color: selected ? '#1976d2 !important' : 'text.secondary',
+                '&:hover': {
+                  backgroundColor: selected ? 'rgba(25, 118, 210, 0.12) !important' : 'rgba(0, 0, 0, 0.04)',
+                },
+                borderLeft: 'none',
+                borderRadius: '4px',
+                margin: '2px 8px',
+                width: 'calc(100% - 16px)',
+                transition: 'all 0.2s ease-in-out',
+
+                // 缂╁皬鏁翠綋闂磋窛
+                padding: '6px 8px', // 鍑忓皯鍐呰竟璺�
+                minHeight: '36px', // 绋嶅井鍑忓皬楂樺害
+
+                '& .RaMenuItemLink-icon': {
+                  color: selected ? '#1976d2 !important' : 'text.secondary',
+                  minWidth: '32px !important', // 缂╁皬鍥炬爣鍖哄煙瀹藉害
+                  marginRight: '4px', // 缂╁皬鍥炬爣鍜屾枃瀛楅棿璺�
+                  display: 'flex',
+                  alignItems: 'center',
+                  justifyContent: 'center', // 鍥炬爣灞呬腑鏄剧ず
+                },
+
+                fontWeight: selected ? 600 : 400,
+
+                // 纭繚鏂囧瓧鍐呭宸﹀榻�
+                '& .MuiListItemText-root': {
+                  margin: 0,
+                  '& .MuiTypography-root': {
+                    textAlign: 'left',
+                    justifyContent: 'flex-start',
+                    fontSize: '0.875rem', // 绋嶅井鍑忓皬瀛椾綋澶у皬
+                    lineHeight: '1.3',
+                  }
+                },
+              }}
+            />
+          );
         }
+      }
     });
   };
 
diff --git a/rsf-admin/src/layout/TabsBar.jsx b/rsf-admin/src/layout/TabsBar.jsx
index bd77113..c346104 100644
--- a/rsf-admin/src/layout/TabsBar.jsx
+++ b/rsf-admin/src/layout/TabsBar.jsx
@@ -297,19 +297,24 @@
                                 <span>{getTabLabel(tab)}</span>
                                 {tab.closable && (
                                     <Tooltip title="鍏抽棴">
-                                        <IconButton
-                                            size="small"
+                                        <Box
+                                            component="span"
                                             onClick={(e) => handleCloseTab(e, tab.path)}
                                             sx={{
+                                                display: 'inline-flex',
+                                                alignItems: 'center',
+                                                justifyContent: 'center',
                                                 p: 0.25,
                                                 ml: 0.5,
+                                                borderRadius: '50%',
+                                                cursor: 'pointer',
                                                 '&:hover': {
                                                     backgroundColor: 'rgba(0, 0, 0, 0.1)',
                                                 },
                                             }}
                                         >
                                             <CloseIcon sx={{ fontSize: 14 }} />
-                                        </IconButton>
+                                        </Box>
                                     </Tooltip>
                                 )}
                             </Box>
diff --git a/rsf-admin/src/layout/index.jsx b/rsf-admin/src/layout/index.jsx
index 293ae54..95e8f93 100644
--- a/rsf-admin/src/layout/index.jsx
+++ b/rsf-admin/src/layout/index.jsx
@@ -31,7 +31,8 @@
       <Box sx={{
         position: 'fixed',
         top: 48,
-        left: sidebarWidth,
+        // left: 0,
+        left: sidebarWidth + 5,
         right: 0,
         zIndex: 1100,
         transition: (theme) =>
diff --git a/rsf-admin/src/page/basicInfo/loc/InitModal.jsx b/rsf-admin/src/page/basicInfo/loc/InitModal.jsx
index 38a3297..ce39a9e 100644
--- a/rsf-admin/src/page/basicInfo/loc/InitModal.jsx
+++ b/rsf-admin/src/page/basicInfo/loc/InitModal.jsx
@@ -232,7 +232,7 @@
                                     onChange={(e) => handleChange(+e.target.value, 'channel')}
                                     size="small"
                                     type="number"
-                                    validate={[required()]}
+                                    // validate={[required()]}
                                 />
                             </Grid>
                             <Grid item xs={4}>
@@ -243,7 +243,7 @@
                                     onChange={(e) => handleChange(+e.target.value, 'startChannel')}
                                     size="small"
                                     type="number"
-                                    validate={[required()]}
+                                    // validate={[required()]}
                                 />
                             </Grid>
                         </Grid>
diff --git a/rsf-admin/src/page/components/PageDrawer.jsx b/rsf-admin/src/page/components/PageDrawer.jsx
index 659e849..521ee7e 100644
--- a/rsf-admin/src/page/components/PageDrawer.jsx
+++ b/rsf-admin/src/page/components/PageDrawer.jsx
@@ -29,10 +29,15 @@
             open={!!drawerVal}
             anchor="right"
             onClose={handleClose}
-            sx={{ zIndex: 100 }}
+            sx={{
+                zIndex: 100,
+                '& .MuiDrawer-paper': {
+                    top: '86px', // AppBar(50px) + TabsBar(36px)                    
+                }
+            }}
         >
             {!!drawerVal && (
-                <Box pt={5} width={{ xs: '100vW', sm: width }} height={'calc(100vh - 200px);'} mt={{ xs: 2, sm: 1 }}>
+                <Box pt={2} width={{ xs: '100vW', sm: width }} mt={{ xs: 2, sm: 1 }}>
                     <Stack direction="row" p={2}>
                         <Typography variant="h6" flex="1">
                             {title}
diff --git a/rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuCreate.jsx b/rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuCreate.jsx
new file mode 100644
index 0000000..4c04e18
--- /dev/null
+++ b/rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuCreate.jsx
@@ -0,0 +1,125 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import {
+    CreateBase,
+    useTranslate,
+    TextInput,
+    NumberInput,
+    BooleanInput,
+    DateInput,
+    SaveButton,
+    SelectInput,
+    ReferenceInput,
+    ReferenceArrayInput,
+    AutocompleteInput,
+    Toolbar,
+    required,
+    useDataProvider,
+    useNotify,
+    Form,
+    useCreateController,
+} from 'react-admin';
+import {
+    Dialog,
+    DialogActions,
+    DialogContent,
+    DialogTitle,
+    Stack,
+    Grid,
+    Box,
+} from '@mui/material';
+import DialogCloseButton from "../components/DialogCloseButton";
+import StatusSelectInput from "../components/StatusSelectInput";
+import MemoInput from "../components/MemoInput";
+
+const MatnrRoleMenuCreate = (props) => {
+    const { open, setOpen } = props;
+
+    const translate = useTranslate();
+    const notify = useNotify();
+
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+        }
+    };
+
+    const handleSuccess = async (data) => {
+        setOpen(false);
+        notify('common.response.success');
+    };
+
+    const handleError = async (error) => {
+        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
+    };
+
+    return (
+        <>
+            <CreateBase
+                record={{}}
+                transform={(data) => {
+                    return data;
+                }}
+                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
+            >
+                <Dialog
+                    open={open}
+                    onClose={handleClose}
+                    aria-labelledby="form-dialog-title"
+                    fullWidth
+                    disableRestoreFocus
+                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
+                >
+                    <Form>
+                        <DialogTitle id="form-dialog-title" sx={{
+                            position: 'sticky',
+                            top: 0,
+                            backgroundColor: 'background.paper',
+                            zIndex: 1000
+                        }}
+                        >
+                            {translate('create.title')}
+                            <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={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.matnrRoleMenu.roleId"
+                                        source="roleId"
+                                        autoFocus
+                                        validate={required()}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.matnrRoleMenu.menuId"
+                                        source="menuId"
+                                        validate={required()}
+                                    />
+                                </Grid>
+
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <StatusSelectInput />
+                                </Grid>
+                                <Grid item xs={12} display="flex" gap={1}>
+                                    <Stack direction="column" spacing={1} width={'100%'}>
+                                        <MemoInput />
+                                    </Stack>
+                                </Grid>
+                            </Grid>
+                        </DialogContent>
+                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
+                                <SaveButton />
+                            </Toolbar>
+                        </DialogActions>
+                    </Form>
+                </Dialog>
+            </CreateBase>
+        </>
+    )
+}
+
+export default MatnrRoleMenuCreate;
diff --git a/rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuEdit.jsx b/rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuEdit.jsx
new file mode 100644
index 0000000..534f128
--- /dev/null
+++ b/rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuEdit.jsx
@@ -0,0 +1,97 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import {
+    Edit,
+    SimpleForm,
+    FormDataConsumer,
+    useTranslate,
+    TextInput,
+    NumberInput,
+    BooleanInput,
+    DateInput,
+    SelectInput,
+    ReferenceInput,
+    ReferenceArrayInput,
+    AutocompleteInput,
+    SaveButton,
+    Toolbar,
+    Labeled,
+    NumberField,
+    required,
+    useRecordContext,
+    DeleteButton,
+} from 'react-admin';
+import { useWatch, useFormContext } from "react-hook-form";
+import { Stack, Grid, Box, Typography } from '@mui/material';
+import * as Common from '@/utils/common';
+import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
+import EditBaseAside from "../components/EditBaseAside";
+import CustomerTopToolBar from "../components/EditTopToolBar";
+import MemoInput from "../components/MemoInput";
+import StatusSelectInput from "../components/StatusSelectInput";
+
+const FormToolbar = () => {
+    const { getValues } = useFormContext();
+
+    return (
+        <Toolbar sx={{ justifyContent: 'space-between' }}>
+            <SaveButton />
+            <DeleteButton mutationMode="optimistic" />
+        </Toolbar>
+    )
+}
+
+const MatnrRoleMenuEdit = () => {
+    const translate = useTranslate();
+
+    return (
+        <Edit
+            redirect="list"
+            mutationMode={EDIT_MODE}
+            actions={<CustomerTopToolBar />}
+            aside={<EditBaseAside />}
+        >
+            <SimpleForm
+                shouldUnregister
+                warnWhenUnsavedChanges
+                toolbar={<FormToolbar />}
+                mode="onTouched"
+                defaultValues={{}}
+            // validate={(values) => { }}
+            >
+                <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
+                    <Grid item xs={12} md={8}>
+                        <Typography variant="h6" gutterBottom>
+                            {translate('common.edit.title.main')}
+                        </Typography>
+                        <Stack direction='row' gap={2}>
+                            <NumberInput
+                                label="table.field.matnrRoleMenu.roleId"
+                                source="roleId"
+                                autoFocus
+                                validate={required()}
+                            />
+                        </Stack>
+                        <Stack direction='row' gap={2}>
+                            <NumberInput
+                                label="table.field.matnrRoleMenu.menuId"
+                                source="menuId"
+                                validate={required()}
+                            />
+                        </Stack>
+
+                    </Grid>
+                    <Grid item xs={12} md={4}>
+                        <Typography variant="h6" gutterBottom>
+                            {translate('common.edit.title.common')}
+                        </Typography>
+                        <StatusSelectInput />
+                        <Box mt="2em" />
+                        <MemoInput />
+                    </Grid>
+                </Grid>
+            </SimpleForm>
+        </Edit >
+    )
+}
+
+export default MatnrRoleMenuEdit;
diff --git a/rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuList.jsx b/rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuList.jsx
new file mode 100644
index 0000000..9044c18
--- /dev/null
+++ b/rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuList.jsx
@@ -0,0 +1,154 @@
+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,
+} from 'react-admin';
+import { Box, Typography, Card, Stack } from '@mui/material';
+import { styled } from '@mui/material/styles';
+import MatnrRoleMenuCreate from "./MatnrRoleMenuCreate";
+import MatnrRoleMenuPanel from "./MatnrRoleMenuPanel";
+import EmptyData from "../components/EmptyData";
+import MyCreateButton from "../components/MyCreateButton";
+import MyExportButton from '../components/MyExportButton';
+import PageDrawer from "../components/PageDrawer";
+import MyField from "../components/MyField";
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
+import * as Common from '@/utils/common';
+
+const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
+    '& .css-1vooibu-MuiSvgIcon-root': {
+        height: '.9em'
+    },
+    '& .RaDatagrid-row': {
+        cursor: 'auto'
+    },
+    '& .column-name': {
+    },
+    '& .opt': {
+        width: 200
+    },
+}));
+
+const filters = [
+    <SearchInput source="condition" alwaysOn />,
+    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
+    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
+
+    <NumberInput source="roleId" label="table.field.matnrRoleMenu.roleId" />,
+    <NumberInput source="menuId" label="table.field.matnrRoleMenu.menuId" />,
+
+    <TextInput label="common.field.memo" source="memo" />,
+    <SelectInput
+        label="common.field.status"
+        source="status"
+        choices={[
+            { id: '1', name: 'common.enums.statusTrue' },
+            { id: '0', name: 'common.enums.statusFalse' },
+        ]}
+        resettable
+    />,
+]
+
+const MatnrRoleMenuList = () => {
+    const translate = useTranslate();
+
+    const [createDialog, setCreateDialog] = useState(false);
+    const [drawerVal, setDrawerVal] = useState(false);
+
+    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.matnrRoleMenu"}
+                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
+                filters={filters}
+                sort={{ field: "create_time", order: "desc" }}
+                actions={(
+                    <TopToolbar>
+                        <FilterButton />
+                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
+                        <SelectColumnsButton preferenceKey='matnrRoleMenu' />
+                        <MyExportButton />
+                    </TopToolbar>
+                )}
+                perPage={DEFAULT_PAGE_SIZE}
+            >
+                <StyledDatagrid
+                    preferenceKey='matnrRoleMenu'
+                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
+                    rowClick={(id, resource, record) => false}
+                    expand={() => <MatnrRoleMenuPanel />}
+                    expandSingle={true}
+                    omit={['id', 'createTime', 'createBy', 'memo']}
+                >
+                    <NumberField source="id" />
+                    <NumberField source="roleId" label="table.field.matnrRoleMenu.roleId" />
+                    <NumberField source="menuId" label="table.field.matnrRoleMenu.menuId" />
+
+                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
+                        <TextField source="nickname" />
+                    </ReferenceField>
+                    <DateField source="updateTime" label="common.field.updateTime" showTime />
+                    <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
+                        <TextField source="nickname" />
+                    </ReferenceField>
+                    <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">
+                        <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
+                        <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
+                    </WrapperField>
+                </StyledDatagrid>
+            </List>
+            <MatnrRoleMenuCreate
+                open={createDialog}
+                setOpen={setCreateDialog}
+            />
+            <PageDrawer
+                title='MatnrRoleMenu Detail'
+                drawerVal={drawerVal}
+                setDrawerVal={setDrawerVal}
+            >
+            </PageDrawer>
+        </Box>
+    )
+}
+
+export default MatnrRoleMenuList;
diff --git a/rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuPanel.jsx b/rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuPanel.jsx
new file mode 100644
index 0000000..e1dd09d
--- /dev/null
+++ b/rsf-admin/src/page/matnrRoleMenu/MatnrRoleMenuPanel.jsx
@@ -0,0 +1,63 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material';
+import {
+    useTranslate,
+    useRecordContext,
+} from 'react-admin';
+import PanelTypography from "../components/PanelTypography";
+import * as Common from '@/utils/common'
+
+const MatnrRoleMenuPanel = () => {
+    const record = useRecordContext();
+    if (!record) return null;
+    const translate = useTranslate();
+    return (
+        <>
+            <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}>
+                <CardContent>
+                    <Grid container spacing={2}>
+                        <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}>
+                            <Typography variant="h6" gutterBottom align="left" sx={{
+                                maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' },
+                                whiteSpace: 'nowrap',
+                                overflow: 'hidden',
+                                textOverflow: 'ellipsis',
+                            }}>
+                                {Common.camelToPascalWithSpaces(translate('table.field.matnrRoleMenu.id'))}: {record.id}
+                            </Typography>
+                            {/*  inherit, primary, secondary, textPrimary, textSecondary, error */}
+                            <Typography variant="h6" gutterBottom align="right" >
+                                ID: {record.id}
+                            </Typography>
+                        </Grid>
+                    </Grid>
+                    <Grid container spacing={2}>
+                        <Grid item xs={12} container alignContent="flex-end">
+                            <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
+                                {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo}
+                            </Typography>
+                        </Grid>
+                    </Grid>
+                    <Box height={20}>&nbsp;</Box>
+                    <Grid container spacing={2}>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.matnrRoleMenu.roleId" 
+                                property={record.roleId}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.matnrRoleMenu.menuId" 
+                                property={record.menuId}
+                            />
+                        </Grid>
+
+                    </Grid>
+                </CardContent>
+            </Card >
+        </>
+    );
+};
+
+export default MatnrRoleMenuPanel;
diff --git a/rsf-admin/src/page/matnrRoleMenu/index.jsx b/rsf-admin/src/page/matnrRoleMenu/index.jsx
new file mode 100644
index 0000000..385d043
--- /dev/null
+++ b/rsf-admin/src/page/matnrRoleMenu/index.jsx
@@ -0,0 +1,18 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import {
+    ListGuesser,
+    EditGuesser,
+    ShowGuesser,
+} from "react-admin";
+
+import MatnrRoleMenuList from "./MatnrRoleMenuList";
+import MatnrRoleMenuEdit from "./MatnrRoleMenuEdit";
+
+export default {
+    list: MatnrRoleMenuList,
+    edit: MatnrRoleMenuEdit,
+    show: ShowGuesser,
+    recordRepresentation: (record) => {
+        return `${record.id}`
+    }
+};
diff --git a/rsf-admin/src/page/orders/asnOrder/AsnOrderModal.jsx b/rsf-admin/src/page/orders/asnOrder/AsnOrderModal.jsx
index d26ea4c..abe737c 100644
--- a/rsf-admin/src/page/orders/asnOrder/AsnOrderModal.jsx
+++ b/rsf-admin/src/page/orders/asnOrder/AsnOrderModal.jsx
@@ -706,7 +706,7 @@
                         },
                     },
                 }}
-                pageSizeOptions={[15, 25, 50, 100]}
+                pageSizeOptions={[10, 20, 50, 100]}
                 editMode="row"
                 checkboxSelection
                 onRowSelectionModelChange={handleSelectionChange}
diff --git a/rsf-admin/src/page/orders/asnOrder/AsnOrderPanel.jsx b/rsf-admin/src/page/orders/asnOrder/AsnOrderPanel.jsx
index 1353f3b..d99b320 100644
--- a/rsf-admin/src/page/orders/asnOrder/AsnOrderPanel.jsx
+++ b/rsf-admin/src/page/orders/asnOrder/AsnOrderPanel.jsx
@@ -175,7 +175,7 @@
                         },
                     },
                 }}
-                pageSizeOptions={[15, 25, 50]}
+                pageSizeOptions={[10, 20, 50]}
             />
         </Box >
 
diff --git a/rsf-admin/src/page/system/role/AssignPermissions_matnr.jsx b/rsf-admin/src/page/system/role/AssignPermissions_matnr.jsx
new file mode 100644
index 0000000..ca9a9f6
--- /dev/null
+++ b/rsf-admin/src/page/system/role/AssignPermissions_matnr.jsx
@@ -0,0 +1,380 @@
+import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
+import {
+    useTranslate,
+    useNotify,
+    TextInput
+} from 'react-admin';
+import { Box, Button, Card, Stack, CardContent, Skeleton, TextField } from '@mui/material';
+import { SimpleTreeView, TreeItem, RichTreeView, useTreeViewApiRef } from '@mui/x-tree-view';
+import SaveIcon from '@mui/icons-material/Save';
+import request from '@/utils/request'
+
+const DEFAULT_EXPAND_ALL = true;
+
+const AssignPermissionsMatnr = (props) => {
+    const { role, originMenuIds, setDrawerVal, closeCallback, authType } = props;
+    const translate = useTranslate();
+    const notify = useNotify();
+
+    const [loading, setLoading] = useState(false);
+    const [treeData, setTreeData] = useState([]);
+    const [selectedItems, setSelectedItems] = useState([]);
+    const [expandedItems, setExpandedItems] = useState([]);
+    const [parmas, setParmas] = useState({ condition: '', authType: authType });
+    const toggledItemRef = useRef({});
+    const apiRef = useTreeViewApiRef();
+
+    useEffect(() => {
+        reload()
+    }, [role, originMenuIds])
+
+    const reload = () => {
+        setSelectedItems(originMenuIds.map(item => item + ""));
+
+        const transformTree = (treeData) => {
+            return treeData.map(data => {
+                return {
+                    id: data.id + '',
+                    label: data.type === 0 ? translate(data.name || data.code) : data.name || data.code,
+                    type: data.type,
+                    children: (data.children && data.children.length > 0 ? transformTree(data.children) : null)
+                }
+            })
+        }
+        const http = async () => {
+            const res = await request.post('/menuMatnrGroup/tree', parmas);
+            if (res?.data?.code === 200) {
+                const transformData = transformTree(res.data.data);
+                setTreeData(transformData);
+                if (DEFAULT_EXPAND_ALL) {
+                    setExpandedItems(getAllItemsWithChildrenItemIds(transformData));
+                }
+            } else {
+                notify(res.data.msg, { type: 'error' });
+            }
+            setLoading(false);
+        }
+        setLoading(true);
+        setTimeout(() => {
+            http();
+        }, 200);
+    }
+
+
+    const getAllItemItemIds = () => {
+        const ids = [];
+        const registerItemId = (item) => {
+            ids.push(item.id);
+            item.children?.forEach(registerItemId);
+        };
+        treeData.forEach(registerItemId);
+        return ids;
+    };
+
+    const getAllItemsWithChildrenItemIds = (treeDataHandle) => {
+        const itemIds = [];
+        const registerItemId = (item) => {
+            if (item.children?.length) {
+                itemIds.push(item.id);
+                item.children.forEach(registerItemId);
+            }
+        };
+        if (treeDataHandle) {
+            treeDataHandle.forEach(registerItemId);
+        } else {
+            treeData.forEach(registerItemId);
+        }
+        return itemIds;
+    };
+
+    const handleSelectedItemsChange = (event, newSelectedItems) => {
+        const itemsToSelect = [];
+        const itemsToUnSelect = {};
+        Object.entries(toggledItemRef.current).forEach(([itemId, isSelected]) => {
+            const item = apiRef.current.getItem(itemId);
+            if (isSelected) {
+                itemsToSelect.push(...getItemDescendantsIds(item));
+
+                const parentIds = getParentIds(treeData, itemId);
+                itemsToSelect.push(...parentIds);
+            } else {
+                // 鍙栨秷瀛愯妭鐐�
+                const treeNode = checkoutTreeNode(treeData, itemId);
+                if (treeNode?.children && treeNode?.children.length > 0) {
+                    const allChildren = getItemDescendantsIds(treeNode);
+                    const childrenSet = new Set(allChildren);
+                    newSelectedItems = newSelectedItems.filter(item => !childrenSet.has(item));
+                }
+
+                // 鍙栨秷鐖惰妭鐐�
+                const removeParentIfAllSiblingsDeselected = (itemId, newSelectedItems, treeData) => {
+                    let updatedSelectedItems = [...newSelectedItems];
+                    let currentId = itemId;
+                    while (true) {
+                        const parentId = getParentId(treeData, currentId);
+                        if (!parentId) break;
+                        const siblings = getChildrenIds(treeData, parentId);
+                        const allSiblingsDeselected = siblings.every(siblingId => !updatedSelectedItems.includes(siblingId));
+                        if (allSiblingsDeselected) {
+                            updatedSelectedItems = updatedSelectedItems.filter(id => id !== parentId);
+                            currentId = parentId;
+                        } else {
+                            break;
+                        }
+                    }
+                    return updatedSelectedItems;
+                };
+                newSelectedItems = removeParentIfAllSiblingsDeselected(itemId, newSelectedItems, treeData);
+            }
+        });
+
+        const newSelectedItemsWithChildren = Array.from(
+            new Set(
+                [...newSelectedItems, ...itemsToSelect].filter(
+                    (itemId) => !itemsToUnSelect[itemId],
+                ),
+            ),
+        );
+
+        setSelectedItems(newSelectedItemsWithChildren);
+
+        toggledItemRef.current = {};
+    };
+
+    const handleSave = (event) => {
+        request.post('/roleMatnr/scope/update', {
+            id: role.id,
+            menuIds: {
+                checked: selectedItems,
+                halfChecked: []
+            }
+        }).then(res => {
+            if (res?.data.code === 200) {
+                setDrawerVal(null);
+                if (closeCallback) {
+                    closeCallback();
+                }
+                notify(res.data.msg, { type: 'info', messageArgs: { _: res.data.msg } })
+            } else {
+                notify(res.data.msg, { type: 'error' })
+            }
+
+        })
+    }
+
+    const search = (e) => {
+        const value = e.target.value;
+        setParmas({
+            ...parmas,
+            condition: value
+        })
+        reload()
+
+    }
+
+    return (
+        <>
+            <Card sx={{
+                ml: 1,
+                mr: 1,
+                height: 'calc(100vh - 140px)',
+                overflowY: 'auto'
+            }}>
+                <CardContent sx={{
+                    overflow: 'auto',
+                    display: 'flex',
+                    flexDirection: 'column',
+                    justifyContent: 'space-between'
+                }}>
+                    <Box>
+                        <Box mb={1} sx={{
+                            display: 'flex',
+                            justifyContent: 'space-between'
+                        }}>
+                            <Button onClick={() => {
+                                setSelectedItems((oldSelected) =>
+                                    oldSelected.length === 0 ? getAllItemItemIds() : [],
+                                );
+                            }}>
+                                {selectedItems.length === 0 ? translate('ra.action.select_all') : translate('ra.action.unselect')}
+                            </Button>
+                            <Button onClick={() => {
+                                setExpandedItems((oldExpanded) =>
+                                    oldExpanded.length === 0 ? getAllItemsWithChildrenItemIds() : [],
+                                );
+                            }}>
+                                {expandedItems.length === 0 ? translate('common.action.expandAll') : translate('common.action.collapseAll')}
+                            </Button>
+                        </Box>
+                        <Box sx={{
+                            display: 'flex',
+                            justifyContent: 'space-between',
+                            alignItems: 'center'
+                        }}>
+                            <TextField sx={{ width: '200px' }} label="鎼滅储鑿滃崟" variant="outlined" value={parmas.condition} onChange={(e) => search(e)} />
+                            <Button startIcon={<SaveIcon />} size="small" variant="contained" onClick={handleSave} sx={{ height: '40px' }}>
+                                {translate('ra.action.save')}
+                            </Button>
+                        </Box>
+
+                        <Box sx={{
+                            minWidth: 290,
+                            overflow: 'auto',
+                            marginTop: '10px',
+                            padding: 1,
+                            borderBottom: '1px solid background.paper',
+                            borderRadius: '4px',
+                            boxShadow: '0 1px 2px rgba(0, 0, 0, 0.2)',
+                            backgroundColor: 'background.paper',
+                        }}>
+                            {loading ? (
+                                <SkeletonBox />
+                            ) : (
+                                <RichTreeView
+                                    multiSelect
+                                    checkboxSelection
+                                    apiRef={apiRef}
+                                    items={treeData}
+                                    selectedItems={selectedItems}
+                                    onSelectedItemsChange={handleSelectedItemsChange}
+                                    onItemSelectionToggle={(event, itemId, isSelected) => {
+                                        toggledItemRef.current[itemId] = isSelected;
+                                    }}
+
+                                    expandedItems={expandedItems}
+                                    onExpandedItemsChange={(event, itemIds) => {
+                                        setExpandedItems(itemIds);
+                                    }}
+                                />
+                            )}
+
+                        </Box>
+                    </Box>
+
+                </CardContent>
+            </Card>
+        </>
+    )
+}
+
+const checkoutTreeNode = (treeData, targetId) => {
+    let result = null;
+    const checkout = (node) => {
+        if (node.id === targetId) {
+            result = node;
+            return true;
+        } else {
+            if (node.children) {
+                for (const child of node.children) {
+                    if (checkout(child)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    };
+    treeData.forEach(item => {
+        if (checkout(item)) {
+            return;
+        }
+    });
+    return result;
+};
+
+const getItemDescendantsIds = (item) => {
+    const ids = [];
+    item.children?.forEach((child) => {
+        ids.push(child.id);
+        ids.push(...getItemDescendantsIds(child));
+    });
+    return ids;
+}
+
+const getParentIds = (tree, targetId) => {
+    let parentIds = [];
+    const searchTree = (node, path = []) => {
+        if (node.id === targetId) {
+            parentIds = [...path];
+            return true;
+        }
+        if (node.children) {
+            for (const child of node.children) {
+                if (searchTree(child, [...path, node.id])) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    };
+    tree.forEach(item => {
+        searchTree(item);
+    })
+    return parentIds;
+};
+
+const getParentId = (tree, targetId) => {
+    let parentId = null;
+    const searchTree = (node) => {
+        if (node.children) {
+            for (const child of node.children) {
+                if (child.id === targetId) {
+                    parentId = node.id;
+                    return true;
+                }
+                if (searchTree(child)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    };
+    tree.forEach(item => {
+        if (searchTree(item)) {
+            return parentId;
+        }
+    });
+    return parentId;
+};
+
+const getChildrenIds = (tree, targetId) => {
+    let childrenIds = [];
+    const searchTree = (node) => {
+        if (node.id === targetId && node.children) {
+            childrenIds = node.children.map(child => child.id);
+        } else if (node.children) {
+            for (const child of node.children) {
+                searchTree(child);
+            }
+        }
+    };
+    tree.forEach(item => {
+        searchTree(item);
+    });
+    return childrenIds;
+};
+
+const SkeletonBox = () => {
+    return (
+        <Stack spacing={1}>
+            <Skeleton variant="rounded" width={200} height={20} />
+            <Skeleton variant="rounded" width={200} height={20} style={{ marginLeft: '50px' }} />
+            <Skeleton variant="rounded" width={200} height={20} style={{ marginLeft: '50px' }} />
+            <Skeleton variant="rounded" width={200} height={20} style={{ marginLeft: '50px' }} />
+            <Skeleton variant="rounded" width={200} height={20} />
+            <Skeleton variant="rounded" width={200} height={20} style={{ marginLeft: '50px' }} />
+            <Skeleton variant="rounded" width={200} height={20} style={{ marginLeft: '50px' }} />
+            <Skeleton variant="rounded" width={200} height={20} style={{ marginLeft: '50px' }} />
+            <Skeleton variant="rounded" width={200} height={20} />
+            <Skeleton variant="rounded" width={200} height={20} style={{ marginLeft: '50px' }} />
+            <Skeleton variant="rounded" width={200} height={20} style={{ marginLeft: '50px' }} />
+            <Skeleton variant="rounded" width={200} height={20} style={{ marginLeft: '50px' }} />
+            <Skeleton variant="rounded" width={200} height={20} />
+            <Skeleton variant="rounded" width={200} height={20} style={{ marginLeft: '50px' }} />
+            <Skeleton variant="rounded" width={200} height={20} style={{ marginLeft: '50px' }} />
+            <Skeleton variant="rounded" width={200} height={20} style={{ marginLeft: '50px' }} />
+        </Stack>
+    )
+}
+
+export default AssignPermissionsMatnr;
\ No newline at end of file
diff --git a/rsf-admin/src/page/system/role/RoleList.jsx b/rsf-admin/src/page/system/role/RoleList.jsx
index 6ef91f5..0c5ea8f 100644
--- a/rsf-admin/src/page/system/role/RoleList.jsx
+++ b/rsf-admin/src/page/system/role/RoleList.jsx
@@ -30,7 +30,8 @@
     useNotify,
     Button,
 } from 'react-admin';
-import { Box, Card, Stack } from '@mui/material';
+import { Box, Card, Stack, Menu, MenuItem, ListItemIcon, ListItemText } from '@mui/material';
+import SecurityIcon from '@mui/icons-material/Security';
 import { styled } from '@mui/material/styles';
 import RoleCreate from "./RoleCreate";
 import RolePanel from "./RolePanel";
@@ -43,9 +44,12 @@
 import * as Common from '@/utils/common';
 import AssignPermissions from "./AssignPermissions";
 import AssignPermissionsPda from "./AssignPermissions_pda";
+import AssignPermissionsMatnr from "./AssignPermissions_matnr";
 import request from '@/utils/request';
 import AssignmentIndIcon from '@mui/icons-material/AssignmentInd';
 import AdUnitsIcon from '@mui/icons-material/AdUnits';
+import ArticleIcon from '@mui/icons-material/Article';
+import { margin } from "@mui/system";
 
 const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
     '& .css-1vooibu-MuiSvgIcon-root': {
@@ -83,12 +87,14 @@
     const [createDialog, setCreateDialog] = useState(false);
     const [drawerVal, setDrawerVal] = useState(false);
     const [drawerValPda, setDrawerValPda] = useState(false);
+    const [drawerValMatnr, setDrawerValMatnr] = useState(false);
 
     const [menuIds, setMenuIds] = useState([]);
 
     const [authType, setAuthType] = useState(0)
 
     const assign = (record) => {
+        setDrawerValPda(false);
         request('/role/scope/list', {
             method: 'GET',
             params: {
@@ -106,6 +112,7 @@
     }
 
     const assignPda = (record) => {
+        setDrawerVal(false);
         request('/rolePda/scope/list', {
             method: 'GET',
             params: {
@@ -122,6 +129,24 @@
         });
     }
 
+    const assignMatnr = (record) => {
+        setDrawerVal(false);
+        request('/roleMatnr/scope/list', {
+            method: 'GET',
+            params: {
+                roleId: record.id
+            }
+        }).then((res) => {
+            if (res?.data?.code === 200) {
+                const { data: menuIds } = res.data;
+                setMenuIds(menuIds || []);
+                setDrawerValMatnr(!!drawerValMatnr && drawerValMatnr === record ? null : record);
+            } else {
+                notify(res.data.msg, { type: 'error' });
+            }
+        });
+    }
+
     return (
         <Box display="flex">
             <List
@@ -131,7 +156,7 @@
                         theme.transitions.create(['all'], {
                             duration: theme.transitions.duration.enteringScreen,
                         }),
-                    marginRight: (!!drawerVal || !!drawerValPda) ? `${PAGE_DRAWER_WIDTH}px` : 0,
+                    marginRight: (!!drawerVal || !!drawerValPda || !!drawerValMatnr) ? `${PAGE_DRAWER_WIDTH}px` : 0,
                 }}
                 title={"menu.role"}
                 empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
@@ -167,9 +192,12 @@
                     <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">
-                        <ScopeButton sx={{ padding: '1px', fontSize: '.75rem' }} assign={assign} auType={0} setAuthType={setAuthType} label="缃戦〉鏉冮檺&nbsp;&nbsp;&nbsp;" />
-                        <PdaScopeButton sx={{ padding: '1px', fontSize: '.75rem' }} assignPda={assignPda} auType={1} setAuthType={setAuthType} label="PDA鏉冮檺&nbsp;&nbsp;&nbsp;" />
-                        <ScopeButton sx={{ padding: '1px', fontSize: '.75rem' }} assign={assign} auType={2} setAuthType={setAuthType} label="浠撳簱鏉冮檺&nbsp;" />
+                        <PermissionMenuButton
+                            assign={assign}
+                            assignPda={assignPda}
+                            assignMatnr={assignMatnr}
+                            setAuthType={setAuthType}
+                        />
                         <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
                         <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
                     </WrapperField>
@@ -215,46 +243,109 @@
                     authType={authType}
                 />
             </PageDrawer>
+            <PageDrawer
+                drawerVal={drawerValMatnr}
+                setDrawerVal={setDrawerValMatnr}
+                title={!!drawerValMatnr ? `Scope by ${drawerValMatnr.code || drawerValMatnr.name}` : 'Role Detail'}
+                closeCallback={() => {
+                    setMenuIds([]);
+                }}
+            >
+                <AssignPermissionsMatnr
+                    role={drawerValMatnr}
+                    originMenuIds={menuIds}
+                    setDrawerVal={setDrawerValMatnr}
+                    closeCallback={() => {
+                        setMenuIds([]);
+                    }}
+                    authType={authType}
+                />
+            </PageDrawer>
         </Box>
     )
 }
 
-const ScopeButton = (props) => {
+const PermissionMenuButton = ({ assign, assignPda, assignMatnr, setAuthType }) => {
     const record = useRecordContext();
-    const { assign, auType, setAuthType, label, ...rest } = props;
-    return (
-        <Button
-            variant="text"
-            color="primary"
-            startIcon={<AssignmentIndIcon />}
-            label={label}
-            onClick={(event) => {
-                setAuthType(auType);
-                event.stopPropagation();
-                assign(record);
-            }}
-            {...rest}
-        />
-    )
-}
+    const [anchorEl, setAnchorEl] = useState(null);
+    const open = Boolean(anchorEl);
 
-const PdaScopeButton = (props) => {
-    const record = useRecordContext();
-    const { assignPda, auType, setAuthType, label, ...rest } = props;
+    const handleClick = (event) => {
+        event.stopPropagation();
+        setAnchorEl(event.currentTarget);
+    };
+
+    const handleClose = (event) => {
+        if (event) event.stopPropagation();
+        setAnchorEl(null);
+    };
+
+    const handleWebPermission = (event) => {
+        event.stopPropagation();
+        setAuthType(0);
+        assign(record);
+        handleClose();
+    };
+
+    const handlePdaPermission = (event) => {
+        event.stopPropagation();
+        setAuthType(1);
+        assignPda(record);
+        handleClose();
+    };
+
+    const handleMatnrPermission = (event) => {
+        event.stopPropagation();
+        setAuthType(2);
+        assignMatnr(record);
+        handleClose();
+    };
+
     return (
-        <Button
-            variant="text"
-            color="primary"
-            startIcon={<AdUnitsIcon />}
-            label={label}
-            onClick={(event) => {
-                setAuthType(auType);
-                event.stopPropagation();
-                assignPda(record);
-            }}
-            {...rest}
-        />
-    )
+        <>
+            <Button
+                variant="text"
+                color="primary"
+                startIcon={<SecurityIcon />}
+                label="鏉冮檺"
+                onClick={handleClick}
+                sx={{ marginLeft: '2px', padding: '1px', fontSize: '.75rem' }}
+            />
+            <Menu
+                anchorEl={anchorEl}
+                open={open}
+                onClose={handleClose}
+                onClick={(e) => e.stopPropagation()}
+                anchorOrigin={{
+                    vertical: 'bottom',
+                    horizontal: 'left',
+                }}
+                transformOrigin={{
+                    vertical: 'top',
+                    horizontal: 'left',
+                }}
+            >
+                <MenuItem onClick={handleWebPermission}>
+                    <ListItemIcon>
+                        <AssignmentIndIcon fontSize="small" />
+                    </ListItemIcon>
+                    <ListItemText>缃戦〉鏉冮檺</ListItemText>
+                </MenuItem>
+                <MenuItem onClick={handlePdaPermission}>
+                    <ListItemIcon>
+                        <AdUnitsIcon fontSize="small" />
+                    </ListItemIcon>
+                    <ListItemText>PDA鏉冮檺</ListItemText>
+                </MenuItem>
+                <MenuItem onClick={handleMatnrPermission}>
+                    <ListItemIcon>
+                        <ArticleIcon fontSize="small" />
+                    </ListItemIcon>
+                    <ListItemText>鐗╂枡鏉冮檺</ListItemText>
+                </MenuItem>
+            </Menu>
+        </>
+    );
 }
 
 export default RoleList;
diff --git a/rsf-admin/src/page/work/stockTransfer/stockTransferList.jsx b/rsf-admin/src/page/work/stockTransfer/stockTransferList.jsx
index a036f5e..2133066 100644
--- a/rsf-admin/src/page/work/stockTransfer/stockTransferList.jsx
+++ b/rsf-admin/src/page/work/stockTransfer/stockTransferList.jsx
@@ -387,7 +387,7 @@
                         },
                     },
                 }}
-                pageSizeOptions={[10, 25, 50, 100]}
+                pageSizeOptions={[10, 20, 50, 100]}
                 editMode="row"                
                 onRowSelectionModelChange={handleSelectionChange}
                 selectionModel={selectedRows}
diff --git a/rsf-open-api/pom.xml b/rsf-open-api/pom.xml
index 7a6ca0a..f1624b7 100644
--- a/rsf-open-api/pom.xml
+++ b/rsf-open-api/pom.xml
@@ -21,6 +21,24 @@
             <artifactId>rsf-common</artifactId>
             <version>1.0.0</version>
         </dependency>
+        <!-- JWT渚濊禆 -->
+        <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>
     </dependencies>
     <build>
         <finalName>rsf-open-api</finalName>
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/annotation/OperationLog.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/annotation/OperationLog.java
new file mode 100644
index 0000000..887cbe2
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/annotation/OperationLog.java
@@ -0,0 +1,42 @@
+package com.vincent.rsf.openApi.annotation;
+
+
+import java.lang.annotation.*;
+
+/**
+ * 鎿嶄綔鏃ュ織璁板綍娉ㄨВ
+ *
+ * @author vincent
+ * @since 2020-03-21 17:03:08
+ */
+@Documented
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface OperationLog {
+
+    /**
+     * 鎿嶄綔鍔熻兘
+     */
+    String value() default "";
+
+    /**
+     * 鎿嶄綔妯″潡
+     */
+    String module() default "";
+
+    /**
+     * 澶囨敞
+     */
+    String comments() default "";
+
+    /**
+     * 鏄惁璁板綍璇锋眰鍙傛暟
+     */
+    boolean param() default true;
+
+    /**
+     * 鏄惁璁板綍杩斿洖缁撴灉
+     */
+    boolean result() default true;
+
+}
\ No newline at end of file
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/aspect/LogAspect.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/aspect/LogAspect.java
new file mode 100644
index 0000000..e840774
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/aspect/LogAspect.java
@@ -0,0 +1,264 @@
+package com.vincent.rsf.openApi.aspect;
+
+import com.alibaba.fastjson.JSON;
+import com.vincent.rsf.common.utils.Utils;
+import com.vincent.rsf.framework.common.Cools;
+import com.vincent.rsf.openApi.annotation.OperationLog;
+import com.vincent.rsf.openApi.entity.app.ApiForeignLog;
+import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.service.ApiForeignLogService;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+import java.rmi.NoSuchObjectException;
+import java.util.*;
+
+/**
+ * Created by Administrator on 2019-07-09.
+ */
+@Component
+@Aspect
+@Slf4j
+@Order(2)
+public class LogAspect {
+
+    // 鍙傛暟銆佽繑鍥炵粨鏋溿�侀敊璇俊鎭瓑鏈�澶т繚瀛橀暱搴�
+    private static final int MAX_LENGTH = 1000;
+    // 鐢ㄤ簬璁板綍璇锋眰鑰楁椂
+    private final ThreadLocal<Long> startTime = new ThreadLocal<>();
+
+    @Resource
+    private ApiForeignLogService apiForeignLogService;
+
+    public LogAspect() {
+    }
+
+    /**
+     * 鍒囧叆鐐�
+     * 鍖归厤controller鍖呭強鍏舵墍鏈夊瓙鍖呬笅鐨勬墍鏈夌被鐨勬墍鏈夋柟娉�
+     */
+    @Pointcut("execution(* com.vincent.rsf.openApi.controller..*.*(..))")
+    public void controllerPc() {
+    }
+
+    /**
+     * 鐜粫閫氱煡
+     * @param pjp ProceedingJoinPoint
+     * @return 鏂规硶缁撴灉
+     */
+    @Around("controllerPc()")
+    public Object around(ProceedingJoinPoint pjp) {
+        String methodName = pjp.getSignature().getName();
+        try {
+            ServletRequestAttributes attributes = Optional.ofNullable((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
+                    .orElseThrow(() -> new NoSuchObjectException("鍓嶇疆閫氱煡涓幏鍙栫殑 ServletRequestAttributes 瀵硅薄涓虹┖"));
+            HttpServletRequest request = attributes.getRequest();
+
+//            if("getToken".equals(methodName) || "getToken".equals(methodName))
+//                return pjp.proceed();
+
+            // 鍓嶇疆閫氱煡
+            log.info("------銆愬墠缃�氱煡銆�------");
+
+            // 璁板綍璇锋眰鍐呭
+            log.info("娴忚鍣ㄨ緭鍏ョ殑缃戝潃锛歿}", request.getRequestURL().toString());
+            log.info("HTTP_METHOD锛歿}", request.getMethod());
+            log.info("IP锛歿}", request.getRemoteAddr());
+            log.info("鎵ц鐨勪笟鍔℃柟娉曞悕锛歿}", pjp.getSignature().getDeclaringTypeName() + "." + pjp.getSignature().getName());
+            log.info("涓氬姟鏂规硶鑾峰緱鐨勫弬鏁帮細{}", Arrays.toString(pjp.getArgs()));
+
+            Object result = pjp.proceed();
+
+            // 鍚庣疆閫氱煡
+            log.info("------銆愬悗缃�氱煡銆�------");
+            log.info("{}鏂规硶鐨勮繑鍥炲�硷細{}", pjp.getSignature().getName(), result);
+
+            saveLog1(pjp, result, null);
+            return result;
+        } catch (Throwable e) {
+            // 寮傚父閫氱煡
+            log.error("------銆愬紓甯搁�氱煡銆�------");
+            log.error("{}鏂规硶寮傚父锛屽弬鏁帮細{}锛屽紓甯革細", pjp.getSignature().getName(), Arrays.toString(pjp.getArgs()), e);
+
+            saveLog1(pjp, null, (Exception) e);
+            return CommonResponse.error("鏈嶅姟鍣ㄥ鐞嗘暟鎹紓甯�");
+        } finally {
+            // 鏈�缁堥�氱煡
+            if(!methodName.isEmpty() && !"getRegistered".equals(methodName)){
+                log.info("------銆愭渶缁堥�氱煡銆�------");
+                log.info("{}鏂规硶鎵ц缁撴潫", pjp.getSignature().getName());
+            }
+        }
+    }
+
+    private void saveLog1(JoinPoint joinPoint, Object result, Exception e) {
+
+        ApiForeignLog record = new ApiForeignLog();
+        Long endTime = startTime.get();
+        record.setCreateTime(new Date());
+        // 璁板綍鎿嶄綔鑰楁椂
+        if (endTime != null) {
+            record.setSpendTime((int) (System.currentTimeMillis() - endTime));
+        }
+        record.setTimestamp(String.valueOf(endTime));
+//        // 璁板綍褰撳墠鐧诲綍鐢ㄦ埛id銆佺鎴穒d
+//        User user = getLoginUser();
+//        if (user != null) {
+//            record.setUserId(user.getId());
+//            record.setTenantId(user.getTenantId());
+//        }
+        // 璁板綍璇锋眰鍦板潃銆佽姹傛柟寮忋�乮p
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        HttpServletRequest request = (attributes == null ? null : attributes.getRequest());
+        if (request != null) {
+            record.setUrl(request.getRequestURI());
+//            record.setClientIp(IpTools.gainRealIp(request));
+        }
+        // 璁板綍寮傚父淇℃伅
+        if (e != null) {
+            record.setResult(0);
+            record.setErr(Utils.sub(e.toString(), MAX_LENGTH));
+        } else {
+            record.setResult(1);
+        }
+//        // 璁板綍鎿嶄綔鍔熻兘
+//        record.setNamespace(desc);
+//        // 璁板綍澶囨敞
+//        if (!Cools.isEmpty(ol.comments())) {
+//            record.setMemo(ol.comments());
+//        }
+        // 璁板綍璇锋眰鍙傛暟
+        record.setRequest(Utils.sub(Arrays.toString(joinPoint.getArgs()), MAX_LENGTH));
+        record.setResponse(Utils.sub(JSON.toJSONString(result), MAX_LENGTH));
+
+        apiForeignLogService.saveAsync(record);
+    }
+
+    /**
+     * 淇濆瓨鎿嶄綔璁板綍
+     */
+    private void saveLog(JoinPoint joinPoint, Object result, Exception e) {
+        // 璁板綍妯″潡鍚嶃�佹搷浣滃姛鑳姐�佽姹傛柟娉曘�佽姹傚弬鏁般�佽繑鍥炵粨鏋�
+        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+        Method method = signature.getMethod();
+        if (null == method) {
+            return;
+        }
+        OperationLog ol = method.getAnnotation(OperationLog.class);
+        if (null == ol) {
+            return;
+        }
+        String desc = getDescription(method, ol);
+        if (Cools.isEmpty(desc)) {
+            return;
+        }
+
+        ApiForeignLog record = new ApiForeignLog();
+        Long endTime = startTime.get();
+        record.setCreateTime(new Date());
+        // 璁板綍鎿嶄綔鑰楁椂
+        if (endTime != null) {
+            record.setSpendTime((int) (System.currentTimeMillis() - endTime));
+        }
+        record.setTimestamp(String.valueOf(endTime));
+//        // 璁板綍褰撳墠鐧诲綍鐢ㄦ埛id銆佺鎴穒d
+//        User user = getLoginUser();
+//        if (user != null) {
+//            record.setUserId(user.getId());
+//            record.setTenantId(user.getTenantId());
+//        }
+        // 璁板綍璇锋眰鍦板潃銆佽姹傛柟寮忋�乮p
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        HttpServletRequest request = (attributes == null ? null : attributes.getRequest());
+        if (request != null) {
+            record.setUrl(request.getRequestURI());
+//            record.setClientIp(IpTools.gainRealIp(request));
+        }
+        // 璁板綍寮傚父淇℃伅
+        if (e != null) {
+            record.setResult(0);
+            record.setErr(Utils.sub(e.toString(), MAX_LENGTH));
+        } else {
+            record.setResult(1);
+        }
+        // 璁板綍鎿嶄綔鍔熻兘
+        record.setNamespace(desc);
+        // 璁板綍澶囨敞
+        if (!Cools.isEmpty(ol.comments())) {
+            record.setMemo(ol.comments());
+        }
+        // 璁板綍璇锋眰鍙傛暟
+        if (ol.param() && request != null) {
+            record.setRequest(Utils.sub(getParams(joinPoint, request), MAX_LENGTH));
+        }
+        // 璁板綍璇锋眰缁撴灉
+        if (ol.result() && result != null) {
+            record.setResponse(Utils.sub(JSON.toJSONString(result), MAX_LENGTH));
+        }
+        apiForeignLogService.saveAsync(record);
+    }
+
+    /**
+     * 鑾峰彇鎿嶄綔鍔熻兘
+     *
+     * @param method Method
+     * @param ol     OperationLog
+     * @return String
+     */
+    private String getDescription(Method method, OperationLog ol) {
+        if (!Cools.isEmpty(ol.value())) {
+            return ol.value();
+        }
+        return null;
+    }
+
+    /**
+     * 鑾峰彇璇锋眰鍙傛暟
+     *
+     * @param joinPoint JoinPoint
+     * @param request   HttpServletRequest
+     * @return String
+     */
+    private String getParams(JoinPoint joinPoint, HttpServletRequest request) {
+        String params;
+
+        Map<String, String> paramsMap = new HashMap<>();
+
+        Map<String, String[]> map = Collections.unmodifiableMap(request.getParameterMap());
+        for (Map.Entry<String, String[]> entry : map.entrySet()) {
+            paramsMap.put(entry.getKey(), Utils.join(entry.getValue(), ","));
+        }
+
+        if (paramsMap.keySet().size() > 0) {
+            params = JSON.toJSONString(paramsMap);
+        } else {
+            StringBuilder sb = new StringBuilder();
+            for (Object arg : joinPoint.getArgs()) {
+                if (null == arg
+                        || arg instanceof MultipartFile
+                        || arg instanceof HttpServletRequest
+                        || arg instanceof HttpServletResponse) {
+                    continue;
+                }
+                sb.append(JSON.toJSONString(arg)).append(" ");
+            }
+            params = sb.toString();
+        }
+        return params;
+    }
+
+}
\ No newline at end of file
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..54491fd
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/config/ApiSecurityConfig.java
@@ -0,0 +1,36 @@
+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瀹夊叏閰嶇疆绫�
+ * 鐢ㄤ簬娉ㄥ唽API璁よ瘉杩囨护鍣�
+ */
+@Configuration
+public class ApiSecurityConfig {
+
+    @Resource
+    private AppIdAuthenticationFilter appIdAuthenticationFilter;
+
+    /**
+     * 娉ㄥ唽API璁よ瘉杩囨护鍣紙鏀寔AppId/AppSecret鍜孴oken璁よ瘉锛�
+     * 
+     * @return 杩囨护鍣ㄦ敞鍐孊ean
+     */
+    @Bean
+    public FilterRegistrationBean<AppIdAuthenticationFilter> apiAuthenticationFilter() {
+        FilterRegistrationBean<AppIdAuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
+        
+        registrationBean.setFilter(appIdAuthenticationFilter);
+        registrationBean.addUrlPatterns("/api/*", "/erp/*", "/mes/*", "/agv/*"); // 鎷︽埅API璇锋眰銆丒RP璇锋眰銆丮ES璇锋眰銆佺鐞咥GV浠诲姟璇锋眰
+        registrationBean.setName("apiAuthenticationFilter");
+        registrationBean.setOrder(1); // 璁剧疆杩囨护鍣ㄤ紭鍏堢骇
+        
+        return registrationBean;
+    }
+}
\ No newline at end of file
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..3597e5f 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
@@ -2,7 +2,9 @@
 
 
 import com.vincent.rsf.openApi.entity.constant.Constants;
+import com.vincent.rsf.openApi.security.filter.AppIdAuthenticationFilter;
 import com.vincent.rsf.openApi.utils.Http;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.AsyncHandlerInterceptor;
@@ -23,36 +25,15 @@
 @Configuration
 public class WebMvcConfig implements WebMvcConfigurer {
 
+    @Autowired
+    private AppIdAuthenticationFilter appIdAuthenticationFilter;
+
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
         registry.addInterceptor(getAsyncHandlerInterceptor())
                 .addPathPatterns("/**")
                 .excludePathPatterns("/swagger-resources/**", "/webjars/**","/erp/**", "/v2/**","/v3/**","/doc.html/**", "/swagger-ui.html/**");
     }
-
-
-    @Bean
-    public AsyncHandlerInterceptor getAsyncHandlerInterceptor() {
-        return new AsyncHandlerInterceptor(){
-            @Override
-            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-                Http.cors(response);
-                return true;
-            }
-        };
-    }
-
-    @Override
-    public void addResourceHandlers(ResourceHandlerRegistry registry) {
-        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
-        registry.addResourceHandler("swagger-ui.html")
-                .addResourceLocations("classpath:/META-INF/resources/");
-        registry.addResourceHandler("doc.html")
-                .addResourceLocations("classpath:/META-INF/resources/");
-        registry.addResourceHandler("/webjars/**")
-                .addResourceLocations("classpath:/META-INF/resources/webjars/");
-    }
-
 
     @Override
     public void addCorsMappings(CorsRegistry registry) {
@@ -66,13 +47,14 @@
     }
 
 
-    public static void cors(HttpServletResponse response){
-        // 璺ㄥ煙璁剧疆
-        response.setHeader("Access-Control-Max-Age", "3600");
-        response.setHeader("Access-Control-Allow-Origin", "*");
-        response.setHeader("Access-Control-Allow-Methods", "*");
-        response.setHeader("Access-Control-Allow-Headers", "*");
-        response.setHeader("Access-Control-Expose-Headers", Constants.TOKEN_HEADER_NAME);
+    @Bean
+    public AsyncHandlerInterceptor getAsyncHandlerInterceptor() {
+        return new AsyncHandlerInterceptor(){
+            @Override
+            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+                Http.cors(response);
+                return true;
+            }
+        };
     }
-
-}
+}
\ No newline at end of file
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..ca9040c
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/AuthController.java
@@ -0,0 +1,174 @@
+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.AppAuthParam;
+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 lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+
+/**
+ * App璁よ瘉鎺у埗鍣�
+ * 
+ * 鎻愪緵AppId鍜孉ppSecret鐨勭櫥褰曡璇佸姛鑳�
+ * 
+ * @author vincent
+ * @since 2026-01-05
+ */
+@RestController
+//@RequestMapping("/auth")
+@Api(tags = "搴旂敤璁よ瘉绠$悊")
+@Slf4j
+public class AuthController {
+
+    // 寮�鍚ā鎷熸暟鎹�
+    @Value("${foreign.api.data.simulated}")
+    public static String SIMULATED_DATA_ENABLE = "1";
+
+    @Resource
+    private AppAuthService appAuthService;
+
+
+    /**
+     * 鑾峰彇App璁よ瘉Token
+     *
+     * @param param 搴旂敤ID鍜屽簲鐢ㄥ瘑閽�
+     * @return 璁よ瘉Token
+     */
+    @ApiOperation("鑾峰彇App璁よ瘉Token")
+    @PostMapping("/getToken")
+    public CommonResponse getToken(@RequestBody AppAuthParam param) {
+        String appId = param.getAppId();
+        String appSecret = param.getAppSecret();
+
+        if (Cools.isEmpty(appId, appSecret)) {
+            return CommonResponse.error("AppId鍜孉ppSecret涓嶈兘涓虹┖");
+        }
+
+        boolean isValid = appAuthService.validateApp(appId, appSecret);
+        if (isValid) {
+            String token = Constants.TOKEN_PREFIX + TokenUtils.generateToken(appId, appSecret);  //appAuthService.generateAppToken(appId, appSecret);
+            return CommonResponse.ok()
+                    .setMsg("鑾峰彇Token鎴愬姛")
+                    .setData(token);
+        } else {
+            return CommonResponse.error("AppId鎴朅ppSecret鏃犳晥");
+        }
+    }
+
+//    /**
+//     * 楠岃瘉Token鐨勬帴鍙�
+//     *
+//     * @param token 瑕侀獙璇佺殑Token
+//     * @return Token楠岃瘉缁撴灉
+//     */
+//    @PostMapping("/validateToken")
+//    public Map<String, Object> validateToken(@RequestHeader(name = "authorization") String token) {
+//        log.info("楠岃瘉Token: {}", token.substring(0, Math.min(10, token.length())) + "...");
+//
+//        boolean isValid = TokenUtils.validateToken(token);
+//
+//        Map<String, Object> response = new HashMap<>();
+//        response.put("code", "200");
+//        response.put("message", isValid ? "Token鏈夋晥" : "Token鏃犳晥");
+//        response.put("data", Map.of(
+//                "valid", isValid,
+//                "appId", isValid ? TokenUtils.getAppIdFromToken(token) : null,
+//                "userId", isValid ? TokenUtils.getUserIdFromToken(token) : null
+//        ));
+//        response.put("success", isValid);
+//
+//        return response;
+//    }
+
+//    /**
+//     * AppId鍜孉ppSecret鐧诲綍璁よ瘉
+//     *
+//     * @param param 璁よ瘉鍙傛暟
+//     * @return 璁よ瘉缁撴灉
+//     */
+//    @ApiOperation("AppId鍜孉ppSecret鐧诲綍璁よ瘉")
+//    @PostMapping("/login")
+//    public CommonResponse login(@RequestBody AppAuthParam param) {
+//        String appId = param.getAppId();
+//        String appSecret = param.getAppSecret();
+//
+//        if (Cools.isEmpty(appId, appSecret)) {
+//            return CommonResponse.error("AppId鍜孉ppSecret涓嶈兘涓虹┖");
+//        }
+//
+//        boolean isValid = appAuthService.validateApp(appId, appSecret);
+//        if (isValid) {
+//            // 鐢熸垚Token
+//            String token = appAuthService.generateAppToken(appId, appSecret);
+//            return CommonResponse.ok()
+//                    .setMsg("鐧诲綍鎴愬姛")
+//                    .setData(token);
+//        } else {
+//            return CommonResponse.error("AppId鎴朅ppSecret鏃犳晥");
+//        }
+//    }
+//
+//
+//
+//    /**
+//     * 楠岃瘉App璁よ瘉
+//     *
+//     * @param request HTTP璇锋眰
+//     * @return 楠岃瘉缁撴灉
+//     */
+//    @ApiOperation("楠岃瘉App璁よ瘉")
+//    @PostMapping("/validate")
+//    public CommonResponse validate(HttpServletRequest request) {
+//        String appId = request.getHeader(Constants.HEADER_APP_ID);
+//        String appSecret = request.getHeader(Constants.HEADER_APP_SECRET);
+//
+//        if (Cools.isEmpty(appId, appSecret)) {
+//            return CommonResponse.error("缂哄皯AppId鎴朅ppSecret");
+//        }
+//
+//        boolean isValid = appAuthService.validateApp(appId, appSecret);
+//        if (isValid) {
+//            return CommonResponse.ok()
+//                    .setMsg("楠岃瘉鎴愬姛")
+//                    .setData(appAuthService.getAppInfo(appId));
+//        } else {
+//            return CommonResponse.error("楠岃瘉澶辫触");
+//        }
+//    }
+//
+//    /**
+//     * 鑾峰彇褰撳墠璁よ瘉鐨凙pp淇℃伅
+//     *
+//     * @param request HTTP璇锋眰
+//     * @return App淇℃伅
+//     */
+//    @ApiOperation("鑾峰彇褰撳墠璁よ瘉鐨凙pp淇℃伅")
+//    @GetMapping("/info")
+//    public CommonResponse getAppInfo(HttpServletRequest request) {
+//        String appId = (String) request.getAttribute("APP_ID");
+//        if (appId == null) {
+//            appId = request.getHeader(Constants.HEADER_APP_ID);
+//        }
+//
+//        if (appId == null) {
+//            return CommonResponse.error("鏈壘鍒癆ppId");
+//        }
+//
+//        var appInfo = appAuthService.getAppInfo(appId);
+//        if (appInfo != null) {
+//            return CommonResponse.ok()
+//                    .setMsg("鑾峰彇App淇℃伅鎴愬姛")
+//                    .setData(appInfo);
+//        } else {
+//            return CommonResponse.error("鏈壘鍒癆pp淇℃伅");
+//        }
+//    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/TaskController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/TaskController.java
new file mode 100644
index 0000000..eed04a3
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/TaskController.java
@@ -0,0 +1,59 @@
+package com.vincent.rsf.openApi.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.vincent.rsf.framework.exception.CoolException;
+import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.entity.phyz.Task;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Objects;
+
+import static com.vincent.rsf.openApi.controller.AuthController.SIMULATED_DATA_ENABLE;
+import static com.vincent.rsf.openApi.controller.phyz.ERPController.paramsFormat;
+
+@RestController
+@Api("浠诲姟绠$悊鎺ュ彛")
+@Slf4j
+public class TaskController {
+
+    @ApiOperation("鐐瑰鐐瑰垱寤篈GV鎼繍浠诲姟")
+    @PostMapping("/agv/transTask/add")
+    public CommonResponse addAgvTask(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        List<Task> tasks = JSON.parseArray(params.toJSONString(), Task.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+
+    @ApiOperation("鐐瑰鐐瑰彇娑圓GV鎼繍浠诲姟")
+    @PostMapping("/agv/transTask/cancel")
+    public CommonResponse cancelAgvTask(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        List<Task> tasks = JSON.parseArray(params.toJSONString(), Task.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+}
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 d5e4230..247afe5 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("/erp1")
 @Api("ERP鎺ュ彛瀵规帴")
 public class WmsErpController {
 
@@ -46,7 +46,7 @@
      * @return
      */
     @ApiOperation("鍗曟嵁淇敼")
-    @PostMapping("/order/upadte")
+    @PostMapping("/order/update")
     public CommonResponse modifyOrderDtel(@RequestBody ErpOpParams params) {
         if (Objects.isNull(params)) {
             throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsRcsController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsRcsController.java
index afa3d5b..0a7d0b8 100644
--- a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsRcsController.java
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/WmsRcsController.java
@@ -3,6 +3,7 @@
 import com.vincent.rsf.framework.common.R;
 import com.vincent.rsf.framework.exception.CoolException;
 import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.entity.dto.SyncLocsDto;
 import com.vincent.rsf.openApi.entity.params.ExMsgCallbackParams;
 import com.vincent.rsf.openApi.entity.params.RcsPubTaskParams;
 import com.vincent.rsf.openApi.entity.params.SyncRcsLocsParam;
@@ -15,6 +16,7 @@
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
@@ -74,11 +76,12 @@
      */
     @ApiOperation("RCS搴撲綅淇℃伅鍚屾")
     @PostMapping("/sync/locs")
-    public R syncLocsToWms(@RequestBody SyncRcsLocsParam params) {
+    public CommonResponse syncLocsToWms(@RequestBody SyncRcsLocsParam params) {
          if (Objects.isNull(params)) {
-             return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+             throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
          }
-         return R.ok().add(wmsRcsService.syncLocs(params));
+         List<SyncLocsDto> result = wmsRcsService.syncLocs(params);
+         return CommonResponse.ok(result);
     }
 
 
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/ApiAuthExampleController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/ApiAuthExampleController.java
new file mode 100644
index 0000000..15cb949
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/ApiAuthExampleController.java
@@ -0,0 +1,96 @@
+package com.vincent.rsf.openApi.controller.example;
+
+import com.vincent.rsf.openApi.entity.constant.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * API璁よ瘉绀轰緥鎺у埗鍣�
+ * 婕旂ず濡備綍浣跨敤缁熶竴鐨勮璇佹満鍒讹紙鏀寔AppId/AppSecret鍜孴oken锛�
+ */
+@RestController
+@RequestMapping("/api/example/auth")
+public class ApiAuthExampleController {
+    private static final Logger log = LoggerFactory.getLogger(ApiAuthExampleController.class);
+
+    /**
+     * 鑾峰彇鍙椾繚鎶ょ殑鏁版嵁 - 鏀寔AppId/AppSecret鎴朤oken璁よ瘉
+     * 
+     * @param request HTTP璇锋眰
+     * @return 鍙椾繚鎶ょ殑鏁版嵁
+     */
+    @GetMapping("/protected-data")
+    public Map<String, Object> getProtectedData(HttpServletRequest request) {
+        // 浠庤姹傚睘鎬т腑鑾峰彇璁よ瘉淇℃伅锛堢敱AppIdAuthenticationFilter璁剧疆锛�
+        String appId = (String) request.getAttribute(Constants.REQUEST_ATTR_APP_ID);
+        String userId = (String) request.getAttribute(Constants.REQUEST_ATTR_USER_ID);
+        
+        log.info("璁块棶鍙椾繚鎶ゆ帴鍙o紝AppId: {}, UserId: {}", appId, userId);
+        
+        Map<String, Object> response = new HashMap<>();
+        response.put("code", "200");
+        response.put("message", "璁块棶鎴愬姛");
+        response.put("data", Map.of(
+            "appId", appId,
+            "userId", userId,
+            "protectedInfo", "杩欐槸鍙椾繚鎶ょ殑鏁版嵁",
+            "authType", userId != null ? "Token" : "AppId/AppSecret",
+            "timestamp", System.currentTimeMillis()
+        ));
+        response.put("success", true);
+        
+        return response;
+    }
+
+    /**
+     * 鑾峰彇璁よ瘉淇℃伅 - 鏀寔AppId/AppSecret鎴朤oken璁よ瘉
+     * 
+     * @param request HTTP璇锋眰
+     * @return 璁よ瘉淇℃伅
+     */
+    @GetMapping("/auth-info")
+    public Map<String, Object> getAuthInfo(HttpServletRequest request) {
+        // 浠庤姹傚睘鎬т腑鑾峰彇璁よ瘉淇℃伅
+        String appId = (String) request.getAttribute(Constants.REQUEST_ATTR_APP_ID);
+        String userId = (String) request.getAttribute(Constants.REQUEST_ATTR_USER_ID);
+        
+        log.info("鑾峰彇璁よ瘉淇℃伅锛孉ppId: {}, UserId: {}", appId, userId);
+        
+        Map<String, Object> response = new HashMap<>();
+        response.put("code", "200");
+        response.put("message", "鑾峰彇璁よ瘉淇℃伅鎴愬姛");
+        response.put("data", Map.of(
+            "appId", appId,
+            "userId", userId,
+            "authType", userId != null ? "Token" : "AppId/AppSecret",
+            "authenticated", appId != null
+        ));
+        response.put("success", true);
+        
+        return response;
+    }
+
+    /**
+     * 娴嬭瘯鎺ュ彛 - 涓嶉渶瑕佽璇�
+     * 
+     * @return 娴嬭瘯鏁版嵁
+     */
+    @GetMapping("/public-test")
+    public Map<String, Object> getPublicTest() {
+        Map<String, Object> response = new HashMap<>();
+        response.put("code", "200");
+        response.put("message", "鍏紑鎺ュ彛璁块棶鎴愬姛");
+        response.put("data", Map.of(
+            "info", "杩欐槸涓�涓笉闇�瑕佽璇佺殑鍏紑鎺ュ彛",
+            "timestamp", System.currentTimeMillis()
+        ));
+        response.put("success", true);
+        
+        return response;
+    }
+}
\ No newline at end of file
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/AppAuthExampleController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/AppAuthExampleController.java
new file mode 100644
index 0000000..37d71ef
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/AppAuthExampleController.java
@@ -0,0 +1,98 @@
+package com.vincent.rsf.openApi.controller.example;
+
+import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.security.utils.AuthUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.GetMapping;
+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;
+
+/**
+ * App璁よ瘉浣跨敤绀轰緥鎺у埗鍣�
+ * 
+ * 婕旂ず濡備綍鍦ㄦ帶鍒跺櫒涓娇鐢ˋppId璁よ瘉
+ * 
+ * @author vincent
+ * @since 2026-01-05
+ */
+@RestController
+@RequestMapping("/example/auth")
+@Api(tags = "App璁よ瘉浣跨敤绀轰緥")
+public class AppAuthExampleController {
+
+    /**
+     * 闇�瑕丄pp璁よ瘉鐨勬帴鍙gず渚�
+     * 
+     * @param request HTTP璇锋眰
+     * @return 鍝嶅簲缁撴灉
+     */
+    @ApiOperation("闇�瑕丄pp璁よ瘉鐨勬帴鍙gず渚�")
+    @GetMapping("/protected")
+    public CommonResponse protectedEndpoint(HttpServletRequest request) {
+        // 鑾峰彇璁よ瘉鐨凙ppId
+        String appId = AuthUtils.getAppId(request);
+        
+        // 妫�鏌ユ槸鍚﹀凡璁よ瘉
+        if (appId == null) {
+            return CommonResponse.error("鏈�氳繃App璁よ瘉");
+        }
+        
+        return CommonResponse.ok()
+                .setMsg("璁块棶鎴愬姛")
+                .setData("璁よ瘉鐨凙ppId: " + appId);
+    }
+
+    /**
+     * 鑾峰彇褰撳墠璁よ瘉鐨凙pp淇℃伅
+     * 
+     * @param request HTTP璇锋眰
+     * @return App淇℃伅
+     */
+    @ApiOperation("鑾峰彇褰撳墠璁よ瘉鐨凙pp淇℃伅")
+    @GetMapping("/app-info")
+    public CommonResponse getAppInfo(HttpServletRequest request) {
+        String appId = AuthUtils.getAppId(request);
+        
+        if (appId == null) {
+            return CommonResponse.error("鏈�氳繃App璁よ瘉");
+        }
+        
+        return CommonResponse.ok()
+                .setMsg("鑾峰彇App淇℃伅鎴愬姛")
+                .setData("褰撳墠AppId: " + appId);
+    }
+
+    /**
+     * 鏃犻渶璁よ瘉鐨勫叕寮�鎺ュ彛
+     * 
+     * @return 鍝嶅簲缁撴灉
+     */
+    @ApiOperation("鏃犻渶璁よ瘉鐨勫叕寮�鎺ュ彛")
+    @GetMapping("/public")
+    public CommonResponse publicEndpoint() {
+        return CommonResponse.ok()
+                .setMsg("鍏紑鎺ュ彛璁块棶鎴愬姛")
+                .setData("浠讳綍浜洪兘鍙互璁块棶姝ゆ帴鍙�");
+    }
+
+    /**
+     * 妫�鏌ヨ璇佺姸鎬�
+     * 
+     * @param request HTTP璇锋眰
+     * @return 璁よ瘉鐘舵��
+     */
+    @ApiOperation("妫�鏌ヨ璇佺姸鎬�")
+    @PostMapping("/check-auth")
+    public CommonResponse checkAuth(HttpServletRequest request) {
+        boolean isAuthenticated = AuthUtils.isAuthenticated(request);
+        String appId = AuthUtils.getAppId(request);
+        
+        return CommonResponse.ok()
+                .setMsg("璁よ瘉妫�鏌ュ畬鎴�")
+                .setData("isAuthenticated: " + isAuthenticated + ", appId: " + appId);
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/FieldMappingExampleController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/FieldMappingExampleController.java
new file mode 100644
index 0000000..60fb96a
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/FieldMappingExampleController.java
@@ -0,0 +1,195 @@
+package com.vincent.rsf.openApi.controller.example;
+
+import com.alibaba.fastjson.JSONObject;
+import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.utils.ParamsMapUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+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;
+
+/**
+ * 鎺ュ彛瀛楁鏄犲皠浣跨敤绀轰緥
+ * 
+ * 灞曠ず濡備綍鍦ㄥ疄闄呬笟鍔′腑浣跨敤 FuncMap 杩涜瀛楁鏄犲皠
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+@Slf4j
+@RestController
+@RequestMapping("/example/mapping")
+@Api(tags = "瀛楁鏄犲皠浣跨敤绀轰緥")
+public class FieldMappingExampleController {
+
+    /**
+     * 绀轰緥1锛欵RP璁㈠崟鎺ユ敹 - 瀛楁鏄犲皠
+     * 
+     * 鍦烘櫙锛氭帴鏀禘RP绯荤粺鐨勮鍗曟暟鎹紝瀛楁鍚嶄笌WMS涓嶄竴鑷达紝闇�瑕佽繘琛屾槧灏�
+     * 
+     * ERP瀛楁 -> WMS瀛楁锛�
+     * orderNumber -> code
+     * orderQty -> qty
+     * orderAmount -> anfme
+     */
+    @ApiOperation("绀轰緥锛欵RP璁㈠崟鎺ユ敹")
+    @PostMapping("/erp/order/receive")
+    public CommonResponse erpOrderReceive(@RequestBody JSONObject requestData) {
+        log.info("鎺ユ敹鍒癊RP璁㈠崟鏁版嵁锛堟槧灏勫墠锛夛細{}", requestData);
+        
+        // 鎵ц瀛楁鏄犲皠
+        String appId = "ERP_SYSTEM";
+        String funcId = "ORDER_SYNC";
+        JSONObject mappedData = ParamsMapUtils.apiMaps(appId, funcId, requestData);
+        
+        log.info("瀛楁鏄犲皠鍚庣殑鏁版嵁锛歿}", mappedData);
+        
+        // 杩欓噷鍙互缁х画璋冪敤WMS鍐呴儴鐨勪笟鍔¢�昏緫澶勭悊鏄犲皠鍚庣殑鏁版嵁
+        // orderService.createOrder(mappedData);
+        
+        return CommonResponse.ok()
+                .setMsg("璁㈠崟鎺ユ敹鎴愬姛")
+                .setData(mappedData);
+    }
+
+    /**
+     * 绀轰緥2锛氱墿鏂欎俊鎭悓姝� - 瀛楁鏄犲皠
+     * 
+     * 鍦烘櫙锛氭帴鏀禘RP绯荤粺鐨勭墿鏂欐暟鎹�
+     * 
+     * ERP瀛楁 -> WMS瀛楁锛�
+     * materialCode -> matnr
+     * materialName -> maktx
+     * materialSpec -> spec
+     */
+    @ApiOperation("绀轰緥锛氱墿鏂欎俊鎭悓姝�")
+    @PostMapping("/erp/material/sync")
+    public CommonResponse materialSync(@RequestBody JSONObject requestData) {
+        log.info("鎺ユ敹鍒扮墿鏂欐暟鎹紙鏄犲皠鍓嶏級锛歿}", requestData);
+        
+        // 鎵ц瀛楁鏄犲皠
+        String appId = "ERP_SYSTEM";
+        String funcId = "MATNR_SYNC";
+        JSONObject mappedData = ParamsMapUtils.apiMaps(appId, funcId, requestData);
+        
+        log.info("瀛楁鏄犲皠鍚庣殑鏁版嵁锛歿}", mappedData);
+        
+        return CommonResponse.ok()
+                .setMsg("鐗╂枡鍚屾鎴愬姛")
+                .setData(mappedData);
+    }
+
+    /**
+     * 绀轰緥3锛歐CS浠诲姟鍒涘缓 - 瀛楁鏄犲皠
+     * 
+     * 鍦烘櫙锛氭帴鏀禬CS绯荤粺鐨勪换鍔″垱寤鸿姹�
+     * 
+     * WCS瀛楁 -> WMS瀛楁锛�
+     * containerCode -> barcode
+     * stationCode -> sourceStaNo
+     * taskType -> ioType
+     */
+    @ApiOperation("绀轰緥锛歐CS浠诲姟鍒涘缓")
+    @PostMapping("/wcs/task/create")
+    public CommonResponse wcsTaskCreate(@RequestBody JSONObject requestData) {
+        log.info("鎺ユ敹鍒癢CS浠诲姟鏁版嵁锛堟槧灏勫墠锛夛細{}", requestData);
+        
+        // 鎵ц瀛楁鏄犲皠
+        String appId = "WCS_SYSTEM";
+        String funcId = "TASK_CREATE";
+        JSONObject mappedData = ParamsMapUtils.apiMaps(appId, funcId, requestData);
+        
+        log.info("瀛楁鏄犲皠鍚庣殑鏁版嵁锛歿}", mappedData);
+        
+        return CommonResponse.ok()
+                .setMsg("浠诲姟鍒涘缓鎴愬姛")
+                .setData(mappedData);
+    }
+
+    /**
+     * 绀轰緥4锛氭壒閲忔暟鎹槧灏�
+     * 
+     * 鍦烘櫙锛氭壒閲忔帴鏀惰鍗曟槑缁嗭紝闇�瑕佸姣忔潯鏄庣粏杩涜瀛楁鏄犲皠
+     */
+    @ApiOperation("绀轰緥锛氭壒閲忚鍗曟槑缁嗘槧灏�")
+    @PostMapping("/erp/order/batch")
+    public CommonResponse batchOrderMapping(@RequestBody JSONObject requestData) {
+        log.info("鎺ユ敹鍒版壒閲忚鍗曟暟鎹�");
+        
+        String appId = "ERP_SYSTEM";
+        String funcId = "ORDER_SYNC";
+        
+        // 鏄犲皠璁㈠崟澶�
+        JSONObject orderHeader = requestData.getJSONObject("header");
+        JSONObject mappedHeader = ParamsMapUtils.apiMaps(appId, funcId, orderHeader);
+        
+        // 鏄犲皠璁㈠崟鏄庣粏鍒楄〃
+        com.alibaba.fastjson.JSONArray items = requestData.getJSONArray("items");
+        com.alibaba.fastjson.JSONArray mappedItems = new com.alibaba.fastjson.JSONArray();
+        
+        if (items != null) {
+            for (int i = 0; i < items.size(); i++) {
+                JSONObject item = items.getJSONObject(i);
+                JSONObject mappedItem = ParamsMapUtils.apiMaps(appId, funcId, item);
+                mappedItems.add(mappedItem);
+            }
+        }
+        
+        // 缁勮缁撴灉
+        JSONObject result = new JSONObject();
+        result.put("header", mappedHeader);
+        result.put("items", mappedItems);
+        
+        log.info("鎵归噺鏄犲皠瀹屾垚锛屽叡澶勭悊 {} 鏉℃槑缁�", mappedItems.size());
+        
+        return CommonResponse.ok()
+                .setMsg("鎵归噺澶勭悊鎴愬姛")
+                .setData(result);
+    }
+
+    /**
+     * 绀轰緥5锛氭潯浠舵槧灏�
+     * 
+     * 鍦烘櫙锛氭牴鎹笉鍚岀殑搴旂敤鏉ユ簮锛屼娇鐢ㄤ笉鍚岀殑鏄犲皠瑙勫垯
+     */
+    @ApiOperation("绀轰緥锛氭潯浠舵槧灏�")
+    @PostMapping("/dynamic/mapping")
+    public CommonResponse dynamicMapping(@RequestBody JSONObject requestData) {
+        
+        // 浠庤姹備腑鑾峰彇搴旂敤鏍囪瘑
+        String source = requestData.getString("source");
+        String funcId = requestData.getString("function");
+        
+        // 绉婚櫎鍏冩暟鎹瓧娈�
+        requestData.remove("source");
+        requestData.remove("function");
+        
+        log.info("鍔ㄦ�佹槧灏� - 鏉ユ簮锛歿}锛屽姛鑳斤細{}", source, funcId);
+        
+        // 鏍规嵁鏉ユ簮閫夋嫨涓嶅悓鐨勬槧灏勮鍒�
+        String appId;
+        switch (source) {
+            case "ERP":
+                appId = "ERP_SYSTEM";
+                break;
+            case "WCS":
+                appId = "WCS_SYSTEM";
+                break;
+            case "MES":
+                appId = "MES_SYSTEM";
+                break;
+            default:
+                return CommonResponse.error("鏈煡鐨勬暟鎹簮锛�" + source);
+        }
+        
+        // 鎵ц鏄犲皠
+        JSONObject mappedData = ParamsMapUtils.apiMaps(appId, funcId, requestData);
+        
+        return CommonResponse.ok()
+                .setMsg("鍔ㄦ�佹槧灏勬垚鍔�")
+                .setData(mappedData);
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/JsonReplaceExampleController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/JsonReplaceExampleController.java
new file mode 100644
index 0000000..e1e8fbf
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/JsonReplaceExampleController.java
@@ -0,0 +1,326 @@
+package com.vincent.rsf.openApi.controller.example;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.utils.ParamsMapUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+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;
+
+/**
+ * JSON灞炴�у悕閫掑綊鏇挎崲浣跨敤绀轰緥
+ * 
+ * 婕旂ず濡備綍浣跨敤 FuncMap 閫掑綊閬嶅巻骞舵浛鎹SON鎵�鏈夊眰绾х殑灞炴�у悕绉�
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+@Slf4j
+@RestController
+@RequestMapping("/example/json-replace")
+@Api(tags = "JSON灞炴�ч�掑綊鏇挎崲绀轰緥")
+public class JsonReplaceExampleController {
+
+    /**
+     * 绀轰緥1锛氱畝鍗曞璞$殑灞炴�ф浛鎹�
+     * 
+     * 杈撳叆锛�
+     * {
+     *   "orderNumber": "PO001",
+     *   "orderQty": 100,
+     *   "orderAmount": 5000.00
+     * }
+     * 
+     * 杈撳嚭锛�
+     * {
+     *   "code": "PO001",
+     *   "qty": 100,
+     *   "anfme": 5000.00
+     * }
+     */
+    @ApiOperation("绀轰緥1锛氱畝鍗曞璞″睘鎬ф浛鎹�")
+    @PostMapping("/simple-replace")
+    public CommonResponse simpleReplace(@RequestBody JSONObject data) {
+        log.info("鍘熷鏁版嵁锛歿}", data);
+        
+        // 瀹氫箟鏄犲皠瑙勫垯
+        Map<String, String> mappingRules = new HashMap<>();
+        mappingRules.put("orderNumber", "code");
+        mappingRules.put("orderQty", "qty");
+        mappingRules.put("orderAmount", "anfme");
+        
+        // 鎵ц鏇挎崲
+        JSONObject result = ParamsMapUtils.replaceJsonKeys(data, mappingRules);
+        
+        log.info("鏇挎崲鍚庢暟鎹細{}", result);
+        
+        return CommonResponse.ok()
+                .setMsg("鏇挎崲鎴愬姛")
+                .setData(result);
+    }
+
+    /**
+     * 绀轰緥2锛氬祵濂楀璞$殑娣卞害鏇挎崲
+     * 
+     * 杈撳叆锛�
+     * {
+     *   "orderNumber": "PO001",
+     *   "customer": {
+     *     "customerName": "寮犱笁",
+     *     "customerPhone": "13800138000",
+     *     "address": {
+     *       "cityName": "鍖椾含",
+     *       "streetName": "鏈濋槼璺�"
+     *     }
+     *   }
+     * }
+     * 
+     * 杈撳嚭锛�
+     * {
+     *   "code": "PO001",
+     *   "customer": {
+     *     "name": "寮犱笁",
+     *     "phone": "13800138000",
+     *     "address": {
+     *       "city": "鍖椾含",
+     *       "street": "鏈濋槼璺�"
+     *     }
+     *   }
+     * }
+     */
+    @ApiOperation("绀轰緥2锛氬祵濂楀璞℃繁搴︽浛鎹�")
+    @PostMapping("/nested-replace")
+    public CommonResponse nestedReplace(@RequestBody JSONObject data) {
+        log.info("鍘熷宓屽鏁版嵁锛歿}", data);
+        
+        // 瀹氫箟鏄犲皠瑙勫垯锛堥�傜敤浜庢墍鏈夊眰绾э級
+        Map<String, String> mappingRules = new HashMap<>();
+        mappingRules.put("orderNumber", "code");
+        mappingRules.put("customerName", "name");
+        mappingRules.put("customerPhone", "phone");
+        mappingRules.put("cityName", "city");
+        mappingRules.put("streetName", "street");
+        
+        // 閫掑綊鏇挎崲鎵�鏈夊眰绾�
+        JSONObject result = ParamsMapUtils.replaceJsonKeys(data, mappingRules);
+        
+        log.info("鏇挎崲鍚庡祵濂楁暟鎹細{}", result);
+        
+        return CommonResponse.ok()
+                .setMsg("宓屽鏇挎崲鎴愬姛")
+                .setData(result);
+    }
+
+    /**
+     * 绀轰緥3锛氭暟缁勫璞$殑鎵归噺鏇挎崲
+     * 
+     * 杈撳叆锛�
+     * {
+     *   "orderNumber": "PO001",
+     *   "items": [
+     *     {
+     *       "materialCode": "MAT001",
+     *       "materialName": "鐗╂枡A",
+     *       "itemQty": 10
+     *     },
+     *     {
+     *       "materialCode": "MAT002",
+     *       "materialName": "鐗╂枡B",
+     *       "itemQty": 20
+     *     }
+     *   ]
+     * }
+     * 
+     * 杈撳嚭锛�
+     * {
+     *   "code": "PO001",
+     *   "items": [
+     *     {
+     *       "matnr": "MAT001",
+     *       "maktx": "鐗╂枡A",
+     *       "qty": 10
+     *     },
+     *     {
+     *       "matnr": "MAT002",
+     *       "maktx": "鐗╂枡B",
+     *       "qty": 20
+     *     }
+     *   ]
+     * }
+     */
+    @ApiOperation("绀轰緥3锛氭暟缁勫璞℃壒閲忔浛鎹�")
+    @PostMapping("/array-replace")
+    public CommonResponse arrayReplace(@RequestBody JSONObject data) {
+        log.info("鍘熷鏁扮粍鏁版嵁锛歿}", data);
+        
+        // 瀹氫箟鏄犲皠瑙勫垯
+        Map<String, String> mappingRules = new HashMap<>();
+        mappingRules.put("orderNumber", "code");
+        mappingRules.put("materialCode", "matnr");
+        mappingRules.put("materialName", "maktx");
+        mappingRules.put("itemQty", "qty");
+        
+        // 閫掑綊鏇挎崲锛堝寘鎷暟缁勪腑鐨勬墍鏈夊璞★級
+        JSONObject result = ParamsMapUtils.replaceJsonKeys(data, mappingRules);
+        
+        log.info("鏇挎崲鍚庢暟缁勬暟鎹細{}", result);
+        
+        return CommonResponse.ok()
+                .setMsg("鏁扮粍鏇挎崲鎴愬姛")
+                .setData(result);
+    }
+
+    /**
+     * 绀轰緥4锛氬鏉傜粨鏋勭殑瀹屾暣鏇挎崲
+     * 
+     * 杈撳叆鍖呭惈锛氬祵濂楀璞� + 鏁扮粍 + 澶氬眰宓屽
+     */
+    @ApiOperation("绀轰緥4锛氬鏉傜粨鏋勫畬鏁存浛鎹�")
+    @PostMapping("/complex-replace")
+    public CommonResponse complexReplace(@RequestBody JSONObject data) {
+        log.info("鍘熷澶嶆潅鏁版嵁锛歿}", data);
+        
+        // 瀹氫箟瀹屾暣鐨勬槧灏勮鍒�
+        Map<String, String> mappingRules = new HashMap<>();
+        // 璁㈠崟瀛楁
+        mappingRules.put("orderNumber", "code");
+        mappingRules.put("orderType", "type");
+        mappingRules.put("orderQty", "qty");
+        mappingRules.put("orderAmount", "anfme");
+        mappingRules.put("orderStatus", "exceStatus");
+        
+        // 瀹㈡埛瀛楁
+        mappingRules.put("customerName", "custName");
+        mappingRules.put("customerCode", "custCode");
+        
+        // 鐗╂枡瀛楁
+        mappingRules.put("materialCode", "matnr");
+        mappingRules.put("materialName", "maktx");
+        mappingRules.put("materialSpec", "spec");
+        mappingRules.put("materialUnit", "meins");
+        
+        // 鍏朵粬瀛楁
+        mappingRules.put("warehouseCode", "whCode");
+        mappingRules.put("locationCode", "locCode");
+        
+        // 鎵ц閫掑綊鏇挎崲
+        JSONObject result = ParamsMapUtils.replaceJsonKeys(data, mappingRules);
+        
+        log.info("鏇挎崲鍚庡鏉傛暟鎹細{}", result);
+        
+        return CommonResponse.ok()
+                .setMsg("澶嶆潅鏇挎崲鎴愬姛")
+                .setData(result);
+    }
+
+    /**
+     * 绀轰緥5锛氫娇鐢╝ppId鍜宖uncId鑷姩鏄犲皠
+     * 
+     * 杩欎釜绀轰緥灞曠ず濡備綍缁撳悎鏁版嵁搴撻厤缃嚜鍔ㄨ繘琛屽瓧娈垫槧灏�
+     */
+    @ApiOperation("绀轰緥5锛氫娇鐢ㄩ厤缃嚜鍔ㄦ槧灏�")
+    @PostMapping("/auto-replace")
+    public CommonResponse autoReplace(@RequestBody JSONObject request) {
+        String appId = request.getString("appId");
+        String funcId = request.getString("funcId");
+        JSONObject data = request.getJSONObject("data");
+        
+        log.info("鑷姩鏄犲皠 - appId:{}, funcId:{}, 鏁版嵁:{}", appId, funcId, data);
+        
+        // 浣跨敤 FuncMap 鐨� apiMaps 鏂规硶锛屽畠浼氳嚜鍔ㄤ粠鏁版嵁搴撳姞杞芥槧灏勮鍒欏苟閫掑綊鏇挎崲
+        JSONObject result = ParamsMapUtils.apiMaps(appId, funcId, data);
+        
+        log.info("鑷姩鏄犲皠鍚庢暟鎹細{}", result);
+        
+        return CommonResponse.ok()
+                .setMsg("鑷姩鏄犲皠鎴愬姛")
+                .setData(result);
+    }
+
+    /**
+     * 绀轰緥6锛氱洿鎺ユ浛鎹SON鏁扮粍
+     */
+    @ApiOperation("绀轰緥6锛氱洿鎺ユ浛鎹SON鏁扮粍")
+    @PostMapping("/array-direct-replace")
+    public CommonResponse arrayDirectReplace(@RequestBody JSONArray array) {
+        log.info("鍘熷JSON鏁扮粍锛歿}", array);
+        
+        // 瀹氫箟鏄犲皠瑙勫垯
+        Map<String, String> mappingRules = new HashMap<>();
+        mappingRules.put("materialCode", "matnr");
+        mappingRules.put("materialName", "maktx");
+        mappingRules.put("stockQty", "qty");
+        mappingRules.put("warehouseCode", "whCode");
+        
+        // 鐩存帴鏇挎崲鏁扮粍
+        JSONArray result = ParamsMapUtils.replaceJsonArrayKeys(array, mappingRules);
+        
+        log.info("鏇挎崲鍚嶫SON鏁扮粍锛歿}", result);
+        
+        return CommonResponse.ok()
+                .setMsg("鏁扮粍鐩存帴鏇挎崲鎴愬姛")
+                .setData(result);
+    }
+
+    /**
+     * 娴嬭瘯鐢ㄤ緥锛氱敓鎴愮ず渚嬫暟鎹�
+     */
+    @ApiOperation("鐢熸垚娴嬭瘯鏁版嵁")
+    @PostMapping("/generate-test-data")
+    public CommonResponse generateTestData() {
+        // 鍒涘缓澶嶆潅鐨勬祴璇曟暟鎹�
+        JSONObject testData = new JSONObject();
+        testData.put("orderNumber", "PO20260104001");
+        testData.put("orderType", "PURCHASE");
+        testData.put("orderQty", 500);
+        testData.put("orderAmount", 25000.00);
+        testData.put("orderStatus", "PENDING");
+        
+        // 瀹㈡埛淇℃伅
+        JSONObject customer = new JSONObject();
+        customer.put("customerCode", "CUST001");
+        customer.put("customerName", "鍖椾含绉戞妧鏈夐檺鍏徃");
+        
+        JSONObject address = new JSONObject();
+        address.put("cityName", "鍖椾含");
+        address.put("districtName", "鏈濋槼鍖�");
+        address.put("streetName", "寤哄浗璺�88鍙�");
+        customer.put("address", address);
+        
+        testData.put("customer", customer);
+        
+        // 璁㈠崟鏄庣粏
+        JSONArray items = new JSONArray();
+        for (int i = 1; i <= 3; i++) {
+            JSONObject item = new JSONObject();
+            item.put("itemNo", i);
+            item.put("materialCode", "MAT00" + i);
+            item.put("materialName", "娴嬭瘯鐗╂枡" + i);
+            item.put("materialSpec", "瑙勬牸" + i);
+            item.put("materialUnit", "EA");
+            item.put("itemQty", 100 + i * 50);
+            item.put("itemAmount", 5000.00 + i * 1000);
+            
+            // 浠撳簱淇℃伅
+            JSONObject warehouse = new JSONObject();
+            warehouse.put("warehouseCode", "WH0" + i);
+            warehouse.put("locationCode", "LOC-A-0" + i);
+            item.put("warehouse", warehouse);
+            
+            items.add(item);
+        }
+        testData.put("items", items);
+        
+        return CommonResponse.ok()
+                .setMsg("娴嬭瘯鏁版嵁鐢熸垚鎴愬姛")
+                .setData(testData);
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/TokenAuthExampleController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/TokenAuthExampleController.java
new file mode 100644
index 0000000..a531bea
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/example/TokenAuthExampleController.java
@@ -0,0 +1,182 @@
+package com.vincent.rsf.openApi.controller.example;
+
+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.AppAuthParam;
+import com.vincent.rsf.openApi.security.service.AppAuthService;
+import com.vincent.rsf.openApi.security.utils.TokenUtils;
+import io.swagger.annotations.ApiOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Token璁よ瘉绀轰緥鎺у埗鍣�
+ * 婕旂ず濡備綍浣跨敤JWT Token杩涜鎺ュ彛淇濇姢
+ */
+@RestController
+@RequestMapping("/api/example/token")
+public class TokenAuthExampleController {
+    private static final Logger log = LoggerFactory.getLogger(TokenAuthExampleController.class);
+
+    @Resource
+    private AppAuthService appAuthService;
+
+    /**
+     * 鑾峰彇鍙椾繚鎶ょ殑鏁版嵁 - 闇�瑕佹湁鏁堢殑Token
+     * 
+     * @param request HTTP璇锋眰
+     * @return 鍙椾繚鎶ょ殑鏁版嵁
+     */
+    @GetMapping("/protected-data")
+    public Map<String, Object> getProtectedData(HttpServletRequest request) {
+        // 浠庤姹傚睘鎬т腑鑾峰彇璁よ瘉淇℃伅锛堢敱TokenAuthenticationFilter璁剧疆锛�
+        String appId = (String) request.getAttribute(Constants.REQUEST_ATTR_APP_ID);
+        String userId = (String) request.getAttribute(Constants.REQUEST_ATTR_USER_ID);
+        
+        log.info("璁块棶鍙椾繚鎶ゆ帴鍙o紝AppId: {}, UserId: {}", appId, userId);
+        
+        Map<String, Object> response = new HashMap<>();
+        response.put("code", "200");
+        response.put("message", "璁块棶鎴愬姛");
+        response.put("data", Map.of(
+            "appId", appId,
+            "userId", userId,
+            "protectedInfo", "杩欐槸鍙椾繚鎶ょ殑鏁版嵁",
+            "timestamp", System.currentTimeMillis()
+        ));
+        response.put("success", true);
+        
+        return response;
+    }
+
+    /**
+     * 鑾峰彇鐢ㄦ埛淇℃伅 - 闇�瑕佹湁鏁堢殑Token
+     * 
+     * @param request HTTP璇锋眰
+     * @return 鐢ㄦ埛淇℃伅
+     */
+    @GetMapping("/user-info")
+    public Map<String, Object> getUserInfo(HttpServletRequest request) {
+        // 浠庤姹傚睘鎬т腑鑾峰彇璁よ瘉淇℃伅
+        String appId = (String) request.getAttribute(Constants.REQUEST_ATTR_APP_ID);
+        String userId = (String) request.getAttribute(Constants.REQUEST_ATTR_USER_ID);
+        
+        log.info("鑾峰彇鐢ㄦ埛淇℃伅锛孉ppId: {}, UserId: {}", appId, userId);
+        
+        Map<String, Object> response = new HashMap<>();
+        response.put("code", "200");
+        response.put("message", "鑾峰彇鐢ㄦ埛淇℃伅鎴愬姛");
+        response.put("data", Map.of(
+            "appId", appId,
+            "userId", userId,
+            "userName", "鐢ㄦ埛" + (userId != null ? userId : "鏈煡"),
+            "role", "USER",
+            "permissions", new String[]{"read", "write"}
+        ));
+        response.put("success", true);
+        
+        return response;
+    }
+
+    /**
+     * 鎵嬪姩鐢熸垚Token鐨勭ず渚嬫帴鍙�
+     * 娉ㄦ剰锛氬湪瀹為檯搴旂敤涓紝杩欎釜鎺ュ彛閫氬父闇�瑕佸叾浠栧舰寮忕殑璁よ瘉
+     * 
+     * @param appId 搴旂敤ID
+     * @param userId 鐢ㄦ埛ID
+     * @return 鍖呭惈Token鐨勫搷搴�
+     */
+    @PostMapping("/generate-token")
+    public Map<String, Object> generateToken(@RequestParam String appId, @RequestParam(required = false) String userId) {
+        log.info("鐢熸垚Token锛孉ppId: {}, UserId: {}", appId, userId);
+        
+        try {
+            // 鐢熸垚Token
+            String token = TokenUtils.generateToken(appId, userId);
+            
+            Map<String, Object> response = new HashMap<>();
+            response.put("code", "200");
+            response.put("message", "Token鐢熸垚鎴愬姛");
+            response.put("data", Map.of(
+                "token", token,
+                "appId", appId,
+                "userId", userId,
+                "expiresIn", 24 * 60 * 60 // 24灏忔椂杩囨湡
+            ));
+            response.put("success", true);
+            
+            return response;
+        } catch (Exception e) {
+            log.error("鐢熸垚Token澶辫触", e);
+            
+            Map<String, Object> response = new HashMap<>();
+            response.put("code", "500");
+            response.put("message", "鐢熸垚Token澶辫触: " + e.getMessage());
+            response.put("data", null);
+            response.put("success", false);
+            
+            return response;
+        }
+    }
+
+
+
+    /**
+     * 鑾峰彇App璁よ瘉Token
+     *
+     * @param param 搴旂敤ID鍜屽簲鐢ㄥ瘑閽�
+     * @return 璁よ瘉Token
+     */
+    @ApiOperation("鑾峰彇App璁よ瘉Token")
+    @PostMapping("/getToken")
+    public CommonResponse getToken(@RequestBody AppAuthParam param) {
+        String appId = param.getAppId();
+        String appSecret = param.getAppSecret();
+
+        if (Cools.isEmpty(appId, appSecret)) {
+            return CommonResponse.error("AppId鍜孉ppSecret涓嶈兘涓虹┖");
+        }
+
+        boolean isValid = appAuthService.validateApp(appId, appSecret);
+        if (isValid) {
+            String token = appAuthService.generateAppToken(appId, appSecret);
+            return CommonResponse.ok()
+                    .setMsg("鑾峰彇Token鎴愬姛")
+                    .setData(token);
+        } else {
+            return CommonResponse.error("AppId鎴朅ppSecret鏃犳晥");
+        }
+    }
+
+//    /**
+//     * 楠岃瘉Token鐨勬帴鍙�
+//     *
+//     * @param token 瑕侀獙璇佺殑Token
+//     * @return Token楠岃瘉缁撴灉
+//     */
+//    @PostMapping("/validateToken")
+//    public Map<String, Object> validateToken(@RequestParam String token) {
+//        log.info("楠岃瘉Token: {}", token.substring(0, Math.min(10, token.length())) + "...");
+//
+//        boolean isValid = TokenUtils.validateToken(token);
+//
+//        Map<String, Object> response = new HashMap<>();
+//        response.put("code", "200");
+//        response.put("message", isValid ? "Token鏈夋晥" : "Token鏃犳晥");
+//        response.put("data", Map.of(
+//                "valid", isValid,
+//                "appId", isValid ? TokenUtils.getAppIdFromToken(token) : null,
+//                "userId", isValid ? TokenUtils.getUserIdFromToken(token) : null
+//        ));
+//        response.put("success", isValid);
+//
+//        return response;
+//    }
+}
\ No newline at end of file
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/ERPController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/ERPController.java
new file mode 100644
index 0000000..b1c826e
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/ERPController.java
@@ -0,0 +1,284 @@
+package com.vincent.rsf.openApi.controller.phyz;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.vincent.rsf.framework.exception.CoolException;
+import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.entity.phyz.*;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.compress.utils.Lists;
+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.List;
+import java.util.Objects;
+
+import static com.vincent.rsf.openApi.controller.AuthController.SIMULATED_DATA_ENABLE;
+
+@RestController
+@RequestMapping("/erp")
+@Api("閾跺骇鏂板伐鍘傦紙浜旀湡锛塃RP鎺ュ彛")
+@Slf4j
+public class ERPController {
+
+    @ApiOperation("浠撳簱淇℃伅鍚屾")
+    @PostMapping("/wareHouse/sync")
+    public CommonResponse syncWareHouse(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        List<Warehouse> warehouseList = JSON.parseArray(params.toJSONString(), Warehouse.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+
+    @ApiOperation("鐗╂枡淇℃伅鍚屾")
+    @PostMapping("/mat/sync")
+    public CommonResponse syncMaterial(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        List<Material> materialList = JSON.parseArray(params.toJSONString(), Material.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+
+    @ApiOperation("瀹㈡埛淇℃伅鍚屾")
+    @PostMapping("/customer/sync")
+    public CommonResponse syncCustomer(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        List<Customer> customerList = JSON.parseArray(params.toJSONString(), Customer.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+
+    @ApiOperation("渚涘簲鍟嗕俊鎭悓姝�")
+    @PostMapping("/supplier/sync")
+    public CommonResponse syncSupplier(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        List<Supplier> supplierList = JSON.parseArray(params.toJSONString(), Supplier.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+
+    @ApiOperation("鍏�/鍑哄簱浠诲姟閫氱煡鍗�")
+    @PostMapping("/order/add")
+    public CommonResponse addOrder(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        List<Order> orderList = JSON.parseArray(params.toJSONString(), Order.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+
+    @ApiOperation("鍏�/鍑哄簱浠诲姟閫氱煡鍗曞彇娑�")
+    @PostMapping("/order/cancel")
+    public CommonResponse cancelOrder(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        List<Order> orderList = JSON.parseArray(params.toJSONString(), Order.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+
+    @ApiOperation("搴撳瓨鏌ヨ鏄庣粏")
+    @PostMapping("/inventory/details")
+    public CommonResponse queryInventoryDetails(@RequestBody JSONObject params) {
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            String x = "[\n" +
+                    "  {\n" +
+                    "    \"locId\": \"LOC-A-01-01\",\n" +
+                    "    \"wareHouseId\": \"WH001\",\n" +
+                    "    \"wareHouseName\": \"鍘熸枡浠撳簱\",\n" +
+                    "    \"palletId\": \"PALLET001\",\n" +
+                    "    \"matNr\": \"MAT10001\",\n" +
+                    "    \"makTx\": \"閽㈡潗Q235\",\n" +
+                    "    \"spec\": \"鍥芥爣GB/T700-2006\",\n" +
+                    "    \"anfme\": 10.5,\n" +
+                    "    \"unit\": \"鍚╘",\n" +
+                    "    \"status\": \"鍙敤\",\n" +
+                    "    \"orderType\": 1,\n" +
+                    "    \"orderNo\": \"Order202698921\",\n" +
+                    "    \"prepareType\": 1,\n" +
+                    "    \"planNo\": \"PLAN202601060001\",\n" +
+                    "    \"batch\": \"BATCH20260106001\",\n" +
+                    "    \"stockOrgId\": \"ORG001\"\n" +
+                    "  },\n" +
+                    "  {\n" +
+                    "    \"locId\": \"LOC-B-02-03\",\n" +
+                    "    \"wareHouseId\": \"WH002\",\n" +
+                    "    \"wareHouseName\": \"鎴愬搧浠撳簱\",\n" +
+                    "    \"palletId\": \"PALLET002\",\n" +
+                    "    \"matNr\": \"MAT20001\",\n" +
+                    "    \"makTx\": \"鐢垫満缁勪欢\",\n" +
+                    "    \"spec\": \"380V 50Hz\",\n" +
+                    "    \"anfme\": 50,\n" +
+                    "    \"unit\": \"鍙癨",\n" +
+                    "    \"status\": \"鍙敤\",\n" +
+                    "    \"orderType\": \"1\",\n" +
+                    "    \"orderNo\": \"SO202601060001\",\n" +
+                    "    \"prepareType\": 1,\n" +
+                    "    \"planNo\": \"PLAN202601060002\",\n" +
+                    "    \"batch\": \"BATCH20260106002\",\n" +
+                    "    \"stockOrgId\": \"ORG001\"\n" +
+                    "  }\n" +
+                    "]";
+            return CommonResponse.ok(JSONArray.parseArray(x, InventoryDetails.class));
+        }
+
+        InventoryQueryCondition condition = JSON.parseObject(params.toJSONString(), InventoryQueryCondition.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        List<InventoryDetails> inventoryDetails = Lists.newArrayList();
+        return new CommonResponse().setCode(200).setData(inventoryDetails);
+    }
+
+    @ApiOperation("搴撳瓨鏌ヨ姹囨��")
+    @PostMapping("/inventory/summary")
+    public CommonResponse queryInventorySummary(@RequestBody JSONObject params) {
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            String s = "{\n" +
+                    "  \"code\": 200,\n" +
+                    "  \"msg\": \"鎿嶄綔鎴愬姛\",\n" +
+                    "  \"data\": [\n" +
+                    "    {\n" +
+                    "      \"wareHouseId\": \"WH001\",\n" +
+                    "      \"wareHouseName\": \"鍘熸枡浠撳簱\",\n" +
+                    "      \"matNr\": \"MAT10001\",\n" +
+                    "      \"makTx\": \"閽㈡潗Q235\",\n" +
+                    "      \"spec\": \"鍥芥爣GB/T700-2006\",\n" +
+                    "      \"anfme\": 10.5,\n" +
+                    "      \"unit\": \"鍚╘",\n" +
+                    "      \"stockOrgId\": \"ORG001\",\n" +
+                    "      \"batch\": \"BATCH20260106001\",\n" +
+                    "      \"planNo\": \"Plan20260106006\"\n" +
+                    "    },\n" +
+                    "    {\n" +
+                    "      \"wareHouseId\": \"WH001\",\n" +
+                    "      \"wareHouseName\": \"鍘熸枡浠撳簱\",\n" +
+                    "      \"matNr\": \"MAT10002\",\n" +
+                    "      \"makTx\": \"閾濆瀷鏉�6061\",\n" +
+                    "      \"spec\": \"鍥芥爣GB/T3190-2008\",\n" +
+                    "      \"anfme\": 20.3,\n" +
+                    "      \"unit\": \"鍚╘",\n" +
+                    "      \"stockOrgId\": \"ORG001\",\n" +
+                    "      \"batch\": \"BATCH20260106002\",\n" +
+                    "      \"planNo\": \"Plan20260106005\"\n" +
+                    "    },\n" +
+                    "    {\n" +
+                    "      \"wareHouseId\": \"WH002\",\n" +
+                    "      \"wareHouseName\": \"鎴愬搧浠撳簱\",\n" +
+                    "      \"matNr\": \"MAT30001\",\n" +
+                    "      \"makTx\": \"鐢垫満鎴愬搧\",\n" +
+                    "      \"spec\": \"380V 50Hz 15KW\",\n" +
+                    "      \"anfme\": 100,\n" +
+                    "      \"unit\": \"鍙癨",\n" +
+                    "      \"stockOrgId\": \"ORG001\",\n" +
+                    "      \"batch\": \"BATCH20260106003\",\n" +
+                    "      \"planNo\": \"Plan20260106004\"\n" +
+                    "    }\n" +
+                    "  ]\n" +
+                    "}";
+            return JSONObject.parseObject(s, CommonResponse.class);
+        }
+
+        InventoryQueryCondition condition = JSON.parseObject(params.toJSONString(), InventoryQueryCondition.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        List<InventorySummary> inventorySummaries = Lists.newArrayList();
+        return new CommonResponse().setCode(200).setData(inventorySummaries);
+
+    }
+
+    @ApiOperation("鐩樼偣缁撴灉纭")
+    @PostMapping("/check/confirm")
+    public CommonResponse checkConfirm(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        CheckOrder checkResult = JSON.parseObject(params.toJSONString(), CheckOrder.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+
+    /**
+     * 鍏煎JSONObject鍜孞SONArray鏍煎紡鏁版嵁
+     *
+     * @param data json鏍煎紡鍙傛暟
+     * @return JSONArray鏍煎紡鏁版嵁
+     */
+    public static JSONArray paramsFormat(Object data) {
+        if (Objects.isNull(data)) {
+            return new JSONArray();
+        }
+
+        try {
+            String jsonStr = JSON.toJSONString(data);
+            if (jsonStr.startsWith("[")) {
+                return JSON.parseArray(jsonStr);
+            } else if (jsonStr.startsWith("{")) {
+                JSONArray params = new JSONArray();
+                params.add(JSON.parseObject(jsonStr));
+                return params;
+            }
+        } catch (Exception e) {
+            // 瑙f瀽澶辫触锛岃繑鍥炵┖鏁扮粍
+            log.error("杞崲鍙傛暟涓簀son鏍煎紡閿欒", e);
+        }
+
+        // 榛樿杩斿洖绌烘暟缁�
+        return new JSONArray();
+    }
+
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/MESController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/MESController.java
new file mode 100644
index 0000000..734a128
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/phyz/MESController.java
@@ -0,0 +1,94 @@
+package com.vincent.rsf.openApi.controller.phyz;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.vincent.rsf.framework.exception.CoolException;
+import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.entity.phyz.*;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+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.List;
+import java.util.Objects;
+
+import static com.vincent.rsf.openApi.controller.AuthController.SIMULATED_DATA_ENABLE;
+import static com.vincent.rsf.openApi.controller.phyz.ERPController.paramsFormat;
+
+@RestController
+@RequestMapping("/mes")
+@Api("閾跺骇鏂板伐鍘傦紙浜旀湡锛塎ES鎺ュ彛")
+public class MESController {
+
+    @ApiOperation("澶囨枡閫氱煡")
+    @PostMapping("/callMaterial")
+    public CommonResponse callMaterial(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("0")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        List<MatPreparationOrder> orders = JSON.parseArray(params.toJSONString(), MatPreparationOrder.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+
+    @ApiOperation("绠�鍗曠敓浜ч鏂欍�侀��鏂橝GV浠诲姟")
+    @PostMapping("/transTask/simpleProduction")
+    public CommonResponse simpleProductionTask(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("0")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        List<SimpleProductionTask> tasks = JSON.parseArray(params.toJSONString(), SimpleProductionTask.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+
+    @ApiOperation("绌烘墭鍑哄簱")
+    @PostMapping("/emptyTray/callOut")
+    public CommonResponse callOutEmptyTray(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        List<Task> tasks = JSON.parseArray(params.toJSONString(), Task.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+
+    @ApiOperation("绌烘墭鍏ュ簱")
+    @PostMapping("/emptyTray/backIn")
+    public CommonResponse emptyTrayBackIn(@RequestBody Object objParams) {
+        if (Objects.isNull(objParams)) {
+            throw new CoolException("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        // 杩斿洖妯℃嫙鏁版嵁
+        if (SIMULATED_DATA_ENABLE.equals("1")) {
+            return CommonResponse.ok();
+        }
+
+        JSONArray params = paramsFormat(objParams);
+        List<Task> tasks = JSON.parseArray(params.toJSONString(), Task.class);
+        // 鏁版嵁澶勭悊锛岃浆鍙憇erver
+        return CommonResponse.ok();
+    }
+
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/ApiFunctionController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/ApiFunctionController.java
new file mode 100644
index 0000000..56eb6d9
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/ApiFunctionController.java
@@ -0,0 +1,100 @@
+package com.vincent.rsf.openApi.controller.platform;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.vincent.rsf.openApi.entity.app.ApiFunction;
+import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.service.ApiFunctionService;
+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;
+
+/**
+ * ApiFunction绠$悊Controller
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+@RestController
+@RequestMapping("/api/function")
+@Api(tags = "鎺ュ彛鍔熻兘绠$悊")
+public class ApiFunctionController {
+
+    @Autowired
+    private ApiFunctionService apiFunctionService;
+
+    @ApiOperation("鍒嗛〉鏌ヨ鎺ュ彛鍔熻兘鍒楄〃")
+    @GetMapping("/page")
+    public CommonResponse page(@RequestParam(defaultValue = "1") Integer current,
+                                @RequestParam(defaultValue = "10") Integer size) {
+        Page<ApiFunction> page = apiFunctionService.page(new Page<>(current, size));
+        return CommonResponse.ok().setData(page);
+    }
+
+    @ApiOperation("鏌ヨ鎵�鏈夋帴鍙e姛鑳�")
+    @GetMapping("/list")
+    public CommonResponse list() {
+        List<ApiFunction> list = apiFunctionService.list();
+        return CommonResponse.ok().setData(list);
+    }
+
+    @ApiOperation("鏍规嵁ID鏌ヨ鎺ュ彛鍔熻兘")
+    @GetMapping("/{id}")
+    public CommonResponse getById(@PathVariable String id) {
+        ApiFunction function = apiFunctionService.getById(id);
+        return CommonResponse.ok().setData(function);
+    }
+
+    @ApiOperation("鏂板鎺ュ彛鍔熻兘")
+    @PostMapping
+    public CommonResponse save(@RequestBody ApiFunction function) {
+        boolean result = apiFunctionService.save(function);
+        if (result) {
+            apiFunctionService.refreshCache();
+            return CommonResponse.ok().setMsg("鏂板鎴愬姛");
+        }
+        return CommonResponse.error("鏂板澶辫触");
+    }
+
+    @ApiOperation("鏇存柊鎺ュ彛鍔熻兘")
+    @PutMapping
+    public CommonResponse update(@RequestBody ApiFunction function) {
+        boolean result = apiFunctionService.updateById(function);
+        if (result) {
+            apiFunctionService.refreshCache();
+            return CommonResponse.ok().setMsg("鏇存柊鎴愬姛");
+        }
+        return CommonResponse.error("鏇存柊澶辫触");
+    }
+
+    @ApiOperation("鍒犻櫎鎺ュ彛鍔熻兘")
+    @DeleteMapping("/{id}")
+    public CommonResponse delete(@PathVariable String id) {
+        boolean result = apiFunctionService.removeById(id);
+        if (result) {
+            apiFunctionService.refreshCache();
+            return CommonResponse.ok().setMsg("鍒犻櫎鎴愬姛");
+        }
+        return CommonResponse.error("鍒犻櫎澶辫触");
+    }
+
+    @ApiOperation("鎵归噺鍒犻櫎鎺ュ彛鍔熻兘")
+    @DeleteMapping("/batch")
+    public CommonResponse deleteBatch(@RequestBody List<String> ids) {
+        boolean result = apiFunctionService.removeByIds(ids);
+        if (result) {
+            apiFunctionService.refreshCache();
+            return CommonResponse.ok().setMsg("鎵归噺鍒犻櫎鎴愬姛");
+        }
+        return CommonResponse.error("鎵归噺鍒犻櫎澶辫触");
+    }
+
+    @ApiOperation("鍒锋柊鍔熻兘缂撳瓨")
+    @PostMapping("/refresh")
+    public CommonResponse refresh() {
+        apiFunctionService.refreshCache();
+        return CommonResponse.ok().setMsg("缂撳瓨鍒锋柊鎴愬姛");
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/ApiMapController.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/ApiMapController.java
new file mode 100644
index 0000000..137bbe7
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/ApiMapController.java
@@ -0,0 +1,138 @@
+package com.vincent.rsf.openApi.controller.platform;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.vincent.rsf.openApi.entity.app.ApiMap;
+import com.vincent.rsf.openApi.entity.dto.CommonResponse;
+import com.vincent.rsf.openApi.service.ApiMapService;
+import com.vincent.rsf.openApi.utils.ParamsMapUtils;
+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;
+
+/**
+ * ApiMap绠$悊Controller
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+@RestController
+@RequestMapping("/api/map")
+@Api(tags = "瀛楁鏄犲皠绠$悊")
+public class ApiMapController {
+
+    @Autowired
+    private ApiMapService apiMapService;
+
+
+    @ApiOperation("鍒嗛〉鏌ヨ瀛楁鏄犲皠鍒楄〃")
+    @GetMapping("/page")
+    public CommonResponse page(@RequestParam(defaultValue = "1") Integer current,
+                                @RequestParam(defaultValue = "10") Integer size,
+                                @RequestParam(required = false) String appId,
+                                @RequestParam(required = false) String funcId) {
+        LambdaQueryWrapper<ApiMap> wrapper = new LambdaQueryWrapper<>();
+        if (appId != null && !appId.isEmpty()) {
+            wrapper.eq(ApiMap::getAppId, appId);
+        }
+        if (funcId != null && !funcId.isEmpty()) {
+            wrapper.eq(ApiMap::getFuncId, funcId);
+        }
+        Page<ApiMap> page = apiMapService.page(new Page<>(current, size), wrapper);
+        return CommonResponse.ok().setData(page);
+    }
+
+    @ApiOperation("鏌ヨ鎵�鏈夊瓧娈垫槧灏�")
+    @GetMapping("/list")
+    public CommonResponse list(@RequestParam(required = false) String appId,
+                                @RequestParam(required = false) String funcId) {
+        LambdaQueryWrapper<ApiMap> wrapper = new LambdaQueryWrapper<>();
+        if (appId != null && !appId.isEmpty()) {
+            wrapper.eq(ApiMap::getAppId, appId);
+        }
+        if (funcId != null && !funcId.isEmpty()) {
+            wrapper.eq(ApiMap::getFuncId, funcId);
+        }
+        List<ApiMap> list = apiMapService.list(wrapper);
+        return CommonResponse.ok().setData(list);
+    }
+
+    @ApiOperation("鏍规嵁ID鏌ヨ瀛楁鏄犲皠")
+    @GetMapping("/{id}")
+    public CommonResponse getById(@PathVariable Integer id) {
+        ApiMap map = apiMapService.getById(id);
+        return CommonResponse.ok().setData(map);
+    }
+
+    @ApiOperation("鏂板瀛楁鏄犲皠")
+    @PostMapping
+    public CommonResponse save(@RequestBody ApiMap map) {
+        boolean result = apiMapService.save(map);
+        if (result) {
+            apiMapService.refreshCache();
+            return CommonResponse.ok().setMsg("鏂板鎴愬姛");
+        }
+        return CommonResponse.error("鏂板澶辫触");
+    }
+
+    @ApiOperation("鎵归噺鏂板瀛楁鏄犲皠")
+    @PostMapping("/batch")
+    public CommonResponse saveBatch(@RequestBody List<ApiMap> maps) {
+        boolean result = apiMapService.saveBatch(maps);
+        if (result) {
+            apiMapService.refreshCache();
+            return CommonResponse.ok().setMsg("鎵归噺鏂板鎴愬姛");
+        }
+        return CommonResponse.error("鎵归噺鏂板澶辫触");
+    }
+
+    @ApiOperation("鏇存柊瀛楁鏄犲皠")
+    @PutMapping
+    public CommonResponse update(@RequestBody ApiMap map) {
+        boolean result = apiMapService.updateById(map);
+        if (result) {
+            apiMapService.refreshCache();
+            return CommonResponse.ok().setMsg("鏇存柊鎴愬姛");
+        }
+        return CommonResponse.error("鏇存柊澶辫触");
+    }
+
+    @ApiOperation("鍒犻櫎瀛楁鏄犲皠")
+    @DeleteMapping("/{id}")
+    public CommonResponse delete(@PathVariable Integer id) {
+        boolean result = apiMapService.removeById(id);
+        if (result) {
+            apiMapService.refreshCache();
+            return CommonResponse.ok().setMsg("鍒犻櫎鎴愬姛");
+        }
+        return CommonResponse.error("鍒犻櫎澶辫触");
+    }
+
+    @ApiOperation("鎵归噺鍒犻櫎瀛楁鏄犲皠")
+    @DeleteMapping("/batch")
+    public CommonResponse deleteBatch(@RequestBody List<Integer> ids) {
+        boolean result = apiMapService.removeByIds(ids);
+        if (result) {
+            apiMapService.refreshCache();
+            return CommonResponse.ok().setMsg("鎵归噺鍒犻櫎鎴愬姛");
+        }
+        return CommonResponse.error("鎵归噺鍒犻櫎澶辫触");
+    }
+
+    @ApiOperation("鍒锋柊鏄犲皠缂撳瓨")
+    @PostMapping("/refresh")
+    public CommonResponse refresh() {
+        apiMapService.refreshCache();
+        return CommonResponse.ok().setMsg("缂撳瓨鍒锋柊鎴愬姛");
+    }
+
+//    @ApiOperation("鍒锋柊鎵�鏈夌紦瀛�")
+//    @PostMapping("/refresh/all")
+//    public CommonResponse refreshAll() {
+//        new ParamsMapUtils().refreshAll();
+//        return CommonResponse.ok().setMsg("鎵�鏈夌紦瀛樺埛鏂版垚鍔�");
+//    }
+}
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..fe3fcab
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/controller/platform/AppController.java
@@ -0,0 +1,100 @@
+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;
+
+/**
+ * App绠$悊Controller
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+@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) {
+        boolean result = appService.save(app);
+        if (result) {
+            appService.refreshCache();
+            return CommonResponse.ok().setMsg("鏂板鎴愬姛");
+        }
+        return CommonResponse.error("鏂板澶辫触");
+    }
+
+    @ApiOperation("鏇存柊搴旂敤")
+    @PutMapping
+    public CommonResponse update(@RequestBody App app) {
+        boolean result = appService.updateById(app);
+        if (result) {
+            appService.refreshCache();
+            return CommonResponse.ok().setMsg("鏇存柊鎴愬姛");
+        }
+        return CommonResponse.error("鏇存柊澶辫触");
+    }
+
+    @ApiOperation("鍒犻櫎搴旂敤")
+    @DeleteMapping("/{id}")
+    public CommonResponse delete(@PathVariable String id) {
+        boolean result = appService.removeById(id);
+        if (result) {
+            appService.refreshCache();
+            return CommonResponse.ok().setMsg("鍒犻櫎鎴愬姛");
+        }
+        return CommonResponse.error("鍒犻櫎澶辫触");
+    }
+
+    @ApiOperation("鎵归噺鍒犻櫎搴旂敤")
+    @DeleteMapping("/batch")
+    public CommonResponse deleteBatch(@RequestBody List<String> ids) {
+        boolean result = appService.removeByIds(ids);
+        if (result) {
+            appService.refreshCache();
+            return CommonResponse.ok().setMsg("鎵归噺鍒犻櫎鎴愬姛");
+        }
+        return CommonResponse.error("鎵归噺鍒犻櫎澶辫触");
+    }
+
+    @ApiOperation("鍒锋柊搴旂敤缂撳瓨")
+    @PostMapping("/refresh")
+    public CommonResponse refresh() {
+        appService.refreshCache();
+        return CommonResponse.ok().setMsg("缂撳瓨鍒锋柊鎴愬姛");
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/AppAuthParam.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/AppAuthParam.java
new file mode 100644
index 0000000..96345a0
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/AppAuthParam.java
@@ -0,0 +1,26 @@
+package com.vincent.rsf.openApi.entity;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * App璁よ瘉鍙傛暟
+ * 
+ * 鐢ㄤ簬AppId鍜孉ppSecret鐨勮璇佽姹�
+ * 
+ * @author vincent
+ * @since 2026-01-05
+ */
+@Data
+@Accessors(chain = true)
+@ApiModel(value = "AppAuthParam", description = "App璁よ瘉鍙傛暟")
+public class AppAuthParam {
+
+    @ApiModelProperty(value = "搴旂敤ID", required = true)
+    private String appId;
+
+    @ApiModelProperty(value = "搴旂敤瀵嗛挜", required = true)
+    private String appSecret;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/ApiForeignLog.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/ApiForeignLog.java
new file mode 100644
index 0000000..2c743c9
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/ApiForeignLog.java
@@ -0,0 +1,136 @@
+package com.vincent.rsf.openApi.entity.app;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.vincent.rsf.framework.common.Cools;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+@Data
+@TableName("open_api_foreign_log")
+public class ApiForeignLog implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * ID
+     */
+    @ApiModelProperty(value= "ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 鍚嶇О绌洪棿
+     */
+    @ApiModelProperty(value= "鍚嶇О绌洪棿")
+    private String namespace;
+
+    /**
+     * 鎺ュ彛鍦板潃
+     */
+    @ApiModelProperty(value= "鎺ュ彛鍦板潃")
+    private String url;
+
+    /**
+     * 骞冲彴瀵嗛挜
+     */
+    @ApiModelProperty(value= "骞冲彴瀵嗛挜")
+    private String appkey;
+
+    /**
+     * 鏃堕棿鎴�
+     */
+    @ApiModelProperty(value= "鏃堕棿鎴�")
+    private String timestamp;
+
+    /**
+     * 瀹㈡埛绔疘P
+     */
+    @ApiModelProperty(value= "瀹㈡埛绔疘P")
+    private String clientIp;
+
+    /**
+     * 璇锋眰鍐呭
+     */
+    @ApiModelProperty(value= "璇锋眰鍐呭")
+    private String request;
+
+    /**
+     * 鍝嶅簲鍐呭
+     */
+    @ApiModelProperty(value= "鍝嶅簲鍐呭")
+    private String response;
+
+    /**
+     * 娑堣�楁椂闂�
+     */
+    @ApiModelProperty(value= "娑堣�楁椂闂�")
+    private Integer spendTime;
+
+    /**
+     * 寮傚父鍐呭
+     */
+    @ApiModelProperty(value= "寮傚父鍐呭")
+    private String err;
+
+    /**
+     * 缁撴灉 1: 鎴愬姛  0: 澶辫触
+     */
+    @ApiModelProperty(value= "缁撴灉 1: 鎴愬姛  0: 澶辫触  ")
+    private Integer result;
+
+    /**
+     * 鐢ㄦ埛
+     */
+    @ApiModelProperty(value= "鐢ㄦ埛")
+    private Long userId;
+
+    /**
+     * 鎵�灞炴満鏋�
+     */
+    @ApiModelProperty(value= "鎵�灞炴満鏋�")
+    private Long tenantId;
+
+    /**
+     * 娣诲姞鏃堕棿
+     */
+    @ApiModelProperty(value= "娣诲姞鏃堕棿")
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+    private Date createTime;
+
+    /**
+     * 澶囨敞
+     */
+    @ApiModelProperty(value= "澶囨敞")
+    private String memo;
+
+    public String getResult$(){
+        if (null == this.result){ return null; }
+        switch (this.result){
+            case 1:
+                return "鎴愬姛";
+            case 0:
+                return "澶辫触";
+            default:
+                return String.valueOf(this.result);
+        }
+    }
+
+    public String getCreateTime$(){
+        if (Cools.isEmpty(this.createTime)){
+            return "";
+        }
+        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
+    }
+
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/ApiFunction.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/ApiFunction.java
new file mode 100644
index 0000000..846ce79
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/ApiFunction.java
@@ -0,0 +1,48 @@
+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.Serial;
+import java.io.Serializable;
+
+@Data
+@TableName("open_api_function")
+public class ApiFunction implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * functionId
+     */
+    @ApiModelProperty(value= "functionId")
+    @TableId(value = "id")
+    private String id;
+
+    /**
+     * functionName
+     */
+    @ApiModelProperty(value = "functionName")
+    private String name;
+
+    /**
+     * functionType锛�1 WMS鎺ユ敹锛�2 WMS鍙戦�侊紱
+     */
+    @ApiModelProperty(value = "functionType")
+    private Integer type;
+
+    /**
+     * functionUrl锛屽搴攔sf-server鐨刢ontroller鎺ュ彛鐩稿璺緞
+     */
+    @ApiModelProperty(value = "functionUrl")
+    private String url;
+
+    /**
+     * 绉熸埛id
+     */
+    @ApiModelProperty(value = "绉熸埛id")
+    private Long tenant_id;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/ApiMap.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/ApiMap.java
new file mode 100644
index 0000000..6a87ed2
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/ApiMap.java
@@ -0,0 +1,78 @@
+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.Serial;
+import java.io.Serializable;
+
+@Data
+@TableName("open_api_attribute_map")
+public class ApiMap implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    @ApiModelProperty(value= "id")
+    @TableId(value = "id")
+    private Integer id;
+
+    /**
+     * appId
+     */
+    @ApiModelProperty(value= "appId")
+    private String appId;
+
+    /**
+     * func_id
+     */
+    @ApiModelProperty(value= "func_id")
+    private String funcId;
+
+    /**
+     * func_type
+     */
+    @ApiModelProperty(value= "func_type")
+    private String funcType;
+
+    /**
+     * source_attribute
+     */
+    @ApiModelProperty(value = "source_attribute")
+    private String sourceAttribute;
+
+//    /**
+//     * source_type锛屾簮灞炴�х被鍨嬶紝Integer銆丼tring銆丏ouble銆丣SONObject銆丣SONArray
+//     */
+//    @ApiModelProperty(value = "source_type")
+//    private String sourceType;
+
+    /**
+     * target_attribute
+     */
+    @ApiModelProperty(value = "target_attribute")
+    private String targetAttribute;
+
+//    /**
+//     * target_type锛岀洰鏍囧睘鎬х被鍨嬶紝Integer銆丼tring銆丏ouble銆丣SONObject銆丣SONArray
+//     */
+//    @ApiModelProperty(value = "target_type")
+//    private String targetType;
+
+    /**
+     * 鏄惁鍚敤锛�0 鏈惎鐢紱1 鍚敤锛�
+     */
+    @ApiModelProperty(value = "鏄惁鍚敤锛�0 鏈惎鐢紱1 鍚敤锛�")
+    private Integer enable;
+
+    /**
+     * 绉熸埛id
+     */
+    @ApiModelProperty(value = "绉熸埛id")
+    private Long tenant_id;
+}
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..3f20cc0
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/app/App.java
@@ -0,0 +1,54 @@
+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.Serial;
+import java.io.Serializable;
+
+@Data
+@TableName("open_api_app")
+public class App implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * appId
+     */
+    @ApiModelProperty(value= "appId")
+    @TableId(value = "id")
+    private String id;
+
+    /**
+     * appScrect
+     */
+    @ApiModelProperty(value = "appScrect")
+    private String screct;
+
+    /**
+     * appName
+     */
+    @ApiModelProperty(value = "appName")
+    private String name;
+
+    /**
+     * appUrl
+     */
+    @ApiModelProperty(value = "appUrl")
+    private String url;
+
+    /**
+     * 鏄惁鍚敤锛�0 鏈惎鐢紱1 鍚敤锛�
+     */
+    @ApiModelProperty(value = "鏄惁鍚敤锛�0 鏈惎鐢紱1 鍚敤锛�")
+    private Integer enable;
+
+    /**
+     * 绉熸埛id
+     */
+    @ApiModelProperty(value = "绉熸埛id")
+    private Long tenant_id;
+}
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..d16dfc9 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
@@ -28,7 +28,7 @@
     /**
      * 鏃犳潈闄愰敊璇爜
      */
-    public static final int UNAUTHORIZED_CODE = 403;
+    public static final String UNAUTHORIZED_CODE = "403";
 
     /**
      * 鏃犳潈闄愭彁绀轰俊鎭�
@@ -38,7 +38,7 @@
     /**
      * 鏈璇侀敊璇爜
      */
-    public static final int UNAUTHENTICATED_CODE = 401;
+    public static final String UNAUTHENTICATED_CODE = "401";
 
     /**
      * 鏈璇佹彁绀轰俊鎭�
@@ -131,4 +131,12 @@
      */
     public static final Integer TASK_SORT_MIN_VALUE =  0;
 
+    // AppId璁よ瘉鐩稿叧甯搁噺
+    public static final String HEADER_APP_ID = "appId"; //X-App-Id
+    public static final String HEADER_APP_SECRET = "appSecret"; //X-App-Secret
+    public static final String HEADER_AUTHORIZATION = "authorization";  //Authorization
+    public static final String TOKEN_PREFIX = "Zoneyung";
+    public static final String REQUEST_ATTR_APP_ID = "appId"; //request_app_id
+    public static final String REQUEST_ATTR_USER_ID = "appSecret"; //request_user_id
+    public static final String REQUEST_ATTR_APP_INFO = "APP_INFO";
 }
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..6a9c23d 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
@@ -1,5 +1,6 @@
 package com.vincent.rsf.openApi.entity.dto;
 
+import com.alibaba.fastjson.JSONObject;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -25,4 +26,35 @@
     @ApiModelProperty("鍝嶅簲缁撴灉")
     private Object data;
 
+    /**
+     * 鎴愬姛鍝嶅簲
+     */
+    public static CommonResponse ok() {
+        CommonResponse response = new CommonResponse();
+        response.setCode(200);
+        response.setMsg("鎿嶄綔鎴愬姛");
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("result", "SUCCESS");
+        response.setData(jsonObject);
+        return response;
+    }
+
+    public static CommonResponse ok(Object data) {
+        CommonResponse response = new CommonResponse();
+        response.setCode(200);
+        response.setMsg("鎿嶄綔鎴愬姛");
+        response.setData(data);
+        return response;
+    }
+
+    /**
+     * 澶辫触鍝嶅簲
+     */
+    public static CommonResponse error(String msg) {
+        CommonResponse response = new CommonResponse();
+        response.setCode(500);
+        response.setMsg(msg);
+        return response;
+    }
+
 }
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/CheckOrder.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/CheckOrder.java
new file mode 100644
index 0000000..890e41a
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/CheckOrder.java
@@ -0,0 +1,35 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "CheckOrder", description = "鐩樼偣鍗�")
+public class CheckOrder {
+
+    // 鐩樼偣鍗曞彿锛屽敮涓�鏍囪瘑
+    @NotNull
+    @JsonProperty("orderNo")
+    private String orderNo;
+    // 鐩樼偣鍗曞悕绉�
+    @JsonProperty("orderName")
+    private String orderName;
+    // 纭鏃堕棿锛岀绾ф椂闂存埑
+    private Long checkTime;
+    // 鍗曟嵁绫诲瀷锛岄渶ERP纭畾涓婃姤鏃堕渶瑕佷娇鐢ㄥ摢涓被鍨�
+    private String orderTypeId;
+    // 璧勪骇缁勭粐
+    private String assetOrgId;
+    // 鐩樼偣鏂规锛岄渶ERP纭畾涓婃姤鏃朵娇鐢ㄥ摢涓柟妗�
+    private String inventSchemeId;
+    // 纭缁撴灉
+    private List<CheckResult> checkItems;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/CheckResult.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/CheckResult.java
new file mode 100644
index 0000000..9b964c4
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/CheckResult.java
@@ -0,0 +1,24 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "CheckResult", description = "鐩樼偣缁撴灉")
+public class CheckResult {
+
+    // 浠撳簱缂栫爜
+    private String wareHouseId;
+    // 鐗╂枡缂栫爜
+    private String matNr;
+    // 搴撳瓨鏁伴噺
+    private Double qty;
+    // 纭鍚庡簱瀛樻暟閲忥紝瀹為檯搴撳瓨浠MS鐩樼偣涓哄噯锛屽彧淇濆瓨涓嶅仛搴撳瓨鏁伴噺璋冩暣
+    private Double checkQty;
+    // 鏄惁瑙e喕锛�0 瑙e喕锛�1 涓嶈В鍐伙紱
+    private Integer defrost;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Customer.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Customer.java
new file mode 100644
index 0000000..ea7e8ee
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Customer.java
@@ -0,0 +1,39 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "Customer", description = "瀹㈡埛淇℃伅鍚屾")
+public class Customer {
+
+    // 瀹㈡埛缂栫爜锛屽敮涓�鏍囪瘑
+    @NotNull
+    @JsonProperty("customerId")
+    private String customerId;
+    // 瀹㈡埛鍚嶇О
+    @JsonProperty("customerName")
+    private String customerName;
+    // 瀹㈡埛鏄电О
+    private String customerNickName;
+    // 瀹㈡埛鍒嗙粍锛屽浗鍐咃紝鍥藉
+    private String customerGroup;
+    // 鑱旂郴浜�
+    private String contact;
+    // 鑱旂郴鐢佃瘽
+    private String telephone;
+    // 閭
+    private String email;
+    // 鍦板潃
+    private String address;
+    // 鎿嶄綔绫诲瀷锛�1 鏂板锛�2 淇敼锛�3绂佺敤锛�4 鍙嶇鐢紱
+    @JsonProperty("operateType")
+    private Integer operateType;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventoryDetails.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventoryDetails.java
new file mode 100644
index 0000000..f4acb24
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventoryDetails.java
@@ -0,0 +1,47 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "InventoryDetails", description = "搴撳瓨鏄庣粏")
+public class InventoryDetails {
+
+    // 搴撲綅缂栫爜
+    private String locId;
+    // 浠撳簱缂栫爜
+    private String wareHouseId;
+    // 浠撳簱鍚嶇О
+    private String wareHouseName;
+    // 鎵樼洏鐮侊紝濡傛灉涓�涓墭鐩樹笂澶囦簡2涓伐鍗曞彿锛屽垯鍒嗕负2鏉�
+    private String palletId;
+    // 鐗╂枡缂栫爜
+    private String matNr;
+    // 鐗╂枡鍚嶇О
+    private String makTx;
+    // 瑙勬牸
+    private String spec;
+    // 鏁伴噺锛屽皬鏁扮偣鏈�闀�6浣�
+    private Double anfme;
+    // 鍗曚綅
+    private String unit;
+    // 搴撳瓨鐘舵�侊紝寰匛RP琛ュ厖锛岀ず渚嬶細鍙敤锛涘喕缁擄紱
+    private String status;
+    // 缁戝畾鐨勮鍗曠被鍨嬶紝1 鍑哄簱鍗曪紱2 鍏ュ簱鍗曪紱3 澶囨枡鍗曪紱鏈粦瀹氭椂涓虹┖
+    private Integer orderType;
+    // 璁㈠崟鍙枫�佸鏂欏崟鍙凤紝鏈粦瀹氭椂涓虹┖
+    private String orderNo;
+    // 澶囨枡绫诲瀷锛屼负澶囨枡鍗曟椂鏈夛紝1 姝e父棰嗘枡锛�2 鐢熶骇琛ユ枡锛�
+    private Integer prepareType;
+    // 璁″垝璺熻釜鍙�
+    private String planNo;
+    // 鎵规鍙�
+    private String batch;
+    // 搴撳瓨缁勭粐
+    private String stockOrgId;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventoryQueryCondition.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventoryQueryCondition.java
new file mode 100644
index 0000000..e64ac5d
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventoryQueryCondition.java
@@ -0,0 +1,28 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "InventoryQueryCondition", description = "搴撳瓨鏌ヨ鏉′欢")
+public class InventoryQueryCondition {
+
+    // 浠撳簱缂栫爜
+    private String wareHouseId;
+    // 鐗╂枡缂栫爜
+    private String matNr;
+    // 鐗╂枡缁�
+    private String matGroup;
+    // 搴撲綅缂栫爜
+    private String locId;
+    // 璁㈠崟鍙�/宸ュ崟鍙�/MES宸ュ崟鍙�
+    private String orderNo;
+    // 鏁拌鍒掕窡韪彿
+    private String planNo;
+    // 鎵规鍙�
+    private String batch;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventorySummary.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventorySummary.java
new file mode 100644
index 0000000..7d51252
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/InventorySummary.java
@@ -0,0 +1,34 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "InventorySummary", description = "搴撳瓨姹囨��")
+public class InventorySummary {
+
+    // 浠撳簱缂栫爜
+    private String wareHouseId;
+    // 浠撳簱鍚嶇О
+    private String wareHouseName;
+    // 鐗╂枡缂栫爜
+    private String matNr;
+    // 鐗╂枡鍚嶇О
+    private String makTx;
+    // 瑙勬牸
+    private String spec;
+    // 鏁伴噺锛屽皬鏁扮偣鏈�闀�6浣�
+    private Double anfme;
+    // 鍗曚綅
+    private String unit;
+    // 搴撳瓨缁勭粐
+    private String stockOrgId;
+    // 鎵瑰彿
+    private String batch;
+    // 璁″垝璺熻釜鍙�
+    private String planNo;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/MatPreparationOrder.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/MatPreparationOrder.java
new file mode 100644
index 0000000..f99e288
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/MatPreparationOrder.java
@@ -0,0 +1,30 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "MatPreparationOrder", description = "澶囨枡鍗�")
+public class MatPreparationOrder {
+
+    // 澶囨枡鍗曞彿锛屽敮涓�鏍囪瘑
+    @NotNull
+    @JsonProperty("orderNo")
+    private String orderNo;
+    // 澶囨枡鍗曞垱寤烘椂闂达紝绉掔骇鏃堕棿鎴�
+    @JsonProperty("orderTime")
+    private Long orderTime;
+    // 澶囨枡绫诲瀷锛屼负澶囨枡鍗曟椂鏈夛紝1 姝e父棰嗘枡锛�2 鐢熶骇琛ユ枡锛�
+    @JsonProperty("prepareType")
+    private Integer prepareType;
+    // 澶囨枡璇︽儏
+    private List<MatPreparationOrderItem> orderItems;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/MatPreparationOrderItem.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/MatPreparationOrderItem.java
new file mode 100644
index 0000000..d2eeea6
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/MatPreparationOrderItem.java
@@ -0,0 +1,24 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "MatPreparationOrderItem", description = "澶囨枡鍗曟槑缁�")
+public class MatPreparationOrderItem {
+
+    // 鐗╂枡缂栫爜
+    @NotNull
+    @JsonProperty("matNr")
+    private String matNr;
+    // 澶囨枡鏁伴噺
+    @JsonProperty("anfme")
+    private Double anfme;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Material.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Material.java
new file mode 100644
index 0000000..bf93123
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Material.java
@@ -0,0 +1,55 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "Material", description = "鐗╂枡淇℃伅鍚屾")
+public class Material {
+
+    // 鐗╂枡缂栫爜锛屽敮涓�鏍囪瘑
+    @NotNull
+    @JsonProperty("matNr")
+    private String matNr;
+    // 鐗╂枡鍚嶇О
+    @JsonProperty("makTx")
+    private String makTx;
+    // 鐗╂枡鍒嗙粍缂栫爜
+    @JsonProperty("groupId")
+    private String groupId;
+    // 鐗╂枡鍒嗙粍
+    @JsonProperty("groupName")
+    private String groupName;
+    // 鍨嬪彿
+    private String model;
+    // 閲嶉噺
+    private Double weight;
+    // 棰滆壊
+    private String color;
+    // 灏哄
+    private String size;
+    // 瑙勬牸
+    private String spec;
+    // 鎻忚堪
+    private String describe;
+    // 鍗曚綅
+    private String unit;
+    // 鍩烘湰鍗曚綅锛岄摱搴ф湁鍙屽崟浣嶏紝濡傞暱搴﹀拰閲嶉噺锛屽彲鑳介渶瑕佽浆鎹�
+    private String baseUnit;
+    // 浣跨敤缁勭粐缂栫爜
+    private String useOrgId;
+    // 浣跨敤缁勭粐鍚嶇О
+    private String useOrgName;
+    // 鐗╂枡灞炴�э紝澶栬喘绛�
+    private String erpClsID;
+    // 鎿嶄綔绫诲瀷锛�1 鏂板锛�2 淇敼锛�3绂佺敤锛�4 鍙嶇鐢紱
+    @JsonProperty("operateType")
+    private Integer operateType;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Order.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Order.java
new file mode 100644
index 0000000..4c1c2c8
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Order.java
@@ -0,0 +1,78 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "Order", description = "鍏�/鍑哄簱閫氱煡鍗�")
+public class Order {
+
+    // 鍏�/鍑哄簱璁㈠崟鍙凤紝鍞竴鏍囪瘑
+    @NotNull
+    @JsonProperty("orderNo")
+    private String orderNo;
+    // 鍗曟嵁鍐呯爜锛屽敮涓�鏍囪瘑
+    @JsonProperty("orderInternalCode")
+    private String orderInternalCode;
+    // 涓氬姟绫诲瀷锛岀ず渚嬶細
+    // 鍏ュ簱锛氭敹鏂欓�氱煡鍗曪紙PUR_ReceiveBill锛夈�侀噰璐叆搴撳崟锛圫TK_InStock锛夈�侀��鏂欑敵璇峰崟锛圥UR_MRAPP锛夈�侀噰璐��鏂欏崟锛圥UR_MRB锛夈��
+    //   閫�璐ч�氱煡鍗曪紙SAL_RETURNNOTICE锛夈�侀攢鍞��璐у崟锛圫AL_RETURNSTOCK锛夈�佺敓浜ч��鏂欏崟锛圥RD_ReturnMtrl锛夈�佺敓浜у叆搴撳崟锛圥RD_INSTOCK锛�/鐢熶骇姹囨姤鍗曪紙PRD_MORPT锛夈��
+    //   鍏朵粬鍏ュ簱鍗曪紙STK_MISCELLANEOUS锛�
+    // 鍑哄簱锛氬彂璐ч�氱煡鍗曪紙SAL_DELIVERYNOTICE锛夈�侀攢鍞嚭搴撳崟锛圫AL_OUTSTOCK锛夈�佸嚭搴撶敵璇峰崟锛圫TK_OutStockApply锛夈�佺敓浜ч鏂欏崟锛圥RD_PickMtrl锛夈��
+    //   鐢熶骇琛ユ枡鍗曪紙PRD_FeedMtrl锛夈�佸叾浠栧嚭搴撳崟锛圫TK_MisDelivery锛�
+    // 璋冩嫧锛氳皟鎷ㄧ敵璇峰崟锛圫TK_TRANSFERAPPLY锛夈�佺洿鎺ヨ皟鎷ㄥ崟锛圫TK_TransferDirect锛�
+    private String wkType;
+    // 璁㈠崟绫诲瀷锛�1 鍑哄簱鍗曪紱2 鍏ュ簱鍗曪紱3 璋冩嫧鍗曪紱
+    private String type;
+    // 鍒涘缓鏃ユ湡锛屾椂闂存埑锛岀簿纭埌绉�
+    private Long createTime;
+    // 涓氬姟鏃ユ湡锛屽璐︿娇鐢紝鏃堕棿鎴筹紝绮剧‘鍒扮
+    private Long businessTime;
+    // 搴撳瓨鏂瑰悜
+    private String stockDirect;
+    // 璁㈠崟鏄庣粏
+    private List<OrderItem> orderItems;
+    // 鍑哄叆搴撴帴椹崇珯鐐癸紝鍑哄簱鏃跺皢鐗╂枡鍑哄簱鍚庤繍杈撹嚦璇ョ珯鐐癸紝鍏ュ簱鏃朵粠璇ョ珯鐐瑰皢鐗╂枡杩愬洖搴撲腑
+    private String stationId;
+
+    // 瀹㈡埛缂栫爜
+    private String customerId;
+    // 瀹㈡埛鍚嶇О
+    private String customerName;
+    // 渚涘簲鍟嗙紪鐮�
+    private String supplierId;
+    // 渚涘簲鍟嗗悕绉�
+    private String supplierName;
+    // 鏀舵枡/鍙戣揣缁勭粐
+    private String stockOrgId;
+    // 鏀舵枡/鍙戣揣缁勭粐鍚嶇О
+    private String stockOrgName;
+    // 閲囪喘缁勭粐
+    private String purchaseOrgId;
+    // 閲囪喘缁勭粐鍚嶇О
+    private String purchaseOrgName;
+    // 閲囪喘鍛�
+    private String purchaseUserId;
+    // 閲囪喘鍛樺悕绉�
+    private String purchaseUserName;
+    // 鐢熶骇缁勭粐
+    private String prdOrgId;
+    // 鐢熶骇缁勭粐鍚嶇О
+    private String prdOrgName;
+    // 閿�鍞粍缁�
+    private String saleOrgId;
+    // 閿�鍞粍缁囧悕绉�
+    private String saleOrgName;
+    // 閿�鍞憳
+    private String saleUserId;
+    // 閿�鍞憳鍚嶇О
+    private String saleUserName;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/OrderItem.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/OrderItem.java
new file mode 100644
index 0000000..474d5ec
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/OrderItem.java
@@ -0,0 +1,65 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "OrderItem", description = "鍏�/鍑哄簱閫氱煡鍗曟槑缁�")
+public class OrderItem {
+
+    // 璁″垝璺熻釜鍙�
+    @NotNull
+    @JsonProperty("planNo")
+    private String planNo;
+    // 琛屽唴鐮侊紝鍞竴鏍囪瘑
+    @JsonProperty("lineId")
+    private String lineId;
+    // 鐗╂枡缂栫爜锛屽敮涓�鏍囪瘑
+    @NotNull
+    @JsonProperty("matNr")
+    private String matNr;
+    // 鐗╂枡鍚嶇О
+    @JsonProperty("makTx")
+    private String makTx;
+    // 瑙勬牸
+    private String spec;
+    // 鍨嬪彿
+    private String model;
+    // 鏁伴噺
+    private Double anfme;
+    // 鎵瑰彿
+    private String batch;
+    // 鍗曚綅
+    private String unit;
+    // 鍩烘湰鍗曚綅
+    private String baseUnitId;
+    // 璁′环鍗曚綅
+    private String priceUnitId;
+    // 寤鸿鐩爣浠撳簱
+    private String palletId;
+    // 璋冨嚭浠�
+    private String targetWareHouseId;
+    // 涓氬姟鏃ユ湡锛屽璐︿娇鐢紝鏃堕棿鎴筹紝绮剧‘鍒扮
+    private String sourceWareHouseId;
+    // 鍏ュ簱绫诲瀷
+    private String inStockType;
+    // 璐т富绫诲瀷
+    private String ownerTypeId;
+    // 璐т富
+    private String ownerId;
+    // 璐т富鍚嶇О
+    private String ownerName;
+    // 淇濈鑰呯被鍨�
+    private String keeperTypeId;
+    // 淇濈鑰�
+    private String keeperId;
+    // 淇濈鑰呭悕绉�
+    private String keeperName;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Pallet.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Pallet.java
new file mode 100644
index 0000000..784146f
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Pallet.java
@@ -0,0 +1,36 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "Pallet", description = "鎵樼洏淇℃伅")
+public class Pallet {
+
+    // 鎵樼洏鏉$爜
+    @NotNull
+    @JsonProperty("BarCode")
+    private String barCode;
+    // 鎵樼洏缂栫爜
+    @JsonProperty("PalletCode")
+    private String palletCode;
+    // 鎵樼洏鍚嶇О
+    @JsonProperty("PalletName")
+    private String palletName;
+    // 鎵樼洏绫诲瀷缂栫爜
+    @JsonProperty("PalletTypeCode")
+    private String palletTypeCode;
+    // 鎵樼洏绫诲瀷
+    @JsonProperty("PalletTypeName")
+    private String palletTypeName;
+    // 鍒涘缓浜�
+    @JsonProperty("CreatedBy")
+    private String createdBy;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/SimpleProductionTask.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/SimpleProductionTask.java
new file mode 100644
index 0000000..93fd25e
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/SimpleProductionTask.java
@@ -0,0 +1,14 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SimpleProductionTask extends Task {
+
+    // 棰嗘枡鍐呭
+    private String matText;
+    // 棰嗘枡鍖哄煙锛岀嚎涓嬪垱寤轰簬MES鍚屾
+    private String regionId;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Station.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Station.java
new file mode 100644
index 0000000..bd23ac7
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Station.java
@@ -0,0 +1,39 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "Station", description = "绔欑偣淇℃伅")
+public class Station {
+
+    // 鎺ラ┏鍙g紪鐮�
+    @NotNull
+    @JsonProperty("ConnPortCode")
+    private String connPortCode;
+    // 鎺ラ┏鍙e悕绉�
+    @JsonProperty("ConnPortName")
+    private String connPortName;
+    // 杞﹂棿缂栫爜
+    @JsonProperty("WorkshopCode")
+    private String workshopCode;
+    // 杞﹂棿
+    @JsonProperty("WorkshopName")
+    private String workshopName;
+    // 浠撳簱缂栫爜
+    @JsonProperty("ProductionLineCode")
+    private String productionLineCode;
+    // 浠撳簱
+    @JsonProperty("ProductionLineName")
+    private String productionLineName;
+    // 鍒涘缓浜�
+    @JsonProperty("CreatedBy")
+    private String createdBy;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Supplier.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Supplier.java
new file mode 100644
index 0000000..8692c00
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Supplier.java
@@ -0,0 +1,39 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "Supplier", description = "渚涘簲鍟嗕俊鎭悓姝�")
+public class Supplier {
+
+    // 渚涘簲鍟嗙紪鐮侊紝鍞竴鏍囪瘑
+    @NotNull
+    @JsonProperty("supplierId")
+    private String supplierId;
+    // 渚涘簲鍟嗗悕绉�
+    @JsonProperty("supplierName")
+    private String supplierName;
+    // 渚涘簲鍟嗘樀绉�
+    private String supplierNickName;
+    // 渚涘簲鍟嗗垎缁勶紝鍥藉唴锛屽浗澶�
+    private String supplierGroup;
+    // 鑱旂郴浜�
+    private String contact;
+    // 鑱旂郴鐢佃瘽
+    private String telephone;
+    // 閭
+    private String email;
+    // 鍦板潃
+    private String address;
+    // 鎿嶄綔绫诲瀷锛�1 鏂板锛�2 淇敼锛�3绂佺敤锛�4 鍙嶇鐢紱
+    @JsonProperty("operateType")
+    private Integer operateType;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Task.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Task.java
new file mode 100644
index 0000000..89f65be
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Task.java
@@ -0,0 +1,36 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "Task", description = "宸ヤ綔浠诲姟")
+public class Task {
+
+    // 浠诲姟鍙凤紝鍞竴鏍囪瘑
+    @NotNull
+    @JsonProperty("taskNo")
+    private String taskNo;
+    // 浠诲姟鍚嶇О
+    @JsonProperty("taskName")
+    private String taskName;
+    // 浠诲姟绫诲瀷锛�1 鍏ュ簱锛�2 鍑哄簱锛�3 杞Щ锛�
+    private Integer taskType;
+    // 浠诲姟浼樺厛绾э紝鑼冨洿锛�1-100锛涙暟瀛楄秺澶э紝浼樺厛绾ц秺楂橈紝榛樿10锛�
+    private Integer initPriority;
+    // 璧峰绔欑偣缂栧彿
+    private String startStationId;
+    // 鐩爣绔欑偣缂栧彿
+    private String endStationId;
+    // 鎵樼洏缂栧彿
+    private String palletId;
+    // 鎵樼洏绫诲瀷锛屾灇涓剧被鍨嬶細1 鎵樼洏锛�2 鏂欐灦锛�3 鏂欑锛涚瓑鏋氫妇绫诲瀷寰呭悗缁鎺�
+    private String palletType;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/TaskResult.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/TaskResult.java
new file mode 100644
index 0000000..e7fdd67
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/TaskResult.java
@@ -0,0 +1,24 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "TaskResult", description = "宸ヤ綔浠诲姟缁撴灉")
+public class TaskResult {
+
+    // 浠诲姟鍙凤紝鍞竴鏍囪瘑
+    @NotNull
+    @JsonProperty("TaskNo")
+    private String taskNo;
+    // 鐘舵��
+    @JsonProperty("Status")
+    private String status;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Warehouse.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Warehouse.java
new file mode 100644
index 0000000..5d0cb68
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/entity/phyz/Warehouse.java
@@ -0,0 +1,33 @@
+package com.vincent.rsf.openApi.entity.phyz;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+@Accessors(chain = true)
+@ApiModel(value = "Warehouse", description = "浠撳簱淇℃伅鍚屾")
+public class Warehouse {
+
+    // 浠撳簱缂栫爜锛屽敮涓�鏍囪瘑
+    @NotNull
+    @JsonProperty("wareHouseId")
+    private String wareHouseId;
+    // 浠撳簱鍚嶇О
+    @JsonProperty("wareHouseName")
+    private String wareHouseName;
+    // 浠撳簱浣嶇疆
+    private String address;
+    // 浣跨敤缁勭粐缂栫爜
+    private String useOrgId;
+    // 浣跨敤缁勭粐鍚嶇О
+    private String useOrgName;
+    // 鎿嶄綔绫诲瀷锛�1 鏂板锛�2 淇敼锛�3绂佺敤锛�4 鍙嶇鐢紱
+    @JsonProperty("operateType")
+    private Integer operateType;
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/ApiForeignLogMapper.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/ApiForeignLogMapper.java
new file mode 100644
index 0000000..27484fc
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/ApiForeignLogMapper.java
@@ -0,0 +1,9 @@
+package com.vincent.rsf.openApi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.vincent.rsf.openApi.entity.app.ApiForeignLog;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface ApiForeignLogMapper extends BaseMapper<ApiForeignLog> {
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/ApiFunctionMapper.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/ApiFunctionMapper.java
new file mode 100644
index 0000000..26940d8
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/ApiFunctionMapper.java
@@ -0,0 +1,15 @@
+package com.vincent.rsf.openApi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.vincent.rsf.openApi.entity.app.ApiFunction;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * ApiFunction Mapper
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+@Mapper
+public interface ApiFunctionMapper extends BaseMapper<ApiFunction> {
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/ApiMapMapper.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/ApiMapMapper.java
new file mode 100644
index 0000000..6144577
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/ApiMapMapper.java
@@ -0,0 +1,15 @@
+package com.vincent.rsf.openApi.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.vincent.rsf.openApi.entity.app.ApiMap;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * ApiMap Mapper
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+@Mapper
+public interface ApiMapMapper extends BaseMapper<ApiMap> {
+}
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..9f0a60d
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/mapper/AppMapper.java
@@ -0,0 +1,15 @@
+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;
+
+/**
+ * App Mapper
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+@Mapper
+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..e8134e6
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/filter/AppIdAuthenticationFilter.java
@@ -0,0 +1,121 @@
+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.apache.tika.utils.StringUtils;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+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鍜孉ppSecret璁よ瘉杩囨护鍣�
+ * 
+ * 鐢ㄤ簬楠岃瘉璇锋眰澶翠腑鐨凙ppId鍜孉ppSecret
+ * 
+ * @author vincent
+ * @since 2026-01-05
+ */
+@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();
+        
+        // 妫�鏌ユ槸鍚︿负璁よ瘉璇锋眰锛堝鑾峰彇token锛�
+        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)) {
+                // Token鏃堕棿璁よ瘉鎴愬姛锛岃璇丄ppId鍜孉ppSecret
+                String tokenAppId = TokenUtils.getAppIdFromToken(token);
+                String tokenAppSecret = TokenUtils.getSecretFromToken(token);
+                if (StringUtils.isBlank(tokenAppId) || StringUtils.isBlank(tokenAppSecret)
+                        || !appAuthService.validateApp(tokenAppId, tokenAppSecret)) {
+                    log.warn("Token楠岃瘉澶辫触");
+                    sendErrorResponse(response, Integer.parseInt(Constants.UNAUTHENTICATED_CODE), "璁よ瘉澶辫触锛岃鎻愪緵鏈夋晥鐨凾oken");
+                    return;
+                } else {
+                    request.setAttribute(Constants.REQUEST_ATTR_APP_ID, tokenAppId);
+                }
+            } else {
+                log.warn("Token楠岃瘉澶辫触鎴栫己澶�");
+                sendErrorResponse(response, Integer.parseInt(Constants.UNAUTHENTICATED_CODE), "璁よ瘉澶辫触锛岃鎻愪緵鏈夋晥鐨凾oken");
+                return;
+            }
+        } else {
+            log.warn("缂哄皯Token璁よ瘉淇℃伅");
+            sendErrorResponse(response, Integer.parseInt(Constants.UNAUTHENTICATED_CODE), "璁よ瘉澶辫触锛岃鎻愪緵鏈夋晥鐨凾oken");
+            return;
+        }
+
+        filterChain.doFilter(request, response);
+    }
+
+    /**
+     * 鍙戦�侀敊璇搷搴�
+     * 
+     * @param response HTTP鍝嶅簲
+     * @param code 閿欒鐮�
+     * @param message 閿欒娑堟伅
+     * @throws IOException
+     */
+    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();
+    }
+
+    /**
+     * 妫�鏌ユ槸鍚︿负璁よ瘉璇锋眰锛堜笉闇�瑕佽璇佺殑璇锋眰锛�
+     * 
+     * @param requestURI 璇锋眰URI
+     * @return 鏄惁涓鸿璇佽姹�
+     */
+    private boolean isAuthRequest(String requestURI) {
+        return requestURI.contains("/getToken");
+//               || requestURI.contains("/auth/validate") ||
+//               requestURI.contains("/auth/login");
+    }
+
+    @Override
+    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
+        String requestURI = request.getRequestURI();
+        
+        // 涓嶈繃婊よ璇佺浉鍏宠姹傚拰鍏紑鎺ュ彛
+        return 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");
+    }
+
+}
\ No newline at end of file
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..68ba1f7
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/service/AppAuthService.java
@@ -0,0 +1,112 @@
+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.stereotype.Service;
+
+import javax.annotation.Resource;
+
+/**
+ * App璁よ瘉鏈嶅姟
+ * 
+ * 鎻愪緵AppId鍜孉ppSecret鐨勯獙璇佸姛鑳�
+ * 
+ * @author vincent
+ * @since 2026-01-05
+ */
+@Slf4j
+@Service
+public class AppAuthService {
+
+    @Resource
+    private AppService appService;
+
+    /**
+     * 楠岃瘉AppId鍜孉ppSecret
+     * 
+     * @param appId 搴旂敤ID
+     * @param appSecret 搴旂敤瀵嗛挜
+     * @return 楠岃瘉缁撴灉
+     */
+    public boolean validateApp(String appId, String appSecret) {
+        log.debug("楠岃瘉AppId: {}, AppSecret: ****", appId);
+        
+        if (appId == null || appSecret == null) {
+            log.warn("AppId鎴朅ppSecret涓虹┖");
+            return false;
+        }
+
+        try {
+            // 浠庢暟鎹簱鏌ヨ搴旂敤淇℃伅
+            App app = appService.getById(appId);
+            if (app == null) {
+                log.warn("鏈壘鍒板簲鐢�: {}", appId);
+                return false;
+            }
+
+            // 妫�鏌ュ簲鐢ㄦ槸鍚﹀惎鐢�
+            if (app.getEnable() != 1) {
+                log.warn("搴旂敤鏈惎鐢�: {}", appId);
+                return false;
+            }
+
+            // 楠岃瘉瀵嗛挜
+            boolean isValid = appSecret.equals(app.getScrect());
+            if (!isValid) {
+                log.warn("AppSecret楠岃瘉澶辫触: AppId={}", appId);
+            } else {
+                log.info("AppId璁よ瘉鎴愬姛: AppId={}", appId);
+            }
+
+            return isValid;
+        } catch (Exception e) {
+            log.error("楠岃瘉AppId鍜孉ppSecret鏃跺彂鐢熷紓甯�", e);
+            return false;
+        }
+    }
+
+    public boolean validateApp(String appId) {
+        App app = appService.getById(appId);
+        return app != null;
+    }
+
+    /**
+     * 鑾峰彇搴旂敤淇℃伅
+     * 
+     * @param appId 搴旂敤ID
+     * @return 搴旂敤淇℃伅
+     */
+    public App getAppInfo(String appId) {
+        if (appId == null) {
+            return null;
+        }
+        
+        try {
+            return appService.getById(appId);
+        } catch (Exception e) {
+            log.error("鑾峰彇搴旂敤淇℃伅澶辫触: {}", appId, e);
+            return null;
+        }
+    }
+
+    /**
+     * 鐢熸垚AppToken锛堝彲閫夊姛鑳斤級
+     * 
+     * @param appId 搴旂敤ID
+     * @param appSecret 搴旂敤瀵嗛挜
+     * @return 鐢熸垚鐨凾oken
+     */
+    public String generateAppToken(String appId, String appSecret) {
+        // 杩欓噷鍙互瀹炵幇鍩轰簬AppId鍜孉ppSecret鐨凾oken鐢熸垚閫昏緫
+        // 渚嬪浣跨敤JWT鐢熸垚Token
+        if (validateApp(appId, appSecret)) {
+            // 鐢熸垚Token鐨勯�昏緫
+            long timestamp = System.currentTimeMillis();
+            String tokenData = appId + ":" + timestamp;
+            // TODO锛氳繖閲屽彲浠ヤ娇鐢ㄦ洿瀹夊叏鐨勫姞瀵嗙畻娉�
+            return java.util.Base64.getEncoder().encodeToString(tokenData.getBytes());
+        }
+        return null;
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/utils/AuthUtils.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/utils/AuthUtils.java
new file mode 100644
index 0000000..b946476
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/utils/AuthUtils.java
@@ -0,0 +1,54 @@
+package com.vincent.rsf.openApi.security.utils;
+
+import com.vincent.rsf.openApi.entity.constant.Constants;
+import com.vincent.rsf.openApi.entity.app.App;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 璁よ瘉宸ュ叿绫�
+ * 
+ * 鎻愪緵璁よ瘉鐩稿叧鐨勯�氱敤鍔熻兘
+ * 
+ * @author vincent
+ * @since 2026-01-05
+ */
+public class AuthUtils {
+
+    /**
+     * 浠庤姹備腑鑾峰彇AppId
+     * 
+     * @param request HTTP璇锋眰
+     * @return AppId
+     */
+    public static String getAppId(HttpServletRequest request) {
+        // 浼樺厛浠庤姹傚睘鎬т腑鑾峰彇锛堣璇佽繃婊ゅ櫒璁剧疆鐨勶級
+        String appId = (String) request.getAttribute(Constants.REQUEST_ATTR_APP_ID);
+        if (appId != null) {
+            return appId;
+        }
+        
+        // 浠庤姹傚ご鑾峰彇
+        return request.getHeader(Constants.HEADER_APP_ID);
+    }
+
+    /**
+     * 浠庤姹備腑鑾峰彇App淇℃伅
+     * 
+     * @param request HTTP璇锋眰
+     * @return App淇℃伅
+     */
+    public static App getAppInfo(HttpServletRequest request) {
+        return (App) request.getAttribute(Constants.REQUEST_ATTR_APP_INFO);
+    }
+
+    /**
+     * 妫�鏌ヨ姹傛槸鍚﹀凡閫氳繃App璁よ瘉
+     * 
+     * @param request HTTP璇锋眰
+     * @return 鏄惁宸茶璇�
+     */
+    public static boolean isAuthenticated(HttpServletRequest request) {
+        return getAppId(request) != 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..cbadad0
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/security/utils/TokenUtils.java
@@ -0,0 +1,161 @@
+package com.vincent.rsf.openApi.security.utils;
+
+import com.vincent.rsf.openApi.entity.app.App;
+import com.vincent.rsf.openApi.entity.constant.Constants;
+import com.vincent.rsf.openApi.service.AppService;
+import io.jsonwebtoken.*;
+import io.jsonwebtoken.security.Keys;
+import org.apache.tika.utils.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Resource;
+import javax.crypto.SecretKey;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * JWT Token宸ュ叿绫�
+ * 鐢ㄤ簬鐢熸垚鍜岄獙璇丣WT Token
+ */
+public class TokenUtils {
+    private static final Logger log = LoggerFactory.getLogger(TokenUtils.class);
+    
+    // 浣跨敤涓�涓畨鍏ㄧ殑瀵嗛挜锛屽疄闄呭簲鐢ㄤ腑搴旇浠庨厤缃枃浠惰鍙�
+    private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
+    
+    // Token杩囨湡鏃堕棿锛岄粯璁�1灏忔椂
+    private static final long TOKEN_EXPIRATION = 60 * 60 * 1000L; // 24灏忔椂
+
+    @Resource
+    private AppService appService;
+
+    /**
+     * 鐢熸垚JWT Token
+     * 
+     * @param claims Token涓寘鍚殑澹版槑淇℃伅
+     * @return 鐢熸垚鐨凾oken瀛楃涓�
+     */
+    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();
+    }
+
+    /**
+     * 鐢熸垚甯ppId鐨凾oken
+     * 
+     * @param appId 搴旂敤ID
+     * @param appSecret 搴旂敤绉橀挜
+     * @return 鐢熸垚鐨凾oken瀛楃涓�
+     */
+    public static String generateToken(String appId, String appSecret) {
+        Map<String, Object> claims = Map.of(
+            "appId", appId,
+            "appSecret", appSecret,
+            "created", System.currentTimeMillis()
+        );
+        return generateToken(claims);
+    }
+
+    /**
+     * 瑙f瀽Token鑾峰彇澹版槑淇℃伅
+     * 
+     * @param token Token瀛楃涓�
+     * @return 澹版槑淇℃伅
+     */
+    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;
+        }
+    }
+
+    /**
+     * 楠岃瘉Token鏃堕棿鏄惁鏈夋晥
+     * 
+     * @param token Token瀛楃涓�
+     * @return 鏃堕棿鏄惁鏈夋晥
+     */
+    public static boolean validateTokenTime(String token) {
+        try {
+            Claims claims = parseToken(token);
+            if (claims == null) {
+                return false;
+            }
+            
+            // 妫�鏌oken鏄惁杩囨湡
+            Date expiration = claims.getExpiration();
+            return expiration != null && expiration.after(new Date());
+        } catch (JwtException e) {
+            log.error("楠岃瘉Token澶辫触: {}", e.getMessage());
+            return false;
+        }
+    }
+
+    /**
+     * 浠嶵oken涓幏鍙朅ppId
+     * 
+     * @param token Token瀛楃涓�
+     * @return AppId
+     */
+    public static String getAppIdFromToken(String token) {
+        Claims claims = parseToken(token);
+        if (claims != null) {
+            return (String) claims.get("appId");
+        }
+        return null;
+    }
+
+    /**
+     * 浠嶵oken涓幏鍙朼ppSecret
+     *
+     * @param token Token瀛楃涓�
+     * @return appSecret
+     */
+    public static String getSecretFromToken(String token) {
+        Claims claims = parseToken(token);
+        if (claims != null) {
+            return (String) claims.get("appSecret");
+        }
+        return null;
+    }
+
+//    /**
+//     * 浠嶵oken涓幏鍙朥serId
+//     *
+//     * @param token Token瀛楃涓�
+//     * @return UserId
+//     */
+//    public static String getUserIdFromToken(String token) {
+//        Claims claims = parseToken(token);
+//        if (claims != null) {
+//            return (String) claims.get("userId");
+//        }
+//        return null;
+//    }
+
+    /**
+     * 浠嶢uthorization澶翠腑鎻愬彇Token
+     * 
+     * @param authHeader Authorization澶村唴瀹�
+     * @return Token瀛楃涓诧紙涓嶅寘鍚獴earer鍓嶇紑锛�
+     */
+    public static String extractTokenFromHeader(String authHeader) {
+        if (authHeader != null && authHeader.startsWith(Constants.TOKEN_PREFIX)) {
+            return authHeader.substring(Constants.TOKEN_PREFIX.length()).trim();
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/ApiForeignLogService.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/ApiForeignLogService.java
new file mode 100644
index 0000000..1ec345c
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/ApiForeignLogService.java
@@ -0,0 +1,9 @@
+package com.vincent.rsf.openApi.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.vincent.rsf.openApi.entity.app.ApiForeignLog;
+
+public interface ApiForeignLogService extends IService<ApiForeignLog> {
+
+    void saveAsync(ApiForeignLog log);
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/ApiFunctionService.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/ApiFunctionService.java
new file mode 100644
index 0000000..ad908af
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/ApiFunctionService.java
@@ -0,0 +1,18 @@
+package com.vincent.rsf.openApi.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.vincent.rsf.openApi.entity.app.ApiFunction;
+
+/**
+ * ApiFunction Service
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+public interface ApiFunctionService extends IService<ApiFunction> {
+    
+    /**
+     * 鍒锋柊鍔熻兘缂撳瓨
+     */
+    void refreshCache();
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/ApiMapService.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/ApiMapService.java
new file mode 100644
index 0000000..c5aa9a3
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/ApiMapService.java
@@ -0,0 +1,18 @@
+package com.vincent.rsf.openApi.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.vincent.rsf.openApi.entity.app.ApiMap;
+
+/**
+ * ApiMap Service
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+public interface ApiMapService extends IService<ApiMap> {
+    
+    /**
+     * 鍒锋柊鏄犲皠缂撳瓨
+     */
+    void refreshCache();
+}
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..8de31f1
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/AppService.java
@@ -0,0 +1,18 @@
+package com.vincent.rsf.openApi.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.vincent.rsf.openApi.entity.app.App;
+
+/**
+ * App Service
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+public interface AppService extends IService<App> {
+    
+    /**
+     * 鍒锋柊搴旂敤缂撳瓨
+     */
+    void refreshCache();
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/ApiForeignLogServiceImpl.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/ApiForeignLogServiceImpl.java
new file mode 100644
index 0000000..e315c3f
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/ApiForeignLogServiceImpl.java
@@ -0,0 +1,18 @@
+package com.vincent.rsf.openApi.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.vincent.rsf.openApi.entity.app.ApiForeignLog;
+import com.vincent.rsf.openApi.mapper.ApiForeignLogMapper;
+import com.vincent.rsf.openApi.service.ApiForeignLogService;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ApiForeignLogServiceImpl extends ServiceImpl<ApiForeignLogMapper, ApiForeignLog> implements ApiForeignLogService {
+
+    @Async
+    @Override
+    public void saveAsync(ApiForeignLog log) {
+        baseMapper.insert(log);
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/ApiFunctionServiceImpl.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/ApiFunctionServiceImpl.java
new file mode 100644
index 0000000..354a7cc
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/ApiFunctionServiceImpl.java
@@ -0,0 +1,30 @@
+package com.vincent.rsf.openApi.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.vincent.rsf.openApi.entity.app.ApiFunction;
+import com.vincent.rsf.openApi.mapper.ApiFunctionMapper;
+import com.vincent.rsf.openApi.service.ApiFunctionService;
+import com.vincent.rsf.openApi.utils.ParamsMapUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * ApiFunction Service Implementation
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+@Slf4j
+@Service
+public class ApiFunctionServiceImpl extends ServiceImpl<ApiFunctionMapper, ApiFunction> implements ApiFunctionService {
+
+    @Override
+    public void refreshCache() {
+        log.info("寮�濮嬪埛鏂版帴鍙e姛鑳界紦瀛�...");
+        List<ApiFunction> functions = this.list();
+        ParamsMapUtils.FUNCTIONS = functions;
+        log.info("鎺ュ彛鍔熻兘缂撳瓨鍒锋柊瀹屾垚锛屽叡鍔犺浇 {} 涓姛鑳�", functions.size());
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/ApiMapServiceImpl.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/ApiMapServiceImpl.java
new file mode 100644
index 0000000..916987e
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/ApiMapServiceImpl.java
@@ -0,0 +1,32 @@
+package com.vincent.rsf.openApi.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.vincent.rsf.openApi.entity.app.ApiMap;
+import com.vincent.rsf.openApi.mapper.ApiMapMapper;
+import com.vincent.rsf.openApi.service.ApiMapService;
+import com.vincent.rsf.openApi.utils.ParamsMapUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * ApiMap Service Implementation
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+@Slf4j
+@Service
+public class ApiMapServiceImpl extends ServiceImpl<ApiMapMapper, ApiMap> implements ApiMapService {
+
+    @Override
+    public void refreshCache() {
+        log.info("寮�濮嬪埛鏂板瓧娈垫槧灏勭紦瀛�...");
+        List<ApiMap> maps = this.list(new LambdaQueryWrapper<ApiMap>()
+                .eq(ApiMap::getEnable, 1));
+        ParamsMapUtils.ATTRIBUTE_MAPS = maps;
+        log.info("瀛楁鏄犲皠缂撳瓨鍒锋柊瀹屾垚锛屽叡鍔犺浇 {} 鏉℃槧灏勮鍒�", maps.size());
+    }
+}
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..a44d708
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/service/impl/AppServiceImpl.java
@@ -0,0 +1,32 @@
+package com.vincent.rsf.openApi.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+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 com.vincent.rsf.openApi.utils.ParamsMapUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * App Service Implementation
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+@Slf4j
+@Service
+public class AppServiceImpl extends ServiceImpl<AppMapper, App> implements AppService {
+
+    @Override
+    public void refreshCache() {
+        log.info("寮�濮嬪埛鏂板簲鐢ㄧ紦瀛�...");
+        List<App> apps = this.list(new LambdaQueryWrapper<App>()
+                .eq(App::getEnable, 1));
+        ParamsMapUtils.APPS = apps;
+        log.info("搴旂敤缂撳瓨鍒锋柊瀹屾垚锛屽叡鍔犺浇 {} 涓簲鐢�", apps.size());
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/utils/JsonReplaceTest.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/utils/JsonReplaceTest.java
new file mode 100644
index 0000000..31db512
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/utils/JsonReplaceTest.java
@@ -0,0 +1,192 @@
+package com.vincent.rsf.openApi.utils;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * JSON灞炴�у悕閫掑綊鏇挎崲娴嬭瘯绫�
+ * 
+ * 婕旂ず濡備綍浣跨敤 FuncMap 杩涜JSON灞炴�у悕鐨勯�掑綊鏇挎崲
+ * 
+ * @author vincent
+ * @since 2026-01-04
+ */
+public class JsonReplaceTest {
+
+    public static void main(String[] args) {
+        System.out.println("========== JSON灞炴�у悕閫掑綊鏇挎崲娴嬭瘯 ==========\n");
+        
+        // 娴嬭瘯1锛氱畝鍗曞璞�
+        testSimpleObject();
+        
+        // 娴嬭瘯2锛氬祵濂楀璞�
+        testNestedObject();
+        
+        // 娴嬭瘯3锛氬寘鍚暟缁�
+        testWithArray();
+        
+        // 娴嬭瘯4锛氬鏉傜粨鏋�
+        testComplexStructure();
+    }
+
+    /**
+     * 娴嬭瘯1锛氱畝鍗曞璞″睘鎬ф浛鎹�
+     */
+    private static void testSimpleObject() {
+        System.out.println("銆愭祴璇�1锛氱畝鍗曞璞°��");
+        
+        // 鏋勯�犳祴璇曟暟鎹�
+        JSONObject data = new JSONObject();
+        data.put("orderNumber", "PO001");
+        data.put("orderQty", 100);
+        data.put("orderAmount", 5000.00);
+        
+        System.out.println("鍘熷鏁版嵁锛�" + data.toJSONString());
+        
+        // 瀹氫箟鏄犲皠瑙勫垯
+        Map<String, String> rules = new HashMap<>();
+        rules.put("orderNumber", "code");
+        rules.put("orderQty", "qty");
+        rules.put("orderAmount", "anfme");
+        
+        // 鎵ц鏇挎崲
+        JSONObject result = ParamsMapUtils.replaceJsonKeys(data, rules);
+        
+        System.out.println("鏇挎崲缁撴灉锛�" + result.toJSONString());
+        System.out.println();
+    }
+
+    /**
+     * 娴嬭瘯2锛氬祵濂楀璞℃繁搴︽浛鎹�
+     */
+    private static void testNestedObject() {
+        System.out.println("銆愭祴璇�2锛氬祵濂楀璞°��");
+        
+        // 鏋勯�犲祵濂楁暟鎹�
+        JSONObject data = new JSONObject();
+        data.put("orderNumber", "PO002");
+        
+        JSONObject customer = new JSONObject();
+        customer.put("customerName", "寮犱笁");
+        customer.put("customerPhone", "13800138000");
+        
+        JSONObject address = new JSONObject();
+        address.put("cityName", "鍖椾含");
+        address.put("streetName", "鏈濋槼璺�88鍙�");
+        customer.put("address", address);
+        
+        data.put("customer", customer);
+        
+        System.out.println("鍘熷鏁版嵁锛�" + data.toJSONString());
+        
+        // 瀹氫箟鏄犲皠瑙勫垯锛堜細搴旂敤鍒版墍鏈夊眰绾э級
+        Map<String, String> rules = new HashMap<>();
+        rules.put("orderNumber", "code");
+        rules.put("customerName", "name");
+        rules.put("customerPhone", "phone");
+        rules.put("cityName", "city");
+        rules.put("streetName", "street");
+        
+        // 鎵ц閫掑綊鏇挎崲
+        JSONObject result = ParamsMapUtils.replaceJsonKeys(data, rules);
+        
+        System.out.println("鏇挎崲缁撴灉锛�" + result.toJSONString());
+        System.out.println();
+    }
+
+    /**
+     * 娴嬭瘯3锛氬寘鍚暟缁勭殑鏇挎崲
+     */
+    private static void testWithArray() {
+        System.out.println("銆愭祴璇�3锛氬寘鍚暟缁勩��");
+        
+        // 鏋勯�犲寘鍚暟缁勭殑鏁版嵁
+        JSONObject data = new JSONObject();
+        data.put("orderNumber", "PO003");
+        
+        JSONArray items = new JSONArray();
+        for (int i = 1; i <= 3; i++) {
+            JSONObject item = new JSONObject();
+            item.put("materialCode", "MAT00" + i);
+            item.put("materialName", "鐗╂枡" + i);
+            item.put("itemQty", 10 * i);
+            items.add(item);
+        }
+        data.put("items", items);
+        
+        System.out.println("鍘熷鏁版嵁锛�" + data.toJSONString());
+        
+        // 瀹氫箟鏄犲皠瑙勫垯
+        Map<String, String> rules = new HashMap<>();
+        rules.put("orderNumber", "code");
+        rules.put("materialCode", "matnr");
+        rules.put("materialName", "maktx");
+        rules.put("itemQty", "qty");
+        
+        // 鎵ц鏇挎崲锛堝寘鎷暟缁勪腑鐨勬墍鏈夊璞★級
+        JSONObject result = ParamsMapUtils.replaceJsonKeys(data, rules);
+        
+        System.out.println("鏇挎崲缁撴灉锛�" + result.toJSONString());
+        System.out.println();
+    }
+
+    /**
+     * 娴嬭瘯4锛氬鏉傜粨鏋勶紙宓屽+鏁扮粍+澶氬眰锛�
+     */
+    private static void testComplexStructure() {
+        System.out.println("銆愭祴璇�4锛氬鏉傜粨鏋勩��");
+        
+        // 鏋勯�犲鏉傜粨鏋�
+        JSONObject data = new JSONObject();
+        data.put("orderNumber", "PO004");
+        data.put("orderStatus", "PENDING");
+        
+        // 瀹㈡埛淇℃伅
+        JSONObject customer = new JSONObject();
+        customer.put("customerCode", "CUST001");
+        customer.put("customerName", "鍖椾含鍏徃");
+        data.put("customer", customer);
+        
+        // 璁㈠崟鏄庣粏鏁扮粍
+        JSONArray items = new JSONArray();
+        for (int i = 1; i <= 2; i++) {
+            JSONObject item = new JSONObject();
+            item.put("itemNo", i);
+            item.put("materialCode", "MAT00" + i);
+            item.put("materialName", "鐗╂枡" + i);
+            item.put("itemQty", 50 + i * 10);
+            
+            // 浠撳簱淇℃伅锛堝祵濂楀湪鏄庣粏涓級
+            JSONObject warehouse = new JSONObject();
+            warehouse.put("warehouseCode", "WH0" + i);
+            warehouse.put("locationCode", "LOC-A-0" + i);
+            item.put("warehouse", warehouse);
+            
+            items.add(item);
+        }
+        data.put("items", items);
+        
+        System.out.println("鍘熷鏁版嵁锛�" + data.toJSONString());
+        
+        // 瀹氫箟瀹屾暣鐨勬槧灏勮鍒�
+        Map<String, String> rules = new HashMap<>();
+        rules.put("orderNumber", "code");
+        rules.put("orderStatus", "exceStatus");
+        rules.put("customerCode", "custCode");
+        rules.put("customerName", "custName");
+        rules.put("materialCode", "matnr");
+        rules.put("materialName", "maktx");
+        rules.put("itemQty", "qty");
+        rules.put("warehouseCode", "whCode");
+        rules.put("locationCode", "locCode");
+        
+        // 鎵ц閫掑綊鏇挎崲
+        JSONObject result = ParamsMapUtils.replaceJsonKeys(data, rules);
+        
+        System.out.println("鏇挎崲缁撴灉锛�" + result.toJSONString());
+        System.out.println();
+    }
+}
diff --git a/rsf-open-api/src/main/java/com/vincent/rsf/openApi/utils/ParamsMapUtils.java b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/utils/ParamsMapUtils.java
new file mode 100644
index 0000000..302e515
--- /dev/null
+++ b/rsf-open-api/src/main/java/com/vincent/rsf/openApi/utils/ParamsMapUtils.java
@@ -0,0 +1,273 @@
+package com.vincent.rsf.openApi.utils;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.vincent.rsf.openApi.entity.app.ApiFunction;
+import com.vincent.rsf.openApi.entity.app.ApiMap;
+import com.vincent.rsf.openApi.entity.app.App;
+import com.vincent.rsf.openApi.service.ApiMapService;
+import com.vincent.rsf.openApi.service.ApiFunctionService;
+import com.vincent.rsf.openApi.service.AppService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.tika.utils.StringUtils;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鎺ュ彛瀛楁鍔ㄦ�佹槧灏勫伐鍏风被
+ * 鏀寔浠庢暟鎹簱鍔犺浇鏄犲皠閰嶇疆锛屽苟鎻愪緵瀛楁杞崲鍔熻兘
+ *
+ * @author vincent
+ * @since 2026-01-04
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class ParamsMapUtils {
+
+    private final AppService appService;
+    private final ApiFunctionService functionService;
+    private final ApiMapService mapService;
+
+    // 缂撳瓨鍒板唴瀛�
+    public static List<App> APPS = new ArrayList<>();
+    public static List<ApiFunction> FUNCTIONS = new ArrayList<>();
+    public static List<ApiMap> ATTRIBUTE_MAPS = new ArrayList<>();
+
+    /**
+     * 搴旂敤瀹屽叏鍚姩鍚庤嚜鍔ㄥ姞杞介厤缃�
+     * 浣跨敤 ApplicationReadyEvent 纭繚 Spring 瀹瑰櫒瀹屽叏鍒濆鍖�
+     */
+    @EventListener(ApplicationReadyEvent.class)
+    public void init() {
+        log.info("=============== 寮�濮嬪姞杞芥帴鍙f槧灏勯厤缃� ===============");
+        try {
+            appService.refreshCache();
+            functionService.refreshCache();
+            mapService.refreshCache();
+
+            log.info("鎺ュ彛鏄犲皠閰嶇疆鍔犺浇瀹屾垚 - 搴旂敤鏁�:{}, 鍔熻兘鏁�:{}, 鏄犲皠瑙勫垯鏁�:{}",
+                    APPS.size(), FUNCTIONS.size(), ATTRIBUTE_MAPS.size());
+        } catch (Exception e) {
+            log.error("鎺ュ彛鏄犲皠閰嶇疆鍔犺浇澶辫触", e);
+        }
+    }
+
+    /**
+     * 鎵嬪姩鍒锋柊鎵�鏈夌紦瀛�
+     */
+    public void refreshAll() {
+        log.info("鎵嬪姩鍒锋柊鎵�鏈夋槧灏勭紦瀛�...");
+        appService.refreshCache();
+        functionService.refreshCache();
+        mapService.refreshCache();
+    }
+
+    /**
+     * 鎵ц瀛楁鏄犲皠杞崲
+     *
+     * @param appId 搴旂敤ID
+     * @param funcId 鍔熻兘ID
+     * @param params 鍘熷鍙傛暟
+     * @return 杞崲鍚庣殑鍙傛暟
+     */
+    public static JSONObject apiMaps(String appId, String funcId, JSONObject params) {
+        if (params == null || params.isEmpty()) {
+            return params;
+        }
+
+        // 1銆佽幏鍙栨槧灏勮〃
+        List<ApiMap> maps = ATTRIBUTE_MAPS.stream()
+                .filter(map -> map.getAppId().equals(appId) && map.getFuncId().equals(funcId))
+                .toList();
+
+        if (maps.isEmpty()) {
+            log.debug("鏈壘鍒版槧灏勯厤缃� - appId:{}, funcId:{}", appId, funcId);
+            return params;
+        }
+
+        // 2銆佹瀯寤烘槧灏勮鍒橫ap
+        Map<String, String> mappingRules = new HashMap<>();
+        for (ApiMap map : maps) {
+            String sourceAttribute = map.getSourceAttribute();
+            String targetAttribute = map.getTargetAttribute();
+            if (!StringUtils.isBlank(sourceAttribute) && !StringUtils.isBlank(targetAttribute)) {
+                mappingRules.put(sourceAttribute, targetAttribute);
+            }
+        }
+
+        // 3銆侀�掑綊鏇挎崲鎵�鏈夊眰绾х殑灞炴�у悕绉�
+        JSONObject result = replaceKeysRecursive(params, mappingRules);
+
+        return result;
+    }
+
+    /**
+     * 閫掑綊鏇挎崲JSON鎵�鏈夊眰绾х殑灞炴�у悕绉�
+     * 鏀寔宓屽瀵硅薄鍜屾暟缁勭殑娣卞害閬嶅巻
+     *
+     * @param json 鍘熷JSON瀵硅薄
+     * @param mappingRules 鏄犲皠瑙勫垯 (婧愬瓧娈靛悕 -> 鐩爣瀛楁鍚�)
+     * @return 鏇挎崲鍚庣殑JSON瀵硅薄
+     */
+    public static JSONObject replaceKeysRecursive(JSONObject json, Map<String, String> mappingRules) {
+        if (json == null || json.isEmpty()) {
+            return json;
+        }
+
+        JSONObject result = new JSONObject();
+
+        for (String key : json.keySet()) {
+            Object value = json.get(key);
+
+            // 纭畾鏂扮殑閿悕锛堝鏋滄湁鏄犲皠瑙勫垯鍒欎娇鐢ㄦ槧灏勫悗鐨勫悕绉帮級
+            String newKey = mappingRules.getOrDefault(key, key);
+
+            if (value instanceof JSONObject) {
+                // 閫掑綊澶勭悊宓屽瀵硅薄
+                JSONObject nestedResult = replaceKeysRecursive((JSONObject) value, mappingRules);
+                result.put(newKey, nestedResult);
+                log.debug("鏇挎崲瀵硅薄瀛楁: {} -> {}", key, newKey);
+
+            } else if (value instanceof JSONArray) {
+                // 閫掑綊澶勭悊鏁扮粍
+                JSONArray arrayResult = replaceKeysInArray((JSONArray) value, mappingRules);
+                result.put(newKey, arrayResult);
+                log.debug("鏇挎崲鏁扮粍瀛楁: {} -> {}", key, newKey);
+
+            } else {
+                // 鏅�氬�肩洿鎺ヨ祴鍊�
+                Object convertedValue = convertValue(value, newKey);
+                result.put(newKey, convertedValue);
+                if (!key.equals(newKey)) {
+                    log.debug("鏇挎崲瀛楁: {} -> {} (鍊�: {})", key, newKey, convertedValue);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 閫掑綊澶勭悊JSON鏁扮粍涓殑鎵�鏈夊厓绱�
+     *
+     * @param array 鍘熷JSON鏁扮粍
+     * @param mappingRules 鏄犲皠瑙勫垯
+     * @return 澶勭悊鍚庣殑JSON鏁扮粍
+     */
+    private static JSONArray replaceKeysInArray(JSONArray array, Map<String, String> mappingRules) {
+        if (array == null || array.isEmpty()) {
+            return array;
+        }
+
+        JSONArray result = new JSONArray();
+
+        for (int i = 0; i < array.size(); i++) {
+            Object element = array.get(i);
+
+            if (element instanceof JSONObject) {
+                // 鏁扮粍鍏冪礌鏄璞★紝閫掑綊澶勭悊
+                JSONObject replacedObject = replaceKeysRecursive((JSONObject) element, mappingRules);
+                result.add(replacedObject);
+
+            } else if (element instanceof JSONArray) {
+                // 鏁扮粍鍏冪礌鏄暟缁勶紝閫掑綊澶勭悊
+                JSONArray replacedArray = replaceKeysInArray((JSONArray) element, mappingRules);
+                result.add(replacedArray);
+
+            } else {
+                // 鍩烘湰绫诲瀷锛岀洿鎺ユ坊鍔�
+                result.add(element);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 閫氱敤鐨凧SON灞炴�у悕鏇挎崲鏂规硶锛堝澶栨彁渚涳級
+     * 鍙互鐩存帴浼犲叆鏄犲皠瑙勫垯杩涜鏇挎崲
+     *
+     * @param json 鍘熷JSON瀵硅薄
+     * @param mappingRules 鏄犲皠瑙勫垯 Map<婧愬瓧娈靛悕, 鐩爣瀛楁鍚�>
+     * @return 鏇挎崲鍚庣殑JSON瀵硅薄
+     */
+    public static JSONObject replaceJsonKeys(JSONObject json, Map<String, String> mappingRules) {
+        return replaceKeysRecursive(json, mappingRules);
+    }
+
+    /**
+     * 閫氱敤鐨凧SON鏁扮粍灞炴�у悕鏇挎崲鏂规硶锛堝澶栨彁渚涳級
+     *
+     * @param array 鍘熷JSON鏁扮粍
+     * @param mappingRules 鏄犲皠瑙勫垯 Map<婧愬瓧娈靛悕, 鐩爣瀛楁鍚�>
+     * @return 鏇挎崲鍚庣殑JSON鏁扮粍
+     */
+    public static JSONArray replaceJsonArrayKeys(JSONArray array, Map<String, String> mappingRules) {
+        return replaceKeysInArray(array, mappingRules);
+    }
+
+
+    /**
+     * 鍊肩被鍨嬭浆鎹紙鍙墿灞曪級
+     *
+     * @param value 鍘熷鍊�
+     * @param targetField 鐩爣瀛楁鍚�
+     * @return 杞崲鍚庣殑鍊�
+     */
+    private static Object convertValue(Object value, String targetField) {
+        if (value == null) {
+            return null;
+        }
+
+        // 杩欓噷鍙互鏍规嵁闇�瑕佹坊鍔犳洿澶氱被鍨嬭浆鎹㈤�昏緫
+        // 渚嬪锛氭棩鏈熸牸寮忚浆鎹€�佹暟瀛楃簿搴﹁浆鎹㈢瓑
+
+        // 绀轰緥锛氬鏋滃瓧娈靛悕鍖呭惈amount銆乸rice绛夛紝杞崲涓築igDecimal
+        String lowerField = targetField.toLowerCase();
+        if ((lowerField.contains("amount") || lowerField.contains("price")
+                || lowerField.contains("qty")) && value instanceof String) {
+            try {
+                return new BigDecimal(value.toString());
+            } catch (Exception e) {
+                log.warn("鏁板瓧杞崲澶辫触: {} -> {}", value, targetField);
+                return value;
+            }
+        }
+
+        return value;
+    }
+
+    /**
+     * 鑾峰彇搴旂敤淇℃伅
+     *
+     * @param appId 搴旂敤ID
+     * @return 搴旂敤淇℃伅
+     */
+    public static App getApp(String appId) {
+        return APPS.stream()
+                .filter(app -> app.getId().equals(appId))
+                .findFirst()
+                .orElse(null);
+    }
+
+    /**
+     * 鑾峰彇鍔熻兘淇℃伅
+     *
+     * @param funcId 鍔熻兘ID
+     * @return 鍔熻兘淇℃伅
+     */
+    public static ApiFunction getFunction(String funcId) {
+        return FUNCTIONS.stream()
+                .filter(func -> func.getId().equals(funcId))
+                .findFirst()
+                .orElse(null);
+    }
+}
diff --git a/rsf-open-api/src/main/resources/logback-spring.xml b/rsf-open-api/src/main/resources/logback-spring.xml
new file mode 100644
index 0000000..2f3a24d
--- /dev/null
+++ b/rsf-open-api/src/main/resources/logback-spring.xml
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 鏃ュ織绾у埆浠庝綆鍒伴珮鍒嗕负TRACE < DEBUG < INFO < WARN < ERROR < FATAL锛屽鏋滆缃负WARN锛屽垯浣庝簬WARN鐨勪俊鎭兘涓嶄細杈撳嚭 -->
+<!-- scan:褰撴灞炴�ц缃负true鏃讹紝閰嶇疆鏂囦欢濡傛灉鍙戠敓鏀瑰彉锛屽皢浼氳閲嶆柊鍔犺浇锛岄粯璁ゅ�间负true -->
+<!-- scanPeriod:璁剧疆鐩戞祴閰嶇疆鏂囦欢鏄惁鏈変慨鏀圭殑鏃堕棿闂撮殧锛屽鏋滄病鏈夌粰鍑烘椂闂村崟浣嶏紝榛樿鍗曚綅鏄绉掋�傚綋scan涓簍rue鏃讹紝姝ゅ睘鎬х敓鏁堛�傞粯璁ょ殑鏃堕棿闂撮殧涓�1鍒嗛挓銆� -->
+<!-- debug:褰撴灞炴�ц缃负true鏃讹紝灏嗘墦鍗板嚭logback鍐呴儴鏃ュ織淇℃伅锛屽疄鏃舵煡鐪媗ogback杩愯鐘舵�併�傞粯璁ゅ�间负false銆� -->
+<configuration scan="true" scanPeriod="10 seconds">
+
+    <!--<include resource="org/springframework/boot/logging/logback/base.xml" />-->
+
+    <contextName>logback</contextName>
+    <!-- name鐨勫�兼槸鍙橀噺鐨勫悕绉帮紝value鐨勫�兼椂鍙橀噺瀹氫箟鐨勫�笺�傞�氳繃瀹氫箟鐨勫�间細琚坊鍔犲埌logger涓婁笅鏂囦腑銆傚畾涔夊彉閲忓悗锛屽彲浠ヤ娇鈥�${}鈥濇潵浣跨敤鍙橀噺銆� -->
+    <!--    <property name="log.path" value="./emp-log"/>-->
+    <!-- 褰╄壊鏃ュ織 -->
+    <!-- 褰╄壊鏃ュ織渚濊禆鐨勬覆鏌撶被 -->
+    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
+    <conversionRule conversionWord="wex"
+                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
+    <conversionRule conversionWord="wEx"
+                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
+    <!-- 閰嶇疆灞炴�� 褰╄壊鏃ュ織鏍煎紡 -->
+    <property name="CONSOLE_LOG_PATTERN"
+              value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
+
+
+    <!--杈撳嚭鍒版帶鍒跺彴鐨刟ppender-->
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <!--鏃ュ織绾у埆杩囨护鍣�-->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <!--鏃ュ織杩囨护绾у埆-->
+            <level>debug</level>
+        </filter>
+        <encoder>
+            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
+            <!-- 璁剧疆瀛楃闆� -->
+            <charset>UTF-8</charset>
+        </encoder>
+    </appender>
+
+
+    <!--log杈撳嚭鏂囦欢璺緞-->
+    <springProperty scope="context" name="log.path" source="logging.file.path"/>
+    <!--鏃ュ織鏂囦欢璺緞灞炴��-->
+    <property name="logback.logdir" value="${log.path}"/>
+
+    <!-- level涓� DEBUG 鏃ュ織 -->
+    <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 姝e湪璁板綍鐨勬棩蹇楁枃浠剁殑璺緞鍙婃枃浠跺悕 -->
+        <file>${logback.logdir}/log_debug.log</file>
+        <!--鏃ュ織鏂囦欢杈撳嚭鏍煎紡-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset> <!-- 璁剧疆瀛楃闆� -->
+        </encoder>
+        <!-- 鎸囧畾鏃ュ織璁板綍鍣ㄧ殑鎷嗗垎褰掓。绛栫暐锛屾寜鏃ユ湡锛屾寜澶у皬璁板綍 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 鏃ュ織褰掓。 -->
+            <fileNamePattern>${logback.logdir}/debug/log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--鏃ュ織鏂囦欢淇濈暀澶╂暟-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!--鏃ュ織绾ц繃婊よ鍒�-->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!--鏃ュ織杩囨护绾у埆-->
+            <level>debug</level>
+            <!--瓒呰繃杩囨护绾у埆鐨勭瓥鐣�-->
+            <onMatch>ACCEPT</onMatch>
+            <!--鏈秴杩囪繃婊ょ骇鍒殑绛栫暐-->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!--  level涓� INFO 鏃ュ織 -->
+    <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 姝e湪璁板綍鐨勬棩蹇楁枃浠剁殑璺緞鍙婃枃浠跺悕 -->
+        <file>${logback.logdir}/log_info.log</file>
+        <!--鏃ュ織鏂囦欢杈撳嚭鏍煎紡-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset>
+        </encoder>
+        <!-- 鎸囧畾鏃ュ織璁板綍鍣ㄧ殑鎷嗗垎褰掓。绛栫暐锛屾寜鏃ユ湡锛屾寜澶у皬璁板綍 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <!-- 姣忓ぉ鏃ュ織褰掓。璺緞浠ュ強鏍煎紡 -->
+            <fileNamePattern>${logback.logdir}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--鏃ュ織鏂囦欢淇濈暀澶╂暟-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!--鏃ュ織绾ц繃婊よ鍒�-->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!--鏃ュ織杩囨护绾у埆-->
+            <level>info</level>
+            <!--瓒呰繃杩囨护绾у埆鐨勭瓥鐣�-->
+            <onMatch>ACCEPT</onMatch>
+            <!--鏈秴杩囪繃婊ょ骇鍒殑绛栫暐-->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!--  level涓� WARN 鏃ュ織 -->
+    <appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 姝e湪璁板綍鐨勬棩蹇楁枃浠剁殑璺緞鍙婃枃浠跺悕 -->
+        <file>${logback.logdir}/log_warn.log</file>
+        <!--鏃ュ織鏂囦欢杈撳嚭鏍煎紡-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset> <!-- 姝ゅ璁剧疆瀛楃闆� -->
+        </encoder>
+        <!-- 鎸囧畾鏃ュ織璁板綍鍣ㄧ殑鎷嗗垎褰掓。绛栫暐 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logback.logdir}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--鏃ュ織鏂囦欢淇濈暀澶╂暟-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!--鏃ュ織绾ц繃婊よ鍒�-->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!--鏃ュ織杩囨护绾у埆-->
+            <level>warn</level>
+            <!--瓒呰繃杩囨护绾у埆鐨勭瓥鐣�-->
+            <onMatch>ACCEPT</onMatch>
+            <!--鏈秴杩囪繃婊ょ骇鍒殑绛栫暐-->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+
+    <!--  level涓� ERROR 鏃ュ織 -->
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 姝e湪璁板綍鐨勬棩蹇楁枃浠剁殑璺緞鍙婃枃浠跺悕 -->
+        <file>${logback.logdir}/log_error.log</file>
+        <!--鏃ュ織鏂囦欢杈撳嚭鏍煎紡-->
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
+            <charset>UTF-8</charset> <!-- 姝ゅ璁剧疆瀛楃闆� -->
+        </encoder>
+        <!--鎸囧畾鏃ュ織璁板綍鍣ㄧ殑鎷嗗垎褰掓。绛栫暐锛屾寜鏃ユ湡锛屾寜澶у皬璁板綍 -->
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${logback.logdir}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
+                <maxFileSize>100MB</maxFileSize>
+            </timeBasedFileNamingAndTriggeringPolicy>
+            <!--鏃ュ織鏂囦欢淇濈暀澶╂暟-->
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+        <!--鏃ュ織绾ц繃婊よ鍒�-->
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
+            <!--鏃ュ織杩囨护绾у埆-->
+            <level>ERROR</level>
+            <!--瓒呰繃杩囨护绾у埆鐨勭瓥鐣�-->
+            <onMatch>ACCEPT</onMatch>
+            <!--鏈秴杩囪繃婊ょ骇鍒殑绛栫暐-->
+            <onMismatch>DENY</onMismatch>
+        </filter>
+    </appender>
+
+    <!--
+        <logger>鐢ㄦ潵璁剧疆鏌愪竴涓寘鎴栬�呭叿浣撶殑鏌愪竴涓被鐨勬棩蹇楁墦鍗扮骇鍒��
+        浠ュ強鎸囧畾<appender>銆�<logger>浠呮湁涓�涓猲ame灞炴�э紝
+        涓�涓彲閫夌殑level鍜屼竴涓彲閫夌殑addtivity灞炴�с��
+        name:鐢ㄦ潵鎸囧畾鍙楁logger绾︽潫鐨勬煇涓�涓寘鎴栬�呭叿浣撶殑鏌愪竴涓被銆�
+        level:鐢ㄦ潵璁剧疆鎵撳嵃绾у埆锛屽ぇ灏忓啓鏃犲叧锛歍RACE, DEBUG, INFO, WARN, ERROR, ALL 鍜� OFF锛�
+              杩樻湁涓�涓壒淇楀�糏NHERITED鎴栬�呭悓涔夎瘝NULL锛屼唬琛ㄥ己鍒舵墽琛屼笂绾х殑绾у埆銆�
+              濡傛灉鏈缃灞炴�э紝閭d箞褰撳墠logger灏嗕細缁ф壙涓婄骇鐨勭骇鍒��
+        addtivity:鏄惁鍚戜笂绾ogger浼犻�掓墦鍗颁俊鎭�傞粯璁ゆ槸true銆�
+    -->
+    <!--<logger name="org.springframework.web" level="info"/>-->
+    <!--<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>-->
+    <!--
+        浣跨敤mybatis鐨勬椂鍊欙紝sql璇彞鏄痙ebug涓嬫墠浼氭墦鍗帮紝鑰岃繖閲屾垜浠彧閰嶇疆浜唅nfo锛屾墍浠ユ兂瑕佹煡鐪媠ql璇彞鐨勮瘽锛屾湁浠ヤ笅涓ょ鎿嶄綔锛�
+        绗竴绉嶆妸<root level="info">鏀规垚<root level="DEBUG">杩欐牱灏变細鎵撳嵃sql锛屼笉杩囪繖鏍锋棩蹇楅偅杈逛細鍑虹幇寰堝鍏朵粬娑堟伅
+        绗簩绉嶅氨鏄崟鐙粰dao涓嬬洰褰曢厤缃甦ebug妯″紡锛屼唬鐮佸涓嬶紝杩欐牱閰嶇疆sql璇彞浼氭墦鍗帮紝鍏朵粬杩樻槸姝e父info绾у埆锛�
+     -->
+
+
+    <!--
+        root鑺傜偣鏄繀閫夎妭鐐癸紝鐢ㄦ潵鎸囧畾鏈�鍩虹鐨勬棩蹇楄緭鍑虹骇鍒紝鍙湁涓�涓猯evel灞炴��
+        level:鐢ㄦ潵璁剧疆鎵撳嵃绾у埆锛屽ぇ灏忓啓鏃犲叧锛歍RACE, DEBUG, INFO, WARN, ERROR, ALL 鍜� OFF锛�
+        涓嶈兘璁剧疆涓篒NHERITED鎴栬�呭悓涔夎瘝NULL銆傞粯璁ゆ槸DEBUG
+        鍙互鍖呭惈闆朵釜鎴栧涓厓绱狅紝鏍囪瘑杩欎釜appender灏嗕細娣诲姞鍒拌繖涓猯ogger銆�
+    -->
+
+    <!--寮�鍙戠幆澧�:鎵撳嵃鎺у埗鍙�-->
+    <springProfile name="dev">
+        <logger name="com.lg.emp.controller" level="error"/>
+    </springProfile>
+
+    <!--root logger 閰嶇疆    -->
+    <root level="INFO">
+        <appender-ref ref="CONSOLE"/>
+        <appender-ref ref="DEBUG_FILE"/>
+        <appender-ref ref="INFO_FILE"/>
+        <appender-ref ref="WARN_FILE"/>
+        <appender-ref ref="ERROR_FILE"/>
+    </root>
+
+    <!--鐢熶骇鐜:杈撳嚭鍒版枃浠�-->
+    <!--<springProfile name="pro">-->
+    <!--<root level="info">-->
+    <!--<appender-ref ref="CONSOLE" />-->
+    <!--<appender-ref ref="DEBUG_FILE" />-->
+    <!--<appender-ref ref="INFO_FILE" />-->
+    <!--<appender-ref ref="ERROR_FILE" />-->
+    <!--<appender-ref ref="WARN_FILE" />-->
+    <!--</root>-->
+    <!--</springProfile>-->
+
+</configuration>
\ No newline at end of file
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java b/rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java
index cd880a0..dccd8f0 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java
@@ -22,9 +22,9 @@
 //        generator.username="sa";
 //        generator.password="Zoneyung@zy56$";
 
-        generator.table = "sys_pda_role_menu";
-        generator.tableDesc = "PDA鏉冮檺";
-        generator.packagePath = "com.vincent.rsf.server.manager";
+        generator.table = "sys_matnr_role_menu";
+        generator.tableDesc = "鐗╂枡鏉冮檺";
+        generator.packagePath = "com.vincent.rsf.server.system";
 
         generator.build();
     }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/common/config/MybatisPlusConfig.java b/rsf-server/src/main/java/com/vincent/rsf/server/common/config/MybatisPlusConfig.java
index 056b4a7..f4bac35 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/common/config/MybatisPlusConfig.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/common/config/MybatisPlusConfig.java
@@ -54,6 +54,7 @@
                         "sys_menu",
                         "sys_pda_role_menu",
                         "sys_menu_pda",
+                        "sys_matnr_role_menu",
                         "man_loc_type_rela",
                         "man_qly_inspect_result",
                         "view_stock_manage",
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/MatnrGroup.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/MatnrGroup.java
index 14431b9..66d873c 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/MatnrGroup.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/MatnrGroup.java
@@ -60,6 +60,8 @@
     @ApiModelProperty(value= "涓婄骇鍒嗙被ID")
     private Long parentId;
 
+    private Integer sort;
+
     /**
      * 鐘舵�� 1: 姝e父  0: 鍐荤粨  
      */
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/MatnrRoleMenuController.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/MatnrRoleMenuController.java
new file mode 100644
index 0000000..9389353
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/controller/MatnrRoleMenuController.java
@@ -0,0 +1,68 @@
+package com.vincent.rsf.server.system.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.vincent.rsf.common.utils.Utils;
+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.common.annotation.OperationLog;
+import com.vincent.rsf.server.manager.entity.MatnrGroup;
+import com.vincent.rsf.server.manager.entity.MenuPda;
+import com.vincent.rsf.server.manager.service.MatnrGroupService;
+import com.vincent.rsf.server.system.controller.param.RoleScopeParam;
+import com.vincent.rsf.server.system.entity.MatnrRoleMenu;
+import com.vincent.rsf.server.system.entity.PdaRoleMenu;
+import com.vincent.rsf.server.system.service.MatnrRoleMenuService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+
+@RestController
+public class MatnrRoleMenuController extends BaseController {
+
+    @Autowired
+    private MatnrRoleMenuService matnrRoleMenuService;
+
+    @Autowired
+    private MatnrGroupService matnrGroupService;
+
+    @GetMapping("/roleMatnr/scope/list")
+    public R scopeList(@RequestParam Long roleId) {
+        return R.ok().add(matnrRoleMenuService.listStrictlyMenuByRoleId(roleId));
+    }
+
+    @PostMapping("/menuMatnrGroup/tree")
+    public R tree(@RequestBody Map<String, Object> map) {
+        List<MatnrGroup> menuList = matnrGroupService.list(new LambdaQueryWrapper<MatnrGroup>().orderByAsc(MatnrGroup::getSort));
+        List<MatnrGroup> treeData = Utils.toTreeData(menuList, 0L, MatnrGroup::getParentId, MatnrGroup::getId,
+                MatnrGroup::setChildren);
+        if (!Cools.isEmpty(map.get("condition"))) {
+            Utils.treeRemove(treeData, String.valueOf(map.get("condition")), MatnrGroup::getName, MatnrGroup::getChildren);
+            Utils.treeRemove(treeData, String.valueOf(map.get("condition")), MatnrGroup::getName, MatnrGroup::getChildren);
+        }
+        return R.ok().add(treeData);
+    }
+
+    @PreAuthorize("hasAuthority('system:role:update')")
+    @OperationLog("Assign Permissions")
+    @PostMapping("/roleMatnr/scope/update")
+    @Transactional
+    public R scopeUpdate(@RequestBody RoleScopeParam param) {
+        Long roleId = param.getId();
+        List<Long> menuIds = new ArrayList<>(param.getMenuIds().getChecked());
+        menuIds.addAll(param.getMenuIds().getHalfChecked());
+        matnrRoleMenuService.remove(new LambdaQueryWrapper<MatnrRoleMenu>().eq(MatnrRoleMenu::getRoleId, roleId));
+        for (Long menuId : menuIds) {
+            if (!matnrRoleMenuService.save(new MatnrRoleMenu(roleId, menuId))) {
+                throw new CoolException("Internal Server Error!");
+            }
+        }
+        return R.ok("Assign Success");
+    }
+
+
+
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/MatnrRoleMenu.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/MatnrRoleMenu.java
new file mode 100644
index 0000000..732cc64
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/entity/MatnrRoleMenu.java
@@ -0,0 +1,50 @@
+package com.vincent.rsf.server.system.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 io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import com.vincent.rsf.framework.common.Cools;
+import com.vincent.rsf.framework.common.SpringUtils;
+import com.vincent.rsf.server.system.service.UserService;
+import com.vincent.rsf.server.system.entity.User;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@TableName("sys_matnr_role_menu")
+public class MatnrRoleMenu implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value= "")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value= "")
+    private Long roleId;
+
+    @ApiModelProperty(value= "")
+    private Long menuId;
+
+    public MatnrRoleMenu() {}
+
+    public MatnrRoleMenu(Long roleId,Long menuId) {
+        this.roleId = roleId;
+        this.menuId = menuId;
+    }
+
+//    MatnrRoleMenu matnrRoleMenu = new MatnrRoleMenu(
+//            null,    // [闈炵┖]
+//            null    // [闈炵┖]
+//    );
+
+
+
+
+
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/MatnrRoleMenuMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/MatnrRoleMenuMapper.java
new file mode 100644
index 0000000..4ecb897
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/mapper/MatnrRoleMenuMapper.java
@@ -0,0 +1,15 @@
+package com.vincent.rsf.server.system.mapper;
+
+import com.vincent.rsf.server.system.entity.MatnrRoleMenu;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Mapper
+@Repository
+public interface MatnrRoleMenuMapper extends BaseMapper<MatnrRoleMenu> {
+
+    List<Long> listStrictlyMenuByRoleId(Long roleId);
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/MatnrRoleMenuService.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/MatnrRoleMenuService.java
new file mode 100644
index 0000000..eae7beb
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/MatnrRoleMenuService.java
@@ -0,0 +1,11 @@
+package com.vincent.rsf.server.system.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.vincent.rsf.server.system.entity.MatnrRoleMenu;
+
+import java.util.List;
+
+public interface MatnrRoleMenuService extends IService<MatnrRoleMenu> {
+
+    List<Long> listStrictlyMenuByRoleId(Long roleId);
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/MatnrRoleMenuServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/MatnrRoleMenuServiceImpl.java
new file mode 100644
index 0000000..f9026cd
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/system/service/impl/MatnrRoleMenuServiceImpl.java
@@ -0,0 +1,17 @@
+package com.vincent.rsf.server.system.service.impl;
+
+import com.vincent.rsf.server.system.mapper.MatnrRoleMenuMapper;
+import com.vincent.rsf.server.system.entity.MatnrRoleMenu;
+import com.vincent.rsf.server.system.service.MatnrRoleMenuService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service("matnrRoleMenuService")
+public class MatnrRoleMenuServiceImpl extends ServiceImpl<MatnrRoleMenuMapper, MatnrRoleMenu> implements MatnrRoleMenuService {
+    @Override
+    public List<Long> listStrictlyMenuByRoleId(Long roleId) {
+        return baseMapper.listStrictlyMenuByRoleId(roleId);
+    }
+}
diff --git a/rsf-server/src/main/java/matnrRoleMenu.sql b/rsf-server/src/main/java/matnrRoleMenu.sql
new file mode 100644
index 0000000..bf053b0
--- /dev/null
+++ b/rsf-server/src/main/java/matnrRoleMenu.sql
@@ -0,0 +1,23 @@
+-- save matnrRoleMenu record
+-- mysql
+insert into `sys_menu` ( `name`, `parent_id`, `route`, `component`, `type`, `sort`, `tenant_id`, `status`) values ( 'menu.matnrRoleMenu', '0', '/system/matnrRoleMenu', 'matnrRoleMenu', '0' , '0', '1' , '1');
+
+insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Query 鐗╂枡鏉冮檺', '', '1', 'system:matnrRoleMenu:list', '0', '1', '1');
+insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Create 鐗╂枡鏉冮檺', '', '1', 'system:matnrRoleMenu:save', '1', '1', '1');
+insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Update 鐗╂枡鏉冮檺', '', '1', 'system:matnrRoleMenu:update', '2', '1', '1');
+insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Delete 鐗╂枡鏉冮檺', '', '1', 'system:matnrRoleMenu:remove', '3', '1', '1');
+
+-- locale menu name
+matnrRoleMenu: 'MatnrRoleMenu',
+
+-- locale field
+matnrRoleMenu: {
+    roleId: "roleId",
+    menuId: "menuId",
+},
+
+-- ResourceContent
+import matnrRoleMenu from './matnrRoleMenu';
+
+case 'matnrRoleMenu':
+    return matnrRoleMenu;
diff --git a/rsf-server/src/main/resources/mapper/system/MatnrRoleMenuMapper.xml b/rsf-server/src/main/resources/mapper/system/MatnrRoleMenuMapper.xml
new file mode 100644
index 0000000..7c00803
--- /dev/null
+++ b/rsf-server/src/main/resources/mapper/system/MatnrRoleMenuMapper.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.vincent.rsf.server.system.mapper.MatnrRoleMenuMapper">
+
+    <select id="listStrictlyMenuByRoleId" resultType="java.lang.Long">
+        select sm.id
+        from man_matnr_group sm
+        left join sys_matnr_role_menu srm on sm.id = srm.menu_id
+        where 1=1
+        and sm.deleted = 0
+        and srm.role_id = #{roleId}
+        <!--
+        and sm.id not in (
+            select sm.parent_id
+            from sys_menu sm
+            inner join sys_role_menu srm on sm.id = srm.menu_id
+            and srm.role_id = #{roleId}
+        )
+        -->
+        order by sm.sort
+    </select>
+</mapper>

--
Gitblit v1.9.1