ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

CTG-MBOSS 对接实战:Spring Boot 鉴权、幂等与对账

2026/9/18 3:14:29 拓冰建站 浏览量
CTG-MBOSS 对接实战:Spring Boot 鉴权、幂等与对账 简介这是一份关于中国电信CTG-MBOSS九七改造规范编写历程的纪实PDF面向电信BSS/OSS从业者、系统集成商技术人员及对电信行业信息化演进感兴趣的读者。文档以亲历者视角回顾2004年前后中国电信启动规范编写的完整脉络从前期厂商交流、Accenture项目组组建、人员笔试面试甄选到编写组入驻北京顺义望潮苑度假村的封闭工作模式均有细致记述。资源包仅1个PDF文件约25KB体量轻便适合碎片时间通读。正文涉及PMO、总体架构、BSS、OSS、技术架构与业务专家组的分工架构CRM与OSS功能规范、系统技术架构规范、数据模型、系统集成规范、测试方法及实施指南等交付件还记录了“先理解后挑战”“一天三看”等工作方法论以及概念模型大讨论中对商品等实体定义的争鸣。已有298人学习浏览可帮助读者理解电信BSS/OSS规范体系的来龙去脉与项目组织经验对参与大型IT规范编写或电信支撑系统建设的人员具有参考价值。1. 一份亲历 CTG-MBOSS的文档真正记的是什么第一次拿到省级公司下发的对接资料往往是一个压缩包总体架构一份、数据模型一份、接口规范一份、几十个 Excel 样例报文没人告诉你先看哪一份。三个月后你要负责把这套东西接进自己的系统这才是 CTG-MBOSS 落到一线工程师手上的真实样子。CTG-MBOSS 是中国电信企业级 IT 架构的框架性规范用统一的架构、数据、接口语言把业务支撑、运营支撑、管理支撑三条线约束在同一套话语体系里。所谓亲历记录的不是某次发布会而是读规范、写适配、调接口、跑对账、扛出账的那几个月。要对接省级 CRM、计费、开通系统的乙方开发做数据中台要抽电信侧指标的数据工程师以及接手存量 BOSS 侧系统运维的人都绕不开这些内容。2. CTG-MBOSS 三域划分与接口规范BSS/OSS/MSS 在工程里各管什么2.1 三域划分与常见系统清单CTG-MBOSS 的骨架是三域 横向数据方法论上参考了 eTOM 和 NGOSS 那一套但落地到各省公司系统清单和边界都不完全一样。判断一个接口属于哪个域看的是它服务的业务动作而不是服务器放在哪个机房。BSS 面向客户与收入订单、订购、计费、账务、结算都在这条线上OSS 面向网络与资源服务开通、资源分配、故障工单、性能告警走这条线MSS 面向企业内部管理组织、人员、财务、采购属于这里。横向的 EDA 层负责把三域的数据抽出来做经分、报表和指标。一线最常见的接口形态如下表。域典型系统对外接口主要用途常见接口形态BSSCRM、订单中心、计费、账务、结算客户资料查询、订购受理、缴费、账单查询WebService(SOAP)、HTTPXMLOSS服务开通、资源管理、故障工单、综合网管开通请求、资源查询、工单派发与回单HTTPXML、文件接口MSSOA、人力、财务、采购组织人员同步、费用报账文件接口、库表同步EDA经分、数据仓库、指标平台明细抽取、指标取数文件接口、数据库只读账号要点在于真正决定你怎么写代码的不是域而是系统编码 服务编码这两个字段。同一域内不同系统的报文风格可能完全不同别拿 A 系统的经验直接套 B 系统。2.2 接口规范里绕不开的报文五要素无论 SOAP 还是 HTTPXML规范里几乎都会定义一层报文头有的叫 msgHeader有的叫 head有的直接平铺。这层头里通常有五个字段决定你能否调通也是联调期报错最集中的地方。字段常见叫法作用典型长度排错时先看什么sysCode / sourceSys标识调用方对方用来做权限和限流4~16是否在对方白名单里大小写是否一致serviceCode服务编码一个编码对应一份入参出参定义8~32是否与规范文档逐字一致别用中文全角transactionId请求流水重发时必须保持一致32~64是否全局唯一重复会被判重timestamp时间戳一般用于防重放14格式 yyyyMMddHHmmss机器时间偏差过大直接被拒sign签名多为 MD5 或 HMAC 摘要32~64拼接顺序、密钥、大小写三者任一错就签名不过提示签名失败是最常见的第一堵墙。规范文档里通常有一句话描述拼接规则比如报文体 时间戳 密钥顺序不能改。写完先用规范里给的样例报文自测一遍样例能过再上真实请求。2.3 用一条 curl 先摸清对方接口的脾气联调第一天不要急着写 Java 工程。先用 curl 发一条最简单、无业务含义的查询把网络、路径、鉴权三层一次验证掉比在 IDE 里断点调试快得多。# 最小探测只验证连通性、路径、鉴权不关心业务返回内容 curl -sS -X POST https://boss-api.example.com/bss/custQuery \ -H Content-Type: text/xml; charsetUTF-8 \ -H SOAPAction: queryCustomer \ --data-binary req.xml \ --connect-timeout 3 \ --max-time 10 \ -o resp.xml \ -w http%{http_code} total%{time_total}s size%{size_download}\n # 先看整体结构再决定要不要深挖字段 head -c 500 resp.xml这几个参数各有分工。--connect-timeout 3卡的是 TCP 握手超时基本是网络或防火墙策略问题--max-time 10卡的是整个请求超时往往是对方后端在排队或数据库慢查询-o把响应落文件避免终端把超长 XML 截断-w输出状态码和耗时方便事后比对哪一次变慢了。如果返回的是 HTML 而不是 XML多半是网关拦截或路径写错跟业务逻辑无关。3. 用 Spring Boot 对接 CTG-MBOSS 接口鉴权、幂等与超时重试的最小实现3.1 报文封装业务字段与报文头分离把报文头和业务体拆成两个对象是这套系统里最容易坚持下来的一个习惯。报文头由框架统一填充和校验业务体由各个服务自己定义出问题时能一眼看出是公共层还是业务层的问题。public class BossMsgHeader { private String sysCode; // 接入系统编码由省公司分配 private String serviceCode; // 服务编码与规范文档一一对应 private String transactionId; // 全局唯一重发时必须沿用原值 private String timestamp; // yyyyMMddHHmmss超过 5 分钟一般判失效 private String sign; // 拼接规则以对方规范为准不可自行调整 // getter / setter 略 } public final class BossSign { public static String md5Upper(String body, String ts, String secret) { String raw body ts secret; // 顺序错一位签名结果完全不同 try { MessageDigest md MessageDigest.getInstance(MD5); byte[] d md.digest(raw.getBytes(StandardCharsets.UTF_8)); StringBuilder sb new StringBuilder(); for (byte b : d) { sb.append(String.format(%02X, b 0xFF)); // 大小写按规范别想当然 } return sb.toString(); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException(MD5 算法不可用, e); } } }参数上有两个坑值得单独说。第一是字符集对方按 GBK 算签名、你按 UTF-8 算只要报文里出现中文就必崩联调前先在规范里确认字符集。第二是签名与报文体必须严格对应很多团队为了好看在发送前重新格式化了 XML缩进和换行一改摘要就对不上所以签名应当在报文序列化之后、发送之前算。3.2 幂等表设计与重复报文处理超时重发在电信侧几乎是常态你超时了、对方其实处理成功了重发一次就可能变成重复订购。幂等兜底不要放在内存里落一张带唯一约束的表是最稳的做法。CREATE TABLE t_boss_idempotent ( transaction_id VARCHAR2(64) NOT NULL, -- 对方流水号作为幂等键 service_code VARCHAR2(32) NOT NULL, -- 同流水可能对应不同服务 req_digest VARCHAR2(64), -- 请求体摘要拦截同流水不同内容 status CHAR(1) DEFAULT P, -- P 处理中 / S 成功 / F 失败 resp_body CLOB, -- 成功响应落库重复请求直接回放 create_time DATE DEFAULT SYSDATE, update_time DATE, CONSTRAINT pk_boss_idem PRIMARY KEY (transaction_id, service_code) );处理顺序是先插入再干活。插入成功说明是首次请求继续走业务抛唯一键冲突说明是重发此时比对req_digest一致就读出resp_body直接返回不一致说明对方用了同一个流水发了不同内容属于异常应该落告警而不是硬跑业务。业务处理完成后把status更新为 S 并写入响应失败则置 F 并记录原因方便人工重推。3.3 超时、重试与补偿的三个参数三个参数决定这套对接在高峰期是稳还是崩建议在编码前就定下来并写进配置中心而不是散落在各个RestTemplate里。参数建议起点为什么这么设设大的副作用连接超时3 秒内网调用握手超过 3 秒基本是网络层异常故障暴露变慢线程被拖住读超时10~20 秒要覆盖对方出账期的慢查询但别到分钟级高峰期线程池打满重试次数1 次且仅超时重试业务失败重试无意义只对超时做补偿重复请求放大对方压力注意重试必须复用原transactionId否则幂等表形同虚设。同理HTTP 5xx 可以重试业务返回码为参数错误客户不存在这类确定性失败时不要重试。4. 从订购实例到出账CTG-MBOSS 数据链路的 SQL 追踪法4.1 关键实体与主键关系数据模型部分看着厚真正天天用的就那么几张表。电信侧常见命名是TF_交易类和TB_资料类打头但各省不完全统一动手前先查一次数据字典别凭经验猜表名。实体常见表名模式主键主要外键主订单TF_B_ORDERorder_idcust_id订单项TF_B_ORDER_ITEMorder_item_idorder_id、offer_id用户实例TF_F_INST_SUBSCRIBERsubs_idinst_id、cust_id账户TF_F_ACCOUNTacct_idcust_id账单TF_F_BILLbill_idsubs_id、bill_cycle4.2 追一条订购实例的完整链路用户投诉办完业务没出账最有效的办法是拿订单号一路串下去看链路断在哪一环而不是先怀疑计费程序。-- 以订单号为锚点串起订单项 - 用户实例 - 账户 - 当期账单 SELECT o.order_id, o.create_time, o.order_state, i.order_item_id, i.offer_id, i.inst_id, s.subs_id, s.serv_number, s.state AS subs_state, a.acct_id, a.cust_id, b.bill_id, b.charge FROM tf_b_order o JOIN tf_b_order_item i ON i.order_id o.order_id LEFT JOIN tf_f_inst_subscriber s ON s.inst_id i.inst_id LEFT JOIN tf_f_account a ON a.cust_id s.cust_id LEFT JOIN tf_f_bill b ON b.subs_id s.subs_id AND b.bill_cycle :cycle WHERE o.order_id :orderId;读结果的方法比写 SQL 更重要。如果order_state就是失败或作废问题在受理侧不用往下查如果实例行给出来了但状态是未激活问题在开通环节去看 OSS 回单如果实例状态正常却没有账单行才轮到计费侧重点查bill_cycle是不是取错月份、账目类型是否被过滤。把这条链路固化成脚本比每次临时拼 SQL 快得多。4.3 出账前的对账差异定位出账前跑一次三方数量对账能把大部分事故拦在账单生成之前受理侧成功订单数、实例侧已激活用户数、计费侧已出账数三者应当能对上。-- 按受理日聚合定位差异集中在哪一天、哪一类业务 SELECT TRUNC(o.create_time) AS order_date, i.offer_id, COUNT(DISTINCT o.order_id) AS ok_order_cnt, COUNT(DISTINCT s.subs_id) AS active_subs_cnt, COUNT(DISTINCT b.bill_id) AS billed_cnt FROM tf_b_order o JOIN tf_b_order_item i ON i.order_id o.order_id LEFT JOIN tf_f_inst_subscriber s ON s.inst_id i.inst_id AND s.state A LEFT JOIN tf_f_bill b ON b.subs_id s.subs_id AND b.bill_cycle :cycle WHERE o.order_state S -- 只统计成功订单 AND o.create_time TRUNC(SYSDATE) - 3 -- 只看近三天避免全表扫 GROUP BY TRUNC(o.create_time), i.offer_id HAVING COUNT(DISTINCT o.order_id) COUNT(DISTINCT b.bill_id);HAVING那行是核心只把对不上的分组捞出来。差异通常集中在两类一是跨零点受理、订单日期和账单周期错位一天二是批量订购场景下部分实例激活延迟出账快照没赶上。前者是口径问题后者补一次增量出账就能收敛。5. 联调上线后的进阶技巧留痕、压测与差异回捞5.1 用 traceId 把日志和报文串起来上线之后最痛苦的不是报错而是对方说发了、我们说没收到。稳妥做法是统一落一张报文流水表请求和响应各存原始报文同时把transactionId塞进日志的 MDC让应用日志、网关日志、Nginx 日志能按同一个值串起来。存原始报文要顺手做脱敏身份证、手机号、银行卡号按前 3 后 4 保留其他位掩码别把生产数据原样拷到测试环境。// 在过滤器里一次性写入后续所有日志自动带流水号 MDC.put(txId, header.getTransactionId()); try { chain.doFilter(req, resp); } finally { MDC.remove(txId); // 线程池复用务必清理否则会串流水 }MDC.remove这一句经常被漏掉。线程池里的线程会被复用不清就会让下一次请求的日志带上别人的流水号排查时能把人带进沟里。5.2 压测要按真实业务比例造数拿同一个服务刷一万次并发没有意义对方的风控和限流会先把你拦下来。更接近真实的是按线上报文的比例混合构造七成查询、两成订购、一成变更且订购类报文必须使用互不重复的transactionId否则全被幂等表挡掉压出来的 TPS 是假的。压测前先和对方确认并发上限和限流阈值把测试环境的触发值调低再跑别直接对生产网关下手。5.3 差异回捞任务怎么写才不重复补补偿任务的关键是可重入。建议单独建一张差异表记录订单号、差异类型、发现时间、重试次数、处理状态每次回捞先查这张表把已处理成功的排除掉重试次数超过阈值一般 3 次的转人工工单不再自动重推。补偿的幂等同样依赖transactionId复用也就是说补发时必须从差异表里读出原始流水而不是新生成一个。这条规则看起来啰嗦但它决定了你的对账是收敛还是越对越乱。本文还有配套的精品资源点击获取