PAYATHON 2026

SoftLayer 如何解析新订购服务的 resourceTableId

数据小陈

结论

不要通过当前账期的 invoiceItems[].resourceTableId 获取资源 ID。这个字段属于发票行数据,在发票正式生成或结算前可能一直是 null。

应先读取对应 SoftLayer_Billing_Item 的 resourceTableId。如果顶层 Billing Item 表示套餐或容器,还要继续检查它的子 Billing Items。资源创建完成并与 Billing Item 建立关联后,resourceTableId 才能稳定返回。

为什么新服务的发票行没有资源 ID

getNextInvoiceTopLevelBillingItems 返回当前有效的计费项目,其中的 invoiceItems 可能包含尚未正式开票的待结算行。

SoftLayer_Invoice_Item.resourceTableId 更像是开票时保存的资源引用,不适合用来实时发现资源。因此,新订单可能处于以下状态:

  • Billing Item 和待结算 invoice line 已经创建。
  • 主机名、域名、备注等计费信息已经写入。
  • 服务仍在 provisioning,或者计费记录尚未与实际资源完成关联。
  • invoiceItems[].resourceTableId 暂时是 null。

历史账期的发票行已经完成结算,数据也已固化,因此通常能看到 resourceTableId。

下面三个 ID 的含义不同:

  • billingItem.id:计费项目 ID。
  • invoiceItem.id:发票行 ID。
  • resourceTableId:实际服务资源 ID,例如 Virtual Guest ID 或 Bare Metal Server ID。

前两个 ID 都不能当作资源 ID 使用。

直接读取 Billing Item 的 resourceTableId

将 resourceTableId 添加到顶层 Billing Item 的 object mask 中,不要只在 invoiceItems 中请求这个字段:

GET /rest/v3.1/SoftLayer_Account/getNextInvoiceTopLevelBillingItems.json
    ?objectMask=mask[
        id,
        categoryCode,
        hostName,
        domainName,
        resourceTableId,
        invoiceItems[
            id,
            createDate,
            hostName,
            domainName,
            resourceTableId,
            notes
        ]
    ]

返回结果可能如下:

[
  {
    "id": 123456789,
    "categoryCode": "guest_core",
    "hostName": "example-vsi",
    "domainName": "example.com",
    "resourceTableId": 987654321,
    "invoiceItems": [
      {
        "id": 456789012,
        "createDate": "2026-09-01T10:20:30+08:00",
        "resourceTableId": null
      }
    ]
  }
]

这里应该使用顶层的 987654321,不需要等待 invoiceItems[0].resourceTableId 补齐。

也可以根据 Billing Item ID 单独查询,确认资源关联是否已经建立:

GET /rest/v3.1/SoftLayer_Billing_Item/123456789/getObject.json
    ?objectMask=mask[
        id,
        categoryCode,
        hostName,
        domainName,
        resourceTableId
    ]

检查子 Billing Items

getNextInvoiceTopLevelBillingItems 只返回顶层计费项目。对于某些产品,顶层项目表示套餐、父级服务或组合订单,实际的资源引用可能保存在子 Billing Item 中。

可以在请求中同时加入子项目:

GET /rest/v3.1/SoftLayer_Account/getNextInvoiceTopLevelBillingItems.json
    ?objectMask=mask[
        id,
        categoryCode,
        hostName,
        domainName,
        resourceTableId,
        children[
            id,
            categoryCode,
            hostName,
            domainName,
            resourceTableId
        ]
    ]

解析时先检查顶层 resourceTableId。如果为空,再检查 children:

def find_resource_ids(billing_item):
    results = []

    resource_id = billing_item.get("resourceTableId")
    if resource_id is not None:
        results.append({
            "billingItemId": billing_item["id"],
            "categoryCode": billing_item.get("categoryCode"),
            "resourceTableId": resource_id,
        })

    for child in billing_item.get("children", []):
        child_resource_id = child.get("resourceTableId")
        if child_resource_id is not None:
            results.append({
                "billingItemId": child["id"],
                "categoryCode": child.get("categoryCode"),
                "resourceTableId": child_resource_id,
            })

    return results

取得资源 ID 后,可以按照产品类型查询实际对象。例如:

GET /rest/v3.1/SoftLayer_Virtual_Guest/987654321/getObject.json

如果是 Bare Metal Server,则使用对应的 Hardware Server 服务:

GET /rest/v3.1/SoftLayer_Hardware_Server/987654321/getObject.json

Billing Item 本身也返回 null 时

如果顶层和子级 Billing Item 的 resourceTableId 都为空,通常表示资源仍未完成 provisioning,或者计费项目与资源之间的关联尚未写入。此时没有可靠的本地算法可以只根据 invoice line 推导资源表主键。

可以按以下顺序处理:

  1. 保存 billingItem.id,将它作为跟踪订单的依据。
  2. 查询 Billing Item 的 provisioning 状态或关联的 provisioning transaction。
  3. 等待订单完成,然后重新读取 Billing Item。
  4. 从 Billing Item 或它的子项目中获取 resourceTableId。
  5. 如果控制台已经显示资源创建完成,但该字段长时间仍为空,应向 IBM Cloud Classic Infrastructure 支持提供 Billing Item ID 和订单信息,请后台检查资源关联。

还可以查询账户下的 Virtual Guests 或 Hardware,再通过它们关联的 Billing Item ID 进行反向匹配。Billing Item ID 比 hostName 和 domainName 更可靠,因为主机名可能重复、被修改,或者在重装后继续使用。

注意事项

  • 不要依赖 invoiceItems[].resourceTableId 查找当前账期内刚创建的资源。
  • 不要将 invoiceItem.id 或 billingItem.id 直接传给 SoftLayer_Virtual_Guest。
  • 顶层 Billing Item 未必是实际资源对应的计费项目。对于组合产品,还需要检查 children。
  • categoryCode 可以帮助判断资源类型,但不同产品的计费结构可能不同,不应只根据单个 category 硬编码。
  • 如果服务仍在 provisioning,可以轮询 Billing Item,但要设置合理的间隔、超时和失败处理,避免高频调用。
  • Billing Item 的 resourceTableId 尚未建立时,通过主机名推断资源只能作为临时兜底,无法保证唯一性。

备注:内容仅供参考。