好的,我们已经圆满完成了对3-GPP TS 29.594规范第四章功能逻辑的全面剖析。现在,我们将正式进入协议实现的“深水区”——第五章和第六章,将前面所有的功能描述,转化为精确、可编程的API接口和数据模型。

深度解析 3GPP TS 29.594:5 & 6 API定义与数据模型 (最终章)

本文技术原理深度参考了3GPP TS 29.594 V18.4.0 (2024-06) Release 18规范中,第五章“Nchf_SpendingLimitControl Service API”和第六章“Data Model”的全部核心内容。本文旨在为读者提供一份详尽的Nchf_SpendingLimitControl API“施工图”和“材料清单”,我们将剖析其完整的资源模型、每一个API端点的具体用法,以及构成这些API交互的所有JSON对象的精确结构。

引言:从“作战指令”到“电报密码本”

在之前的系列文章中,我们已经学习了Nchf_SpendingLimitControl服务的全套“作战指令”——Subscribe, Unsubscribe, Notify。我们知道了PCF和CHF在逻辑上应该如何交互。

现在,我们将进入“通信兵”的角色,学习如何将这些指令编码成双方都能准确无误理解的“电报”——即具体的HTTP请求和响应。第五章和第六章,正是这部规范的“密码本”和“电报格式规定”。

  • 第五章 (API Definitions):定义了“电报”的收发地址(URI)、信道规则(HTTP方法)和回执格式(响应码)。
  • 第六章 (Data Model):定义了“电报”正文内容的每一个字、每一个词(JSON字段)的精确含义和写法。

对于负责开发PCF和CHF的工程师来说,这两章是他们工作中最重要的参考依据。让我们最后一次跟随用户“乐乐”的场景,将这些抽象的API定义和数据模型,与真实的网络事件紧密结合起来。


1. 解读第五章 Nchf_SpendingLimitControl Service API - API的“骨架”

第五章构建了整个API的顶层结构和基础通信规则。

1.1 5.1 & 5.2 Introduction & Usage of HTTP (API入口与通信规则)

这部分内容与我们解读过的TS 29.673非常相似,都遵循了3GPP SBA的统一规定:

  • API URI结构: {apiRoot}/nchf-spendinglimitcontrol/v1/...
    • apiName固定为nchf-spendinglimitcontrol
    • apiVersion固定为v1
  • HTTP协议: 必须使用HTTP/2
  • 数据格式: 主要使用application/json,错误响应使用application/problem+json

1.2 5.3 Resources (资源) - API的“功能地图”

本节是第五章的核心,通过Figure 5.3.1-1Table 5.3.1-1为我们绘制了API的完整资源地图。

Figure 5.3.1-1: Resource URI structure…

  • API的资源结构非常简洁,只有一个主分支:/subscriptions
  • 该分支下可以访问单个资源:/{subscriptionId}

Table 5.3.1-1: Resources and methods overview 这张表是连接“服务操作”与“API实现”的最终桥梁。

Resource nameResource URIHTTP methodDescription
Spending Limit Retrieval Subscriptions/subscriptionsPOSTCreates a new individual spending limit retrieval subscription.
Individual Spending Limit Retrieval Subscription/subscriptions/{subscriptionId}PUTModifies an existing subscription…
DELETEDeletes an individual subscription.

API与服务操作的映射关系:

  • POST /subscriptions Subscribe (初始创建)
  • DELETE /subscriptions/{subscriptionId} Unsubscribe
  • PUT /subscriptions/{subscriptionId} Subscribe (中间修改)

这个PUT方法是我们首次在协议层面遇到的新内容,它实现了我们在第四章提到的“中间消费限额报告获取”,即对一个已存在订阅的修改操作。

1.3 5.5 Notifications (通知)

本节定义了由CHF主动发起的Notify操作在API层面的实现。

Table 5.5.1-1: Notifications overview

NotificationCallback URIHTTP methodDescription
Spending limit notification{notifUri}/notifyPOSTCounter Status changes notification.
Subscription Termination{notifUri}/terminatePOSTIndication of subscription termination.

这张表清晰地定义了两种通知:

  • 常规状态通知: CHF向{notifUri}/notify端点发起POST请求。
  • 订阅终止通知: CHF向{notifUri}/terminate端点发起POST请求。

通过在回调URI后附加不同的路径段(/notify/terminate),使得消费者(PCF)可以轻松地在同一个IP/端口上,为不同类型的通知设置不同的处理逻辑。


2. 解读第六章 Data Model - API的“血肉”

第六章定义了构成API交互信息体的所有JSON对象的精确结构。

2.1 Type: SpendingLimitContext - “订阅申请表”

这是POST /subscriptions(创建)和PUT /subscriptions/{subscriptionId}(修改)操作的请求体。

Table 5.6.2.2-1: Definition of type SpendingLimitContext

Attribute nameData typePCardinalityDescription
supiSupiC0..1The Subscription Permanent Identifier (Supi) shall be present…
gpsiGpsiO0..1The Generic Public Subscription Identifier (Gpsi)…
policyCounterIdsarray(PolicyCounterId)O1..NThis is a list of policy counter identifier(s)…
notifUriUriC0..1This attribute identifies the recipient of spending limit notifications…
notifIdstringC0..1Notification Correlation ID…
expiryDateTimeO0..1The expiry time may be included in a subscription request.
supportedFeaturesSupportedFeaturesC0..1The list of supported features…

场景链接:PCF为乐乐的直播业务发起订阅时,构造的SpendingLimitContext对象如下:

{
  "supi": "supi-of-lele",
  "policyCounterIds": ["LiveStream_Data_Counter", "Total_Data_Counter"],
  "notifUri": "http://pcf.operator.net/chf-notifications/lele-session-123"
}```
 
#### 2.2 Type: `SpendingLimitStatus` - “消费状态报告”
 
这是`POST /subscriptions`成功后的响应体,也是`Notify`(`POST .../notify`)的请求体。
 
> **Table 5.6.2.3-1: Definition of type SpendingLimitStatus**
 
| Attribute name | Data type | P | Cardinality | Description |
| :--- | :--- | :--- | :--- | :--- |
| **supi** | Supi | O | 0..1 | The Subscription Permanent Identifier (Supi) shall be present within the callback notify... |
| **notifId** | string | C | 0..1 | Notification Correlation ID... |
| **statusInfos** | map(PolicyCounterInfo) | C | 1..N | Status of the requested policy counters. The key of the map is... "policyCounterId". |
| **expiry** | DateTime | C | 0..1 | This expiry time shall be included in a subscription response... |
| **supportedFeatures** | SupportedFeatures | C | 0..1 | ... |
 
- **`statusInfos`**: 这是核心字段,一个以`policyCounterId`为键,`PolicyCounterInfo`对象为值的Map。
 
#### 2.3 Type: `PolicyCounterInfo` - 单个“计数器”的详细信息
 
这是`statusInfos` Map中的值,描述了一个计数器的完整状态。
 
> **Table 5.6.2.4-1: Definition of type PolicyCounterInfo**
 
| Attribute name | Data type | P | Cardinality | Description |
| :--- | :--- | :--- | :--- | :--- |
| **policyCounterId** | PolicyCounterId | M | 1 | Identifies the requested policy counter. |
| **currentStatus** | string | M | 1 | Identifies the policy counter status... |
| **penPolCounterStatuses** | array(PendingPolicyCounterStatus) | O | 1..N | Provides the pending policy counter status. |
 
- **`currentStatus`**: 当前状态标签,是一个字符串。
- **`penPolCounterStatuses`**: 待生效状态的数组。
 
#### 2.4 Type: `PendingPolicyCounterStatus` - “未来事件”的描述
 
> **Table 5.6.2.5-1: Definition of type PendingPolicyCounterStatus**
 
| Attribute name | Data type | P | Cardinality | Description |
| :--- | :--- | :--- | :--- | :--- |
| **policyCounterStatus** | string | M | 1 | Identifies the policy counter status... |
| **activationTime** | DateTime | M | 1 | Indicates the time at which the pending policy counter status becomes the current status... |
 
这个结构清晰地定义了一个“未来事件”:在`activationTime`这个时间点,计数器的状态将变为`policyCounterStatus`。
 
#### 2.5 Type: `SubscriptionTerminationInfo` - “订阅终止通知书”
 
这是由CHF发起的终止请求(`POST .../terminate`)的请求体。
 
> **Table 5.6.2.6-1: Definition of type SubscriptionTerminationInfo**
 
| Attribute name | Data type | P | Cardinality | Description |
| :--- | :--- | :--- | :--- | :--- |
| **supi** | Supi | M | 1 | Subscription Permanent Identifier. |
| **notifId** | string | C | 0..1 | Notification Correlation ID... |
| **termCause** | TerminationCause | O | 0..1 | Indicates the cause for requesting the termination... |
 
- **`termCause`**: 这是一个枚举,目前只定义了一个值 `REMOVED_SUBSCRIBER`,表示用户已从CHF系统中移除。
 
---
 
### 3. 全景复盘:一次完整的生命周期API之旅
 
现在,让我们将所有知识点串联起来,以API的视角,完整地走一遍乐乐观看直播的生命周期:
 
1.  **会话开始 - 订阅与初始状态获取**
    -   **PCF -> CHF**: `POST /subscriptions`
    -   **Request Body (`SpendingLimitContext`)**: 包含乐乐的`supi`,要监控的`policyCounterIds`,以及PCF的`notifUri`。
    -   **CHF -> PCF**: `201 Created`
    -   **Response Header**: `Location: /subscriptions/sub-lele-live`
    -   **Response Body (`SpendingLimitStatus`)**: 包含了`LiveStream_Data_Counter`和`Total_Data_Counter`的当前状态(`currentStatus: "VALID"`)。PCF据此下发高清QoS策略。
 
2.  **直播中 - 状态变化通知**
    -   乐乐的定向流量即将用尽,CHF内部`LiveStream_Data_Counter`状态变为`"FINAL"`。
    -   **CHF -> PCF**: `POST /chf-notifications/lele-session-123/notify`
    -   **Request Body (`SpendingLimitStatus`)**: 包含`supi`和`statusInfos`,其中`LiveStream_Data_Counter`的`currentStatus`为`"FINAL"`。
    -   **PCF -> CHF**: `204 No Content`
    -   PCF收到通知后,更新内部策略,准备在定向流量用尽后进行QoS降级。
 
3.  **会话结束 - 退订**
    -   乐乐关闭直播,PDU会话终止。
    -   **PCF -> CHF**: `DELETE /subscriptions/sub-lele-live`
    -   **CHF -> PCF**: `204 No Content`
    -   订阅关系解除,生命周期结束。
 
---
 
### 总结与宣告
 
通过对TS 29.594第五章和第六章的最终解读,我们已经将`Nchf_SpendingLimitControl`服务从抽象的逻辑功能,彻底解构为了具体、精确、可实现的API接口和数据模型。
 
1.  **简洁而强大的API**: 整个API只围绕`/subscriptions`这一个核心资源构建,通过`POST`, `PUT`, `DELETE`三种方法,就完整地实现了订阅的创建、修改和删除,设计极其凝练。
2.  **信息丰富的Data Model**: 以`SpendingLimitContext`和`SpendingLimitStatus`为核心的数据模型,字段设计精确,不仅能传递当前状态,更能预告未来变化,为PCF的智能决策提供了强大的数据支撑。
3.  **完整的生命周期闭环**: 从`POST`创建,到`POST`通知,再到`DELETE`销毁,整个服务的API交互流程形成了一个清晰、健壮的闭环,完美诠释了SBA的事件驱动架构思想。
 
**至此,我们对3GPP TS 29.594规范的系列深度解读已全部完成。** 我们从“话费保卫战”的宏大愿景出发,深入到了构成这场“战争”的每一条“电报”的编码细节。希望这个系列的文章,能为您在理解和实践5G策略与计费控制技术时,提供一份有力的参考和指引。
 
---
 
### FAQ
 
**Q1:为什么修改订阅要用`PUT`而不是`PATCH`?**
A1:在RESTful API设计中,`PUT`通常用于**全量替换**一个资源,而`PATCH`用于**部分更新**。TS 29.594选择了`PUT`,意味着PCF每次修改订阅时,都必须提供一个**完整**的`SpendingLimitContext`对象。例如,即使只是想延长过期时间,也必须在请求体中再次提供`supi`, `notifUri`和完整的`policyCounterIds`列表。这种设计的优点是简单、原子性强,服务器逻辑清晰;缺点是不够灵活,如果只想修改单个字段,传输的数据量会稍大。3GPP在这里选择了简单性和原子性。
 
**Q2:`SpendingLimitStatus`中的`statusInfos`字段为什么是一个Map而不是一个Array?**
A2:使用Map(在JSON中表现为对象)有几个好处:1)**快速查找**: PCF收到后,可以通过`statusInfos["LiveStream_Data_Counter"]`这样的方式,以O(1)的复杂度直接获取到它关心的计数器的信息,而无需遍历一个数组。2)**天然去重**: Map的键(key)必须是唯一的,这天然地保证了`policyCounterId`不会重复。3. **可扩展性**: 易于增删计数器信息。
 
**Q3:`policyCounterId`和`currentStatus`这两个字段的类型都是`string`,这意味着运营商可以随意命名吗?**
A3:是的。这就是“非标准化”的体现。运营商可以根据自己的计费系统和业务需求,自由定义计数器的ID(如`"Monthly_5G_Prime_Package"`)和状态标签(如`"Usage_Below_50_Percent"`)。唯一的约束是,这些自定义的字符串必须在PCF和CHF两端有共同的、一致的理解和配置。这种灵活性是该服务能够适应千差万别商业模式的关键。
 
**Q4:整个API交互中,安全性是如何保证的?**
A4:规范在第5.9章“Security”中有详细说明。PCF与CHF之间的所有通信都发生在受保护的核心网内部网络,并强制使用TLS进行传输层加密。此外,还可以启用OAuth 2.0进行应用层授权。PCF在首次访问CHF前,需要向NRF(作为授权服务器)申请一个Access Token,并在后续的每次API请求中携带这个Token。CHF会验证Token的有效性,确保只有经过授权的PCF实例才能访问用户的消费信息。
 
**Q5:这份规范的解读已经完成,接下来如果我想深入了解PCF是如何根据这些信息做出决策的,应该看哪个规范?**
A5:一个非常好的衔接是去阅读**3GPP TS 29.513** (`Npcf_SMPolicyControl Service API`)。这份规范定义了PCF与SMF之间的接口。你会看到,PCF从CHF收到的`SpendingLimitStatus`,会如何影响它在`TS 29.513`中下发给SMF的PCC规则(PCC Rule),例如修改QoS参数(QFI, ARP)、设置门控(Gate)状态、或者重定向流量等。将TS 29.594和TS 29.513结合起来,你就能构建一个从“消费感知”到“策略执行”的完整技术链路。