lfx 1 周之前
父节点
当前提交
dcf2109939

+ 270 - 0
mjava-minglei/ERP-YIDA-PUSH-API.md

@@ -0,0 +1,270 @@
+# ERP 推送宜搭数据接口文档
+
+## 1. 接口说明
+
+ERP(T100)向明磊业务集成服务推送单据数据。当前接口仅负责接收和记录请求数据,暂不调用宜搭、不发起宜搭流程、不保存数据库。
+
+## 2. 基本信息
+
+| 项目 | 内容 |
+| --- | --- |
+| 接口名称 | ERP 推送宜搭数据 |
+| 请求方式 | `POST` |
+| 请求地址 | `http://10.0.0.250/api/erp/yida/push` |
+| 请求格式 | `application/json` |
+| 返回格式 | `application/json` |
+| 当前处理方式 | 接收入参、打印日志、返回成功 |
+| 当前支持类型 | `type` 非空即可,暂不限制类型白名单 |
+| `cpmp402` 对应单据 | 包材核价单通知 |
+| `cpmp402` 目标宜搭应用 | `APP_RJJL69QUIZVQSV7YC8TL` |
+
+## 3. 请求头
+
+```http
+Content-Type: application/json
+```
+
+## 4. 请求参数
+
+### 4.1 请求体
+
+```json
+{
+  "type": "cpmp402",
+  "mainData": {
+    "rq": "2026-09-07",
+    "bm": "采购部",
+    "tjr": "ERP_USER_001",
+    "gs": "明磊锂能有限公司",
+    "t100id": "T100_USER_001",
+    "ent": "CN",
+    "t100bm": "PURCHASE"
+  },
+  "detailData": [
+    {
+      "hjdh": "HJ20260907001",
+      "gysbh": "SUP001",
+      "gysmc": "供应商名称",
+      "t100id": "T100_SUP001",
+      "ent": "CN",
+      "yy": "zh_CN"
+    }
+  ]
+}
+```
+
+### 4.2 顶层字段
+
+| 字段 | 类型 | 必填 | 说明 |
+| --- | --- | --- | --- |
+| `type` | `String` | 是 | 单据类型标识。`cpmp402` 表示包材核价单通知,对应宜搭应用 `APP_RJJL69QUIZVQSV7YC8TL`。当前只校验非空,不限制具体值。 |
+| `mainData` | `Object` | 是 | 固定主数据对象。 |
+| `detailData` | `Array/Object` | 是 | 明细数据节点。必须传入,不校验具体 JSON 类型,可以是数组或对象。 |
+
+### 4.3 `mainData` 字段
+
+| 字段 | 类型 | 必填 | 说明                      |
+| --- | --- | --- |-------------------------|
+| `rq` | `String` | 否 | 日期(yyyy-MM-dd)。 |
+| `bm` | `String` | 否 | 部门(钉钉部门编号)。             |
+| `tjr` | `String` | 是 | 提交人(工号),不能为空字符串或空白字符。   |
+| `gs` | `String` | 否 | 公司(代码)。                 |
+| `t100id` | `String` | 否 | T100 用户或业务对象标识。         |
+| `ent` | `String` | 否 | 地区(代码)。                 |
+| `t100bm` | `String` | 否 | T100 部门。                |
+
+`mainData` 中除 `tjr` 外的字段可以传空字符串、空值或不填写。建议 ERP 端按约定字段完整传输,便于后续接入宜搭时直接映射。
+
+### 4.4 `detailData` 明细字段
+
+`detailData` 的格式不做校验,允许根据 `type` 对应的单据类型传入数组、对象或其他 JSON 结构。对于当前 ERP 推送格式,也支持在 `detailData` 外层再包一层 `detailData` 对象。
+
+以 `cpmp402` 包材核价单通知为例,推荐字段如下:
+
+| 字段 | 类型 | 必填 | 说明 |
+| --- | --- | --- | --- |
+| `hjdh` | `String` | 否 | 核价单号。 |
+| `gysbh` | `String` | 否 | 供应商编号。 |
+| `gysmc` | `String` | 否 | 供应商名称。 |
+| `t100id` | `String` | 否 | T100 业务对象标识。 |
+| `ent` | `String` | 否 | 地区。 |
+| `yy` | `String` | 否 | 语言。 |
+
+接口不会校验明细对象的具体字段,后续新增单据类型或单据字段时,可直接扩展请求内容。
+
+## 5. 成功响应
+
+HTTP 状态码:`200 OK`
+
+响应示例:
+
+```json
+{
+  "success": true,
+  "code": "200",
+  "message": "SUCCESS"
+}
+```
+
+该响应由基座 `com.malk.server.common.McR` 的 `McR.success()` 生成,当前不返回 ERP 推送的业务数据或处理结果。
+
+## 6. 参数校验失败
+
+以下情况会被拒绝:
+
+- `type` 缺失或为空白字符串。
+- `mainData` 缺失或为 `null`。
+- `mainData.tjr` 缺失或为空白字符串。
+- `detailData` 缺失或为 `null`。
+- 请求 JSON 格式错误。
+
+HTTP 状态码:`400 Bad Request`
+
+示例:
+
+```json
+{
+  "timestamp": "2026-09-07T13:50:00.000+00:00",
+  "status": 400,
+  "error": "Bad Request",
+  "path": "/api/erp/yida/push"
+}
+```
+
+`detailData` 可以为空数组:
+
+```json
+{
+  "type": "cpmp402",
+  "mainData": {
+    "tjr": "ERP_USER_001"
+  },
+  "detailData": []
+}
+```
+
+也可以使用对象格式:
+
+```json
+{
+  "type": "cpmp402",
+  "mainData": {
+    "rq": "2026-09-08",
+    "bm": "ML07000000",
+    "tjr": "3075",
+    "gs": "ML",
+    "t100id": "ML",
+    "ent": 87,
+    "t100bm": "ML07000000"
+  },
+  "detailData": {
+    "detailData": [
+      {
+        "hjdh": "MLCH326090800001",
+        "gysbh": "113009",
+        "gysmc": "南京鼎泰五金工具有限公司",
+        "t100id": "ML",
+        "ent": "87",
+        "yy": "zh_CN"
+      }
+    ]
+  }
+}
+```
+
+## 7. 日志说明
+
+请求成功进入 Service 后,会记录以下两类 `INFO` 日志:
+
+```text
+ERP Yida push request: {请求 JSON}
+ERP Yida push response: {McR 响应 JSON}
+```
+
+示例:
+
+```text
+ERP Yida push request: {"type":"cpmp402","mainData":{"tjr":"ERP_USER_001"},"detailData":[]}
+ERP Yida push response: {"success":true,"code":"200","message":"SUCCESS"}
+```
+
+当前日志记录的是完整业务请求内容。生产环境如请求中包含密码、令牌或其他敏感信息,应在 ERP 端避免传输无关敏感字段;后续如接入敏感字段,需要增加日志脱敏策略。
+
+## 8. 调用示例
+
+### 8.1 PowerShell
+
+```powershell
+$body = @{
+    type = "cpmp402"
+    mainData = @{
+        rq = "2026-09-07"
+        bm = "采购部"
+        tjr = "ERP_USER_001"
+        gs = "明磊锂能有限公司"
+        t100id = "T100_USER_001"
+        ent = "CN"
+        t100bm = "PURCHASE"
+    }
+    detailData = @(
+        @{
+            hjdh = "HJ20260907001"
+            gysbh = "SUP001"
+            gysmc = "供应商名称"
+            t100id = "T100_SUP001"
+            ent = "CN"
+            yy = "zh_CN"
+        }
+    )
+} | ConvertTo-Json -Depth 10
+
+Invoke-RestMethod `
+    -Method Post `
+    -Uri "http://10.0.0.250/api/erp/yida/push" `
+    -ContentType "application/json" `
+    -Body $body
+```
+
+### 8.2 curl
+
+```bash
+curl -X POST "http://10.0.0.250/api/erp/yida/push" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "type": "cpmp402",
+    "mainData": {
+      "rq": "2026-09-07",
+      "bm": "采购部",
+      "tjr": "ERP_USER_001",
+      "gs": "明磊锂能有限公司",
+      "t100id": "T100_USER_001",
+      "ent": "CN",
+      "t100bm": "PURCHASE"
+    },
+    "detailData": [
+      {
+        "hjdh": "HJ20260907001",
+        "gysbh": "SUP001",
+        "gysmc": "供应商名称",
+        "t100id": "T100_SUP001",
+        "ent": "CN",
+        "yy": "zh_CN"
+      }
+    ]
+  }'
+```
+
+## 9. 单据类型映射
+
+| `type` | 单据名称 | 目标宜搭应用 | 当前状态 |
+| --- | --- | --- | --- |
+| `cpmp402` | 包材核价单通知 | `APP_RJJL69QUIZVQSV7YC8TL` | 仅接收并记录 ERP 推送数据,暂不调用宜搭 |
+
+## 10. 当前处理边界
+
+- 当前不调用宜搭接口。
+- 当前不发起宜搭流程。
+- 当前不根据 `type` 分发具体单据处理器。
+- 当前不校验 `cpmp402` 以外的单据类型。
+- 当前不持久化 ERP 推送数据。
+- 后续接入宜搭时,`cpmp402` 应路由至宜搭应用 `APP_RJJL69QUIZVQSV7YC8TL` 下的包材核价单通知表单,并根据 `type` 增加单据类型处理器和字段映射逻辑。

+ 1 - 1
mjava-minglei/README.md

@@ -291,7 +291,7 @@ Content-Type: application/json
 }
 ```
 
-`mainData` 为固定主数据结构,其中 `tjr` 必填;`detailData` 必须存在,元素字段可随单据类型变化,也可以为空数组。接口成功返回基座 `McR.success()`,Service 会记录请求和响应 JSON 日志。
+`mainData` 为固定主数据结构,其中 `tjr` 必填;`detailData` 必须存在,但不校验具体 JSON 类型,可传数组或对象(包括外层再包一层 `detailData` 的对象结构)。接口成功返回基座 `McR.success()`,Service 会记录请求和响应 JSON 日志。
 
 ## 5. 统一响应和异常处理
 

+ 3 - 5
mjava-minglei/src/main/java/com/malk/minglei/dto/ErpYidaPushRequest.java

@@ -3,8 +3,6 @@ package com.malk.minglei.dto;
 import javax.validation.Valid;
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
-import java.util.List;
-import java.util.Map;
 
 /**
  * ERP 推送宜搭的统一请求模型。
@@ -19,7 +17,7 @@ public class ErpYidaPushRequest {
     private ErpYidaPushMainData mainData;
 
     @NotNull
-    private List<Map<String, Object>> detailData;
+    private Object detailData;
 
     public String getType() {
         return type;
@@ -37,11 +35,11 @@ public class ErpYidaPushRequest {
         this.mainData = mainData;
     }
 
-    public List<Map<String, Object>> getDetailData() {
+    public Object getDetailData() {
         return detailData;
     }
 
-    public void setDetailData(List<Map<String, Object>> detailData) {
+    public void setDetailData(Object detailData) {
         this.detailData = detailData;
     }
 }

+ 2 - 2
mjava-minglei/src/test/java/com/malk/minglei/controller/ErpYidaPushControllerTest.java

@@ -18,9 +18,9 @@ class ErpYidaPushControllerTest {
             .build();
 
     @Test
-    void acceptsDynamicDetailDataAndReturnsMcRSuccess() throws Exception {
+    void acceptsObjectDetailDataAndReturnsMcRSuccess() throws Exception {
         String request = "{\"type\":\"cpmp402\",\"mainData\":{\"tjr\":\"user-001\"},"
-                + "\"detailData\":[{\"hjdh\":\"HJ-001\",\"customField\":123}]}";
+                + "\"detailData\":{\"detailData\":[{\"hjdh\":\"HJ-001\",\"customField\":123}]}}";
 
         mockMvc.perform(post("/api/erp/yida/push")
                         .contentType(MediaType.APPLICATION_JSON)

+ 2 - 1
mjava-minglei/src/test/java/com/malk/minglei/service/impl/ErpYidaPushServiceImplTest.java

@@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 
 import java.util.Collections;
+import java.util.Map;
 
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.mockito.Mockito.mock;
@@ -23,7 +24,7 @@ class ErpYidaPushServiceImplTest {
         ErpYidaPushRequest request = new ErpYidaPushRequest();
         request.setType("cpmp402");
         request.setMainData(mainData);
-        request.setDetailData(Collections.<java.util.Map<String, Object>>singletonList(
+        request.setDetailData(Collections.<Map<String, Object>>singletonList(
                 Collections.<String, Object>singletonMap("hjdh", "HJ-001")));
 
         McR result = service.push(request);