ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

在线计费系统OCS核心机制:从业务用量到批价入账的模板化设计

在线计费系统OCS核心机制:从业务用量到批价入账的模板化设计

简介:面向电信运营商业务支撑系统从业者与计费系统设计人员的一份技术文档,围绕OCS在线计费系统的计费原理与实现展开,适合需要理解实时计费流程、准备系统设计或排查计费逻辑的中高级读者参考。资源包内共1个doc文件,约1.87MB,内容按序篇、扫盲篇等模块组织,涵盖计费系统从离线批处理到实时计费的演进脉络,并对TAG、业务用量、费用项、产品、订购、入帐关系、模板、策略、事件与子事件等名词逐一解释,进而梳理预处理、批价等计费流程环节,以及计费引擎、数据库、消息中间件等实现组件与安全机制。已有297人学习,可帮助读者建立OCS核心概念框架,理清事件捕获、费用计算、账单生成到结算的完整链路,为优化计费流程与系统设计提供参考。

1. 在线计费系统:为什么“先扣钱再打电话”比“打完再算账”难十倍

很多人以为计费就是“通话时长乘以费率”,写个乘法而已。但真正让在线计费系统(OCS)门槛高到初学者望而却步的,恰恰是那个反直觉的约束:用户拨号的那一刻,系统还不知道这通电话会打多久,却必须立刻给出一个“你能打几分钟”的答案,并且这个答案要能随时被中断、被修正、被回滚。离线计费可以事后慢慢算,算错了下个月账单调整;在线计费没有后悔药,授权给多了就是欠费风险,授权给少了用户当场断线。这份资料把 OCS 从原始计费到在线计费的演进拆成四个时代,用“买矿泉水、买面包”这种生活比喻讲清了业务用量、费用项、定价计划这些抽象概念,再层层深入到呼叫、充值、月结三条典型流程和批价核心。搞懂它,你就明白为什么一个看似简单的扣费动作,背后要设计出模板、策略、事件、子事件这一整套抽象。

2. 计费系统的四次进化:从两张表到事件驱动

2.1 原始计费时代:反算时长解决欠费风险

最早的计费系统只有两张表:用户表存金额,费率表根据主被叫归属地和拜访地号码头查费率。它要解决的核心问题是预付费用户的欠费风险控制,用的办法叫“反算”或“预算”——通话前先根据余额算出可通话时长,把这个时长交给呼叫控制系统去监控,通话结束后再按实际时长扣费。这个思路很朴素,但它埋下了一个根本矛盾:余额和时长是一对多的关系,一个账户在同一时刻只能被一个业务“预定”,一旦用户同时要用多种业务,这套模型就撑不住了。资料里把这个阶段称为原始计费时代,对应的现实原型是早期智能网系统。理解这个起点很重要,因为后面所有的抽象,本质上都是在解决“一个账户如何被多个业务安全共享”这个问题。

2.2 简单计费时代:把费率从计费里抽出来

用户数一涨,号段一多,费率表的记录数就爆炸了。主被叫归属地、拜访地四个维度组合,每加一个号段就要配一遍到各地市的费率,局方改一次费率要分析一大片记录,血泪经验就是“改得越多错得越多”。这个阶段的改进是把查费率拆成三步:先做号码分析,把多个号段归一成地区号;再用计费矩阵把一次呼叫拆成基本费、漫游费、长途费三种消费类别,每种类别对应一个费率索引;最后费率表只描述费率本身,被计费矩阵引用。这样改费率只动费率表几条记录,加号段只动号码分析表。从机制上看,这是第一次把“识别消费类型”和“计算金额”解耦,计费矩阵里的 ChargeClass 就是后来业务用量的前身。这个设计动机很值得琢磨:可维护性不是靠文档解决的,是靠数据结构的分层解决的。

2.3 复杂计费时代:账户抽象与定期事务分离

运营商开始玩套餐了:消费积分、专款专销、充值分月赠送、越打越便宜。原来的单一余额模型不够用,于是做了三件事。第一,把计费系统一分为二:实时计费模块负责每次呼叫的费率计算,定期事务模块负责赠送和扣月租,每天后台跑。第二,把用户余额抽象成“账户”,可以定义多种账户类型,分别存放长途免费时长、市话免费时长、赠送金额、累计消费等。第三,把 ChargeClass 从纯费率索引扩展成“消费类别”,定义它只能在哪些账户上扣、入账优先级如何、费率分几档。这一步的关键设计是“入账关系”——消费明细到底该落到哪个账户,由消费类别预先声明,而不是在扣费时临时判断。资料里提到,这个阶段对应后期智能网的预付费业务系统,它已经能支撑专款专销和消费累积,但面对 3G 时代的多业务融合开始吃力。

2.4 OCS 计费时代:业务用量、事件与定价计划

3G 带来的问题是复合的:一卡多用要求同一账户被多个业务并发消费;计费请求的单位从时长扩展到条数、流量、直接扣款;套餐推出越来越快,原来要同时改两套系统的配置;预付费和后付费要融合。OCS 的解法是四层抽象。第一,把消费类别升级为“业务用量”,同时表达“消费的是什么业务”和“消费了多少量”,比如 20 分钟长途费、2M 流量。第二,把每种计费请求抽象成“计费事件”,影响计费的参量抽象成“计费要素”,新增业务种类只加代码不改老逻辑,修改是线性的。第三,把实时计费和定期事务的计费能力抽象成可复用的“模板”或“策略模板”,赠送内容、费率、适配条件都做成参数。第四,把每次优惠活动抽象成“定价计划”,定价计划调用多个模板,新增活动只是新增数据,通过“订购”关系只影响订购了的用户,测试时只给测试用户订购即可,不影响现网其他用户。这套设计的精髓在于:把“变”的部分收敛到数据配置,把“不变”的部分沉淀成模板代码。

提示:资料里反复强调可测试性和兼容性——新增定价计划是纯数据新增,不碰现网特性,这是电信级系统能快速推活动的底层保障。

3. 呼叫流程拆解:一次通话在 OCS 里到底走了几步

3.1 预处理、批价、入账、后处理四段式

OCS 处理一次呼叫计费,流程分成四段。预处理负责把原始计费请求翻译成系统内部能理解的结构,做校验、补全、事件识别;批价负责根据业务用量和定价计划算出费用项链表;入账负责按入账关系把费用项落到具体账户上扣减;后处理负责生成话单、通知外围系统。资料在入门篇刻意封装了预/后处理的细节,只讲输入输出,这是很聪明的教学取舍——初学者如果一头扎进预处理的状态机,很容易出不来。但从机制上必须知道:批价是纯计算,不碰账户余额;入账是纯扣减,不做费率判断。这个职责边界划清楚,后面排查问题时才能快速定位是“算错了”还是“扣错了”。常见的实现思路是,预处理阶段把 TAG 变量(可以理解为老系统中的 CID,只是 CHG 侧习惯用数值标识,比如 TAG 1080 存呼叫时长)收集齐,批价阶段只读这些变量和定价计划数据。

3.2 计费能力集层与实例化资费数据层

呼叫流程里有两个层次需要区分:计费能力集层和实例化资费数据层。计费能力集层是模板和策略所在的地方,它定义“这类计费能做哪些事”,比如呼叫计费模板支持按业务用量计费、支持分档费率、支持免费资源抵扣。实例化资费数据层是具体定价计划的数据,比如“省内漫游 1 毛一分钟”这种参数。批价时,系统先根据事件和用户订购关系找到定价计划,定价计划再调用对应的模板,模板拿着参数去执行。这个分层的好处是,新增一个套餐不需要改模板代码,只需要在数据层新增一条定价计划并让用户订购。资料里广东 OCS 的实现把主叫呼叫子流程划分了五个业务用量:基本费时长、长途费时长、漫游费时长、被叫漫游长途时长、附加费业务量,每个用量有固定的 TAG 编号。这种“用量类别预先规划好”的做法,和智能网 ChargeClass 的思路一脉相承,但表达力更强,因为它带上了量的维度。

3.3 免费资源处理与累计项处理

免费资源是套餐计费里最容易翻车的地方。用户订购了“500 分钟长途免费时长”,一次长途呼叫 300 秒,批价时不能直接按费率算钱,而要先去免费资源池里抵扣。机制上,免费资源在账户体系里表现为一种特殊账户类型,批价输出的费用项会带上“可从哪些账户扣、优先级如何”的信息,入账时按优先级依次扣减。累计项处理则是另一条线:消费积分、累计消费金额这类数据不直接参与本次扣费,但要在批价或入账后更新,供后续的赠送模板或等级判断使用。这里的设计取舍是:累计项更新放在入账之后而不是批价之中,因为批价可能因为授权调整而重算,累计项如果跟着批价走就会重复累加。我一般会这么理解:批价是“算这次该收多少”,累计是“记下用户又消费了多少”,两者生命周期不同,必须分开。

3.4 充值流程与月结流程的模板复用

充值流程和呼叫流程共用同一套模板机制。充值的预处理负责解析充值请求、识别充值事件,模板说明部分定义赠送规则,比如充 100 送 40 分几个月赠送。月结流程则是定期事务的典型:后台按周期跑,处理月租扣取、分月赠送、回赠等动作。资料里把月结和回赠单独拿出来讲,是因为它们暴露了 OCS 的一个关键能力——定时触发的计费事件和用户触发的计费事件走的是同一套批价入账逻辑,只是事件来源不同。这种统一性带来的好处是,预付费和后付费在批价入账层面完全一致,差异只在支付方式和后续的账单打印、催缴流程。从实现角度看,这意味着模板不能假设自己一定在呼叫上下文中执行,参数传递要足够通用。

4. 批价核心:业务识别与费用计算 API

4.1 业务识别:从原始请求到计费事件

批价的第一步不是算钱,是识别“这到底是个什么事件”。原始计费请求里可能只有一些号码、时长、流量之类的原始字段,业务识别要判断这是本地呼叫还是漫游呼叫、是长途还是市话、是语音还是视频、有没有叠加增值业务。资料在提高篇里把业务识别单独拎出来,说明它是批价准确性的前提。常见的实现思路是,业务识别依赖预处理阶段准备好的 TAG 变量和号码分析结果,按事件类型加载不同的识别规则。这里有个容易踩的坑:同一个呼叫可能同时命中多个业务用量,比如主叫漫游打长途,基本费、长途费、漫游费三个用量都要算,业务识别不能做成互斥判断,而要做成叠加判断。资料里广东版本的例子就是三个用量各 300 秒,费用项链表里三条记录分别对应本地通话、长途通话、漫游通话三个费用项。

4.2 批价处理:费用项链表的生成逻辑

批价处理的核心输出是费用项链表。每个费用项记录业务用量、数值、金额和费用项类型。用资料里的例子:主叫漫游拨打被叫长途 5 分钟,市话费率 1 毛一分钟、长途 2 毛、漫游 3 毛,那么费用项链表里基本费时长 300 秒对应 50 分、长途费时长 300 秒对应 100 分、漫游费时长 300 秒对应 150 分。这个链表结构的设计动机很直接:一次计费可能产生多种消费,用链表可以动态扩展,入账时逐条处理。批价过程中还要处理分档费率,比如头 3 分钟 4 毛一分钟、后 10 分钟 2 毛、10 分钟后 1 毛,这要求批价能对同一个业务用量做分段计算。资料提到费率表从单一费率扩展成多个分档,正是为这个服务的。从源码级角度看,批价函数通常接收事件对象和定价计划,返回费用项链表,内部按用量逐段匹配费率档位,累加金额。

# 批价核心逻辑的示意性伪代码,非资料原文 def rate_event(event, pricing_plan): fee_items = [] for usage in event.usages: # 遍历本次事件的所有业务用量 if usage.value <= 0: continue # 量为 0 的用量不产生费用项 template = pricing_plan.match_template(usage.type) # 按用量类型匹配模板 segments = template.split_by_tier(usage.value) # 按分档切分用量 amount = 0 for seg in segments: rate = template.get_rate(seg.tier) # 取该档费率 amount += rate.calc(seg.duration) # 分段计费累加 fee_items.append(FeeItem(usage.type, usage.value, amount)) return fee_items # 返回费用项链表,供入账使用

这段伪代码里几个参数值得说明:usage.type决定匹配哪个模板,split_by_tier负责把总用量按费率档位切开,rate.calc里封装了按秒还是按分钟计费的细节。真实实现里还要处理免费资源抵扣和封顶规则,但主干就是这个循环。理解了这个循环,再看资料里“费用项是对每一条消费的归纳”这句话就具体了——费用项不是拍脑袋设计的,是批价循环的自然输出。

4.3 费用计算 API:模板与数据的分界线

资料在提高篇最后提到费用计算 API,这是模板暴露给定价计划的调用接口。定价计划是数据,模板是代码,两者之间靠 API 契约连接。API 的入参通常包括业务用量、费率参数、适配条件,出参是费用项或授权结果。这个设计的关键取舍是:API 要足够稳定,因为定价计划数据可能成千上万条,不能因为模板升级就全部重配;同时 API 要足够灵活,能表达分档、免费资源、封顶这些常见需求。常见的实现思路是把费率计算做成可组合的算子,定价计划通过参数选择算子组合,而不是为每种套餐写一段代码。这样新增套餐是配置工作,不是开发工作,才谈得上快速推出活动。

5. 避坑与排查:OCS 计费里那些容易翻车的地方

5.1 授权时长算多了导致欠费

现象:预付费用户余额不多,但系统授权了过长的通话时间,通话结束后账户被扣成负数。原因:反算授权时只考虑了单一费率,没有把漫游费、长途费叠加进去,或者免费资源抵扣没有参与授权计算。解决:授权计算必须走和批价一致的业务识别和费率匹配逻辑,不能简化;授权结果要留出安全余量,并在通话过程中支持中途重授权。

5.2 费用项重复入账

现象:一次呼叫结束后,账户被扣了两次相同金额。原因:批价和入账的职责边界被打破,批价阶段直接改了账户余额,入账阶段又扣了一遍;或者重试机制没有做幂等。解决:严格保持批价只算不扣、入账只扣不算;入账接口用事件标识做幂等键,重复请求直接返回上次结果。

5.3 免费资源抵扣顺序错乱

现象:用户有基本余额和长途免费时长,打长途时先扣了基本余额,免费时长没动。原因:入账关系里账户优先级配错,或者费用项没有正确声明可扣账户列表。解决:检查消费类别到账户的入账关系配置,确认优先级顺序;批价输出的费用项要带上可扣账户范围,入账时按优先级逐级扣减。

5.4 新增套餐影响现网用户

现象:上线一个新定价计划后,部分没订购该套餐的用户费率变了。原因:定价计划的适配条件写得太宽,或者模板参数被多个计划共享修改。解决:新增活动坚持“新增定价计划加订购关系”的方式,不改老计划数据;测试时只给测试用户订购,验证不影响其他用户后再批量放号。

5.5 累计项重复累加

现象:用户消费积分比实际消费多出一截。原因:批价重算或授权调整时,累计项跟着批价走了多次。解决:累计项更新放在入账成功之后,并用事件标识去重;批价阶段只读累计项做判断,不写累计项。

6. 设计取舍:为什么 OCS 选择“模板加数据”而不是“一需求一代码”

回头看 OCS 的四次进化,每一次都是在回答同一个问题:变化来了,改哪里。原始时代改费率表,简单时代把费率抽出来,复杂时代把账户和消费类别抽出来,OCS 时代把计费能力抽成模板、把套餐抽成定价计划。这条路径背后的取舍很清晰:电信业务的套餐变化频率远高于计费逻辑本身的变化频率,所以要把易变的部分推到数据层,把稳定的部分沉到代码层。代价是抽象层次多了,初学者要跨过 TAG、业务用量、费用项、定价计划、模板、策略这一串概念才能看懂一次扣费,门槛自然高。资料在序篇里坦言 OCS 概念抽象、细节暴露太多、版本里揉成一团,正是这个代价的体现。

验证这套设计是否真的 work,有个很实用的方法:拿一个新套餐需求,看它能不能只靠新增定价计划和订购关系实现,不碰模板代码。如果能,说明抽象是有效的;如果每次都要改模板,说明模板的 API 契约没设计好,参数表达能力不够。另一个验证点是故障隔离:新套餐上线出问题时,能不能只回退订购关系而不影响其他用户。这两个检查点,比看多少架构图都管用。

进阶方向可以往批价的性能与一致性走:批价是纯计算,天然适合并行,但入账要保证账户余额的强一致,两者之间的边界怎么划、授权和实际扣费之间的差额怎么冲正,都是深水区。资料里提到的请求业务用量、授权业务用量、已使用业务用量三分法,其实就是为这个边界准备的——预付费先请求、拿授权、用完后按已使用量批价入账,差额自然冲正。后付费没有授权环节,已使用量直接批价入账,两条路径在批价入账层统一。

从那以后我每次看计费类系统,都强制自己先问一句:这个字段是“算的时候用”还是“扣的时候用”,两者混在一起的地方迟早出问题。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进