前端表单能看到“门禁说明”,Network里的shippingAddress.extension_attributes也有值,保存订单后 sales_order_address 却是空的。这个问题通常不是一个插件就能解决,因为字段要穿过四层:UI数据、Web API数据对象、Quote Address持久化、Quote转Order。任何一层没声明或没复制,后面都会消失。

1. 先确认字段放在正确的payload位置

不同字段集成方式可能使用customAttributes或extension_attributes。打开shipping-information请求,确认最终JSON结构与后端接口的数据对象一致,不要只看Knockout组件里的值。

shipping_address: {
  ...
  extension_attributes: {
    access_note: "Call on arrival"
  }
}

2. 声明接口扩展属性

<extension_attributes for="Magento\Quote\Api\Data\AddressInterface">
    <attribute code="access_note" type="string" />
</extension_attributes>
<extension_attributes for="Magento\Sales\Api\Data\OrderAddressInterface">
    <attribute code="access_note" type="string" />
</extension_attributes>

修改extension_attributes.xml后清生成代码并重新DI编译,检查生成接口里确实出现getAccessNote/setAccessNote。只清配置缓存不足以更新生成类。

3. 把值落到Quote Address

在ShippingInformationManagement保存前后选择合适插件,将extension attribute复制到Quote Address的数据字段。数据库需要声明quote_address.access_note列。插件要处理extensionAttributes为null的情况,也要做长度与字符校验。

$ext = $address->getExtensionAttributes();
if ($ext && $ext->getAccessNote() !== null) {
    $address->setData("access_note", trim($ext->getAccessNote()));
}

4. Quote转Order时复制字段

在etc/fieldset.xml配置shipping_address_convert_quote_address到order_address,或使用转换事件插件。

<fieldset id="sales_convert_quote_address">
  <field name="access_note">
    <aspect name="to_order_address" />
  </field>
</fieldset>

数据库同时要有sales_order_address.access_note。若字段也要通过订单REST/GraphQL返回,OrderAddressInterface的extension attribute还需要加载插件,把数据库值放回扩展对象。

常见漏点

  • 只给shipping地址加了字段,billing地址流程没有处理
  • 游客接口正常,登录客户接口走了不同Service
  • 多地址结账使用了另一套转换流程
  • 插件sortOrder导致值在后续处理器中被覆盖为空
  • 前端把字段放进custom_attributes,后端却只读extension_attributes

端到端测试

  • 游客下单与登录下单
  • 配送地址和账单地址不同
  • 门禁说明为空、超长、含中文和特殊字符
  • 后台查看订单、REST读取订单和邮件模板显示一致
  • 重复提交订单时字段不会串到另一张Quote
不要只在下单后Observer里从Request硬取值。异步下单、API下单或重试时未必还有原始HTTP请求,字段应跟随Quote数据模型流转。