从销售订单推送 MES 看懂微服务调用(二):DTO 转换与 OpenFeign 调用

第一篇我们已经把"销售订单为什么要推送 MES、前端按钮怎么触发后端接口"讲清楚了。

这一篇继续往后走,重点看 CRM 后端拿到订单 ID 之后,如何查询真实订单、校验业务规则、组装 MES 需要的 DTO,并通过 OpenFeign 调用 MES 服务。

这篇文章适合三类读者:

  • 刚接触 Spring Cloud / OpenFeign,不知道微服务之间到底怎么互相调用的人。
  • 做过 CRUD,但还不太理解"跨系统业务链路"的后端开发同学。
  • 想通过制造业 CRM / MES 项目,理解订单从销售侧流转到生产侧的同学。

读完这一篇,你至少应该能说清楚四件事:

  • 前端为什么只传订单 ID,而不是传整张订单数据。
  • CRM 为什么要重新查询真实订单,并在推送前做业务校验。
  • DTO 转换到底是在转换什么,不只是"换个对象"。
  • OpenFeign 如何把 CRM 的 Java 方法调用变成一次对 MES 的 HTTP 请求。

目录

text 复制代码
一、这一篇先解决一个核心问题
二、先看 Controller:后端入口只接收订单 ID
三、批量推送不是一次性乱推,而是逐条处理
四、单条推送的核心:syncToMes(id)
五、为什么后端必须重新查订单?
六、推送前为什么要先校验?
七、订单明细 DTO 转换:CRM 字段变成 MES 看得懂的字段
八、订单主表 DTO 转换:一次推送的主体数据
九、OpenFeign 是怎么把 CRM 调到 MES 的?
十、MES 那边是谁接收?
十一、成功后 CRM 怎么记录状态?
十二、失败后为什么不能只抛异常?
十三、把整条链路串起来
十四、为什么这部分值得重点学习?
十五、读代码时建议按这个顺序看
十六、常见问题排查清单
十七、项目/面试中怎么表达这段经历
十八、本篇总结

一、这一篇先解决一个核心问题

很多刚接触微服务调用的同学,会把"调接口"理解成一句代码:

java 复制代码
mesCrmFeign.syncSaleOrder(req);

但在真实业务系统里,真正重要的不是这一句调用本身,而是调用之前和调用之后的整套链路:

text 复制代码
前端只传订单 ID
        ↓
CRM 后端根据 ID 查询真实订单
        ↓
校验订单是否允许推送 MES
        ↓
把 CRM 订单转换成 MES 接口 DTO
        ↓
通过 OpenFeign 调用 MES 服务
        ↓
记录推送成功 / 失败状态

这也是制造业系统和普通 CRUD 最大的区别之一:数据不是孤立存在的。CRM 的销售订单,后面可能要进入 MES 的排产、生产、质检、发货流程,所以每一次跨系统推送都要保证数据真实、字段完整、状态合理、失败可追踪。

本文结合项目里的真实代码来讲,不凭空设计接口。

二、先看 Controller:后端入口只接收订单 ID

代码位置:

text 复制代码
D:\Tools\java\work\smartmake-platform\smartmake-platform-crm\src\main\java\com\smartmake\platform\crm\controller\OrderController.java

销售订单推送 MES 有两个入口:单条推送和批量推送。

java 复制代码
@PostMapping("/sync/{id}")
public OrderBatchSyncItemRspDTO syncToMes(@PathVariable Long id) {
    return orderService.syncToMes(id);
}

@PostMapping("/sync/batch")
public OrderBatchSyncRspDTO batchSyncToMes(@RequestBody List<Long> ids) {
    return orderService.batchSyncToMes(ids);
}

这里有一个很关键的设计点:前端没有把整张订单、订单明细、客户信息、金额信息都传过来,而是只传订单 ID。

为什么这样更合理?

因为前端页面上的数据只能用于展示和选择,不能作为跨系统推送的最终依据。真正推送给 MES 的数据,应该由 CRM 后端根据订单 ID 重新查询数据库得到。

这能避免几个问题:

  • 前端表格数据不完整,导致推送字段缺失。
  • 用户页面停留时间太久,前端缓存数据已经过期。
  • 前端被篡改参数,导致推送了不可信的数据。
  • 批量推送时每条订单需要单独校验和记录结果。

所以这里的 Controller 做得很轻:接收 ID,然后交给 Service。

三、批量推送不是一次性乱推,而是逐条处理

代码位置:

text 复制代码
D:\Tools\java\work\smartmake-platform\smartmake-platform-crm\src\main\java\com\smartmake\platform\crm\service\impl\OrderServiceImpl.java

批量推送的方法是:

java 复制代码
public OrderBatchSyncRspDTO batchSyncToMes(List<Long> ids) {
    OrderBatchSyncRspDTO result = new OrderBatchSyncRspDTO();
    if (ids == null || ids.isEmpty()) {
        result.setTotal(0);
        result.setSuccessCount(0);
        result.setFailureCount(0);
        return result;
    }

    int successCount = 0;
    int failureCount = 0;
    for (Long id : ids) {
        OrderBatchSyncItemRspDTO item;
        try {
            item = syncToMes(id);
        } catch (Exception e) {
            item = new OrderBatchSyncItemRspDTO();
            item.setOrderId(id);
            item.setSuccess(false);
            item.setMessage(friendlyMesSyncErrorV2(e));
        }

        if (Boolean.TRUE.equals(item.getSuccess())) {
            successCount++;
        } else {
            failureCount++;
        }
        result.getItems().add(item);
    }

    result.setTotal(ids.size());
    result.setSuccessCount(successCount);
    result.setFailureCount(failureCount);
    return result;
}

这一段代码的业务价值很高。

它不是简单地说"批量推送成功"或者"批量推送失败",而是每条订单单独调用 syncToMes(id),最后统计:

  • 总共推送几条。
  • 成功几条。
  • 失败几条。
  • 每条订单自己的推送结果。

这样前端才能给用户一个更清楚的反馈,比如:

text 复制代码
推送完成:成功 3 条,失败 1 条

而不是把一堆异常堆给用户看。

这类设计在企业系统里很常见:批量操作最好支持"部分成功、部分失败",否则一条数据异常就会影响整批业务。

四、单条推送的核心:syncToMes(id)

真正的核心方法是 syncToMes(Long id)

可以先把它理解成五步:

text 复制代码
1. 根据订单 ID 查询 CRM 订单详情
2. 校验订单是否允许推送 MES
3. 转换订单明细 DTO
4. 转换订单主表 DTO
5. 调用 MES,并回写 CRM 推送状态

对应代码核心结构如下:

java 复制代码
public OrderBatchSyncItemRspDTO syncToMes(Long id) {
    OrderBatchSyncItemRspDTO result = new OrderBatchSyncItemRspDTO();
    result.setOrderId(id);

    OrderRspDTO dto = getById(id);
    if (dto != null) {
        result.setOrderCode(dto.getCode());
    }

    try {
        validateMesSyncOrder(dto);

        List<CrmOrderSyncItemReq> itemReqs = ...;

        CrmOrderSyncReq req = new CrmOrderSyncReq()
                .setExternalId(String.valueOf(dto.getId()))
                .setSaleOrderNum(dto.getCode())
                .setClientName(dto.getCustomerName())
                .setClientCode(dto.getCustomerCode())
                .setItems(itemReqs);

        mesCrmFeign.syncSaleOrder(req);

        // 推送成功后,回写 CRM 的 MES 同步状态
    } catch (Exception e) {
        // 推送失败后,记录失败原因
    }

    return result;
}

这一段就是本文的主线。

五、为什么后端必须重新查订单?

这句代码很关键:

java 复制代码
OrderRspDTO dto = getById(id);

前端传过来的只是订单 ID,后端要通过 getById(id) 查出完整的订单数据。

这里查出来的 OrderRspDTO 不是只有订单主表字段,还会包含订单明细等关联信息。因为 MES 生产需要的不只是"有一张订单",还要知道:

  • 订单编号是什么。
  • 客户是谁。
  • 预计交货日期是什么。
  • 收货地址是什么。
  • 订单金额是多少。
  • 订单里有哪些产品。
  • 每个产品的数量、规格、单位、价格是多少。

这就是业务系统里经常说的"真实数据关联"。不是前端表格上看到什么就推什么,而是后端按订单 ID 拉取当前数据库里的完整真实数据。

六、推送前为什么要先校验?

代码里有一个专门的方法:

java 复制代码
private void validateMesSyncOrder(OrderRspDTO dto) {
    if (dto == null) {
        throw new IllegalArgumentException("订单不存在,无法推送 MES");
    }
    if (!MES_SYNCABLE_STATUSES.contains(dto.getStatus())) {
        throw new IllegalArgumentException("当前订单状态不允许推送 MES");
    }
    if (StrUtil.isBlank(dto.getCode())) {
        throw new IllegalArgumentException("订单编号为空,无法推送 MES");
    }
    if (StrUtil.isBlank(dto.getCustomerName())) {
        throw new IllegalArgumentException("客户名称为空,请完善客户信息");
    }
    if (dto.getItems() == null || dto.getItems().isEmpty()) {
        throw new IllegalArgumentException("订单明细为空,无法推送 MES");
    }
}

实际代码里还会继续校验每一行订单明细,例如产品编号、产品名称、数量、价格等。

当前项目里允许推送 MES 的订单状态是:

java 复制代码
private static final Set<String> MES_SYNCABLE_STATUSES = Set.of("confirmed", "producing");

也就是说,不是所有订单都能推送 MES。只有已经确认或已进入生产相关阶段的订单,才有资格同步到 MES。

这一步非常重要。

如果不校验,可能会出现这些问题:

  • 草稿订单被推送到 MES,生产系统拿到未确认需求。
  • 没有客户名称,MES 无法生成完整销售订单。
  • 没有产品明细,MES 不知道生产什么。
  • 产品数量异常,后续排产和库存都会出错。

所以跨系统调用前,一定不要只想"能不能调通接口",还要先问:"这份数据有没有资格进入下一个系统?"

七、订单明细 DTO 转换:CRM 字段变成 MES 看得懂的字段

订单明细转换用的是 CrmOrderSyncItemReq

代码位置:

text 复制代码
D:\Tools\java\work\smartmake-platform\smartmake-platform-feign\smartmake-platform-mes-api\src\main\java\com\smartmake\platform\mes\feign\domain\req\CrmOrderSyncItemReq.java

这个 DTO 主要描述订单里的产品行:

java 复制代码
public class CrmOrderSyncItemReq {
    private Integer sequence;
    private String productId;
    private String productCode;
    private String productName;
    private String spec;
    private String unit;
    private BigDecimal qty;
    private BigDecimal price;
    private BigDecimal taxRate;
    private BigDecimal amount;
    private BigDecimal taxAmount;
    private BigDecimal taxInclusiveAmount;
    private String remark;
}

CRM 里每一行订单明细,会被转换成 MES 接口里的每一行产品明细:

java 复制代码
List<CrmOrderSyncItemReq> itemReqs = dto.getItems()
        .stream()
        .map(item -> new CrmOrderSyncItemReq()
                .setSequence(item.getSequence())
                .setProductId(item.getProductId() != null ? String.valueOf(item.getProductId()) : null)
                .setProductCode(item.getProductCode())
                .setProductName(item.getProductName())
                .setSpec(item.getSpec())
                .setUnit(item.getUnit())
                .setQty(item.getQty())
                .setPrice(item.getPrice())
                .setTaxRate(item.getTaxRate())
                .setAmount(item.getAmount())
                .setTaxAmount(item.getTaxAmount())
                .setTaxInclusiveAmount(item.getTaxInclusiveAmount())
                .setRemark(item.getRemark()))
        .collect(Collectors.toList());

这里要理解一个点:DTO 转换不是机械复制字段,而是在做两个系统之间的数据协议适配。

CRM 关心的是销售订单,MES 关心的是后续生产执行。所以 CRM 推过去的明细至少要让 MES 知道:

  • 生产哪个产品:productCodeproductName
  • 产品规格是什么:spec
  • 数量是多少:qty
  • 单位是什么:unit
  • 金额、税额如何:amounttaxAmounttaxInclusiveAmount
  • 明细备注是什么:remark

如果这些字段没有处理好,MES 即使接到了请求,也很难进入后续业务流程。

八、订单主表 DTO 转换:一次推送的主体数据

订单主表转换用的是 CrmOrderSyncReq

代码位置:

text 复制代码
D:\Tools\java\work\smartmake-platform\smartmake-platform-feign\smartmake-platform-mes-api\src\main\java\com\smartmake\platform\mes\feign\domain\req\CrmOrderSyncReq.java

主要字段如下:

java 复制代码
public class CrmOrderSyncReq {
    private String externalId;
    private String saleOrderNum;
    private String clientCode;
    private String clientName;
    private String source;
    private String sourceSystem;
    private String deliveryDate;
    private String address;
    private BigDecimal includeTax;
    private BigDecimal noIncludeTax;
    private String isTax;
    private String status;
    private String remark;
    private List<CrmOrderSyncItemReq> items;
}

CRM 组装请求的核心代码是:

java 复制代码
CrmOrderSyncReq req = new CrmOrderSyncReq()
        .setExternalId(String.valueOf(dto.getId()))
        .setSaleOrderNum(dto.getCode())
        .setClientName(dto.getCustomerName())
        .setClientCode(dto.getCustomerCode())
        .setDeliveryDate(dto.getDeliveryDate() != null
                ? new SimpleDateFormat("yyyy-MM-dd").format(dto.getDeliveryDate()) : null)
        .setNoIncludeTax(dto.getTotalAmount())
        .setIncludeTax(dto.getTaxInclusiveAmount())
        .setIsTax("1")
        .setAddress(dto.getReceiveAddress())
        .setRemark(dto.getRemark())
        .setSource("CRM")
        .setSourceSystem("smartmake-CRM")
        .setStatus("0")
        .setItems(itemReqs);

这里最值得注意的是 externalId

java 复制代码
.setExternalId(String.valueOf(dto.getId()))

它表示 CRM 订单在 MES 里的外部系统 ID。简单说,就是 MES 要知道这张单来自 CRM 的哪一条订单。

这对后续很重要:

  • MES 可以根据 externalId 判断是不是同一张 CRM 订单。
  • CRM 后续查询 MES 进度时,也可以通过这个 ID 找回对应的 MES 订单。
  • 如果后续要做幂等、防重复推送、状态回写,externalId 就是关键关联字段。

所以不要只把它当成一个普通字符串,它是 CRM 与 MES 订单关系的锚点。

九、OpenFeign 是怎么把 CRM 调到 MES 的?

代码位置:

text 复制代码
D:\Tools\java\work\smartmake-platform\smartmake-platform-feign\smartmake-platform-mes-api\src\main\java\com\smartmake\platform\mes\feign\MesCrmFeign.java

Feign 接口是这样定义的:

java 复制代码
@FeignClient(name = FeignConstants.MES_FEIGN_NAME)
public interface MesCrmFeign {

    @PostMapping("/order/sale/inner/syncFromCrm")
    void syncSaleOrder(@RequestBody CrmOrderSyncReq req);

    @GetMapping("/order/sale/inner/progress/by-external-id/{externalId}")
    MesOrderProgressRsp getOrderProgress(@PathVariable("externalId") String externalId);
}

这里有两个重点。

第一个重点:@FeignClient(name=FeignConstants.MES_FEIGN_NAME)

它表示这个接口不是普通 Java 接口,而是一个远程服务客户端。CRM 服务调用这个接口时,OpenFeign 会根据服务名找到 MES 服务。

第二个重点:@PostMapping("/order/sale/inner/syncFromCrm")

它表示 CRM 实际请求 MES 的这个内部接口:

text 复制代码
POST /order/sale/inner/syncFromCrm

于是 CRM 里的这一句:

java 复制代码
mesCrmFeign.syncSaleOrder(req);

就不再是普通方法调用,而是一次跨服务 HTTP 调用。

很多新手第一次看 OpenFeign,容易误解成"它只是帮我少写了一个 HTTP 工具类"。这个理解不算错,但不够完整。

在这个项目里,OpenFeign 至少承担了三层作用:

  • 用 Java 接口声明远程服务契约,让 CRM 侧知道可以调用 MES 哪些内部接口。
  • 通过服务名找到目标服务,而不是在业务代码里写死 IP 和端口。
  • 把 Java 对象 CrmOrderSyncReq 序列化成请求体,发送到 MES 对应 Controller。

所以 MesCrmFeign 本质上是 CRM 和 MES 之间的"接口契约层"。如果这个契约变了,比如字段名、路径、请求方式发生变化,CRM 和 MES 两边都要一起确认。

可以把它理解成:

text 复制代码
CRM OrderServiceImpl
        ↓
MesCrmFeign.syncSaleOrder(req)
        ↓
OpenFeign 根据 MES 服务名找到 MES 实例
        ↓
发送 POST /order/sale/inner/syncFromCrm
        ↓
MES SaleController 接收 CrmOrderSyncReq

十、MES 那边是谁接收?

MES 接收接口在:

text 复制代码
D:\Tools\java\work\smartmake-platform\smartmake-platform-mes\src\main\java\com\smartmake\platform\mes\controller\order\SaleController.java

代码是:

java 复制代码
@PostMapping("/inner/syncFromCrm")
public void syncFromCrm(@RequestBody CrmOrderSyncReq req) {
    saleService.syncFromCrm(req);
}

注意这里的方法路径是:

text 复制代码
/inner/syncFromCrm

而 Feign 里写的是:

text 复制代码
/order/sale/inner/syncFromCrm

这是因为 SaleController 自己通常还有类级别路径,Feign 调用时写的是完整访问路径。

这一篇我们重点讲 CRM 侧的 DTO 转换和 Feign 调用。MES 接收之后如何落库、如何避免重复同步、如何把生产进度回传给 CRM,可以放到第三篇继续讲。

十一、成功后 CRM 怎么记录状态?

调用 MES 成功后,CRM 会更新订单的 MES 同步状态:

java 复制代码
lambdaUpdate()
        .eq(Order::getId, id)
        .set(Order::getMesSyncStatus, "success")
        .set(Order::getMesSyncTime, new Date())
        .set(Order::getMesSyncError, null)
        .update();

这里的三个字段非常重要:

text 复制代码
mesSyncStatus  推送状态
mesSyncTime    推送时间
mesSyncError   推送失败原因

这就是为什么前端销售订单列表可以展示"是否推送 MES、什么时候推送、失败原因是什么"。

如果没有这些字段,用户点了推送按钮之后就很难知道:

  • 到底推送成功没有。
  • 失败原因是什么。
  • 能不能重新推送。
  • 哪些订单已经进入 MES。

企业系统不是只要按钮点得动,还要让业务人员能追踪结果。

十二、失败后为什么不能只抛异常?

失败时,代码会把异常转成更友好的信息,然后记录到订单上:

java 复制代码
String friendlyMessage = friendlyMesSyncErrorV2(e);
result.setSuccess(false);
result.setMessage(friendlyMessage);

if (dto != null) {
    patch.setMesSyncStatus("failed");
    patch.setMesSyncTime(new Date());
    patch.setMesSyncError(friendlyMessage);
    baseMapper.updateById(patch);
}

这一点也很关键。

如果直接把 Java 异常返回给前端,用户看到的可能是:

text 复制代码
Connection refused
NullPointerException
FeignException

这些信息对开发排查有用,但对业务人员没有帮助。更合理的方式是:

  • 后端日志里保留完整异常,方便开发排查。
  • 前端展示业务人员能理解的失败原因。
  • 数据库记录 mesSyncError,方便后续查看和重试。

这也是跨系统调用里非常重要的一点:失败要可解释、可追踪、可恢复。

十三、把整条链路串起来

这一篇的完整链路可以这样理解:

text 复制代码
前端选择订单
        ↓
POST /crm/order/sync/batch
        ↓
OrderController.batchSyncToMes(ids)
        ↓
OrderServiceImpl.batchSyncToMes(ids)
        ↓
逐条调用 syncToMes(id)
        ↓
getById(id) 查询 CRM 真实订单
        ↓
validateMesSyncOrder(dto) 做业务校验
        ↓
订单明细转换成 CrmOrderSyncItemReq
        ↓
订单主表转换成 CrmOrderSyncReq
        ↓
MesCrmFeign.syncSaleOrder(req)
        ↓
MES SaleController.syncFromCrm(req)
        ↓
CRM 记录 mesSyncStatus / mesSyncTime / mesSyncError

如果用一句话总结:

CRM 不是把前端表格数据直接扔给 MES,而是根据订单 ID 查询真实订单,完成业务校验和 DTO 转换,再通过 OpenFeign 调用 MES,并把同步结果记录回 CRM。

十四、为什么这部分值得重点学习?

这段代码看起来只是"订单推送 MES",但它背后包含了很多真实项目里非常有价值的能力:

  • 跨服务调用:CRM 通过 OpenFeign 调 MES。
  • 真实数据组装:前端只传 ID,后端重新查完整订单。
  • DTO 协议设计:CRM 字段转换成 MES 能理解的请求对象。
  • 业务校验:不是所有订单都允许进入 MES。
  • 批量处理:支持成功几条、失败几条的结构化反馈。
  • 状态追踪:记录 MES 同步状态、同步时间、失败原因。
  • 异常处理:把技术异常转换成业务可理解的提示。

这些能力,比单纯写一个增删改查页面更有业务含金量。

十五、读代码时建议按这个顺序看

如果你想在 IDEA 里跟着项目代码自己看一遍,可以按这个顺序:

text 复制代码
1. OrderController.java
   看 /sync/{id} 和 /sync/batch 两个入口

2. OrderService.java
   看 syncToMes 和 batchSyncToMes 方法定义

3. OrderServiceImpl.java
   重点看 batchSyncToMes、syncToMes、validateMesSyncOrder

4. CrmOrderSyncReq.java
   看 CRM 推给 MES 的订单主表字段

5. CrmOrderSyncItemReq.java
   看 CRM 推给 MES 的订单明细字段

6. MesCrmFeign.java
   看 CRM 如何通过 OpenFeign 调 MES

7. SaleController.java
   看 MES 侧如何接收 CRM 推送

这样看代码会比直接在项目里乱搜更清晰,因为你是在沿着真实业务链路走。

十六、常见问题排查清单

如果销售订单推送 MES 出问题,可以按下面顺序排查。

1. 前端有没有把订单 ID 传到后端?

先看浏览器 Network,请求是否打到了:

text 复制代码
POST /crm/order/sync/batch

请求体是不是类似:

json 复制代码
[1001, 1002, 1003]

如果这里没有 ID,后端就没有办法查询真实订单。

2. Gateway 路由有没有转到 CRM 服务?

第一篇已经讲过,前端访问的是 /crm/order/sync/batch,经过网关后才进入 CRM 的 /order/sync/batch

如果网关路由、服务注册、前端代理配置不对,请求可能根本到不了 CRM 服务。

3. CRM 订单状态是否允许推送?

当前代码只允许这些状态推送:

java 复制代码
Set.of("confirmed", "producing")

如果订单还是草稿、已取消、已关闭,就不应该进入 MES。

4. 订单数据是否完整?

重点看这些字段:

text 复制代码
订单编号
客户名称
订单明细
产品编号
产品名称
产品数量

这些数据缺失时,不应该继续推送 MES。因为 MES 拿到不完整订单后,后面的排产、生产、质检都会受影响。

5. Feign 是否真的调到了 MES?

看 CRM 日志里有没有 Feign 调用异常,比如:

text 复制代码
Connection refused
Read timed out
Load balancer does not contain an instance

这些通常说明不是 DTO 转换问题,而是 MES 服务、注册中心、网络、网关或服务名配置的问题。

6. CRM 有没有写回同步状态?

推送成功后应该能看到:

text 复制代码
mesSyncStatus = success
mesSyncTime   = 当前推送时间
mesSyncError  = null

推送失败后应该能看到:

text 复制代码
mesSyncStatus = failed
mesSyncTime   = 当前推送时间
mesSyncError  = 失败原因

如果状态没有写回,前端就很难判断订单是否已经同步到 MES。

十七、项目/面试中怎么表达这段经历

如果你要把这段经历写到项目总结、日报或者面试里,不建议只写:

text 复制代码
完成销售订单推送 MES 接口。

这个表达太浅了,听起来像只是调了一个接口。

更好的表达是:

text 复制代码
参与 CRM 销售订单推送 MES 链路建设,负责订单推送前的数据校验、CRM 订单到 MES 同步 DTO 的字段映射、OpenFeign 跨服务调用以及同步结果状态回写。支持批量推送后按订单维度返回成功/失败结果,并记录 MES 同步状态、同步时间和失败原因,提升订单从销售侧进入生产侧的可追踪性和异常处理能力。

如果面试官继续追问"你具体做了什么",可以按这条线回答:

text 复制代码
前端只传订单 ID,CRM 后端根据 ID 查询真实订单,校验订单状态和明细完整性,再把订单主表和明细转换成 MES 约定的 CrmOrderSyncReq / CrmOrderSyncItemReq,通过 OpenFeign 调 MES 内部接口。调用成功后写入 success 和同步时间,调用失败后记录 failed 和失败原因,方便前端展示和后续重试。

这就能体现你理解的不只是接口调用,还有业务链路、真实数据一致性、状态流转和异常追踪。

十八、本篇总结

这一篇我们主要讲清楚了 CRM 推送 MES 的后端核心链路:

text 复制代码
订单 ID → 查询真实订单 → 业务校验 → DTO 转换 → OpenFeign 调 MES → 回写同步状态

其中最重要的不是 Feign 那一行代码,而是围绕它前后的业务处理:

  • 为什么不能相信前端传完整数据。
  • 为什么推送前要校验订单状态和订单明细。
  • 为什么要用 DTO 做系统之间的数据协议。
  • 为什么成功和失败都要写回 CRM。

理解了这一篇,再去看第三篇 MES 如何接收、如何避免重复同步、如何把生产进度和状态回传给 CRM,就会顺很多。

相关推荐
Patrick在香港1 小时前
Python爬虫框架实战:优雅抓取香港政府公开API数据的通用方案
java·开发语言·爬虫·python·ai编程·数据可视化·可用性测试
hey_sml1 小时前
MySQL常用操作速记:从字符集到游标全解析
java·开发语言·mysql
SamDeepThinking1 小时前
第3篇:企业级CAS单点登录实战-技术架构设计方案
后端·程序员·架构
掉鱼的猫1 小时前
Solon AOT & Native:三段式编译,从 Java 到原生可执行文件
java·云原生
Java内核笔记1 小时前
容错能力进入 spring-core:Spring Boot 4 原生重试机制全解析
java·后端
未秃头的程序猿2 小时前
虚拟线程上线一周后翻车了——pinning问题排查实录
java·后端·架构
wuminyu2 小时前
JDK21解决虚拟线程IO阻塞原理剖析
java·linux·c语言·jvm·c++
用户6919026813392 小时前
Harness工程的概念,以及简单的代码示例
javascript·架构
Dr.kangder2 小时前
嵌入式面试总结(一)——嵌入式系统实时性
面试·职场和发展·架构·嵌入式·虚拟化