0执行摘要
销项系统不是"代码写得差",而是失去了可验证的真相源——改一张表算不出炸到谁,改一个接口答不出谁在调,发一次补丁确定不了覆盖了哪些副本。重构的第一目标不是拆服务,而是把今天不可能的操作变为可能。
最致命的三件事
| # | 事实 | 为什么致命 | 证据 |
|---|---|---|---|
| 1 | 开票主链路有 4 个独立写入方,而幂等键已确证缺失。 inv_seller_make_out_request(7 生产副本)、inv_seller_pre_invoice(9 副本) 零唯一索引;inv_seller_invoice 的唯一索引是「发票号码+代码+删除标志」事后约束,不是请求级幂等键。主服务另有 9 处把跨服务调用嵌在 @Transactional 里(1 处吞异常) |
开票状态机的一致性不由任何单一服务保证。系统已把"疑似成功重试"与"金额统计订正"当作常态运维手段——这本身就是主流程一致性不可靠的自证。重复开票的结构性风险今天就存在,不是重构引入的 | evidence/03 §3.3 code-1 §3.4/§4.4 verify/facts §2 |
| 2 | 系统的真相既不在代码里,也不完整地在配置里。 调用拓扑真值在 Apollo Ribbon listOfServers,但它只覆盖 47.6%(42 个 Feign 目标名中 20 个);PROD 另跑 Eureka,还有 8 处硬编码 URL、36 个 RestTemplate 点。68% 的定时任务(139/204)调度真相只在 XXL-Job 控制台;53% 的 MQ 发送点通道名运行时才确定;前后端契约可验证率 1.5% |
任何改动的爆炸半径都算不出来,也无法证明"我没切断谁"。 这不是缺文档,是缺一个可验证的真相源——它使任何重构方案在今天都不具备验收条件 | evidence/02 §4.4 evidence/04 §4.4 verify/facts §3 |
| 3 | 私有化比 SaaS 主站大 2.92 倍,且四分之一的副本溯源已断。 私有化 5 个 ns 合计 435 workload / 595 副本,SaaS 三环境 149/217;118 workload / 146 副本源码已从 GitLab 消失;27 条租户定制分支承载 109 副本;华润有 3 套独立销项库实例(含已 EOL 的 MySQL 5.6);反向污染已发生——20-phoenix-fat 跑着 release-huarun 的产物 |
"在主干修一次,全网生效"是伪命题。 不把租户路径写进方案,主线的一切成果都会在租户侧被永久稀释 | evidence/06 §1/§2.3-2.5 verify/executable 断言 9 |
建议做什么
采用「绞杀者渐进式重构」为骨架(三视角加权 8.10,见 design/scorecard.md):不拆、不重写、不停业;用系统已有的间接层插入双向门面截流,此后每一刀走「影子读 → 双写 → 切读 → 收写 → 拆旧」五步,每步有明确的回滚动作、回滚时间、数据代价与审批人。定点嫁接方案三的零代码止血包、六个 lock 文件与 30 个可 SQL 复跑的度量指标,以及方案一的领域语义判断与 CI 三门禁。
listOfServers 零代码截流只覆盖约 13/45;幂等键已确证缺失须升级为独立前置工程;抬版 2.7.x 与门面机制互斥。代价与收益
| 工期 | 30 个月(6 阶段,刻意重叠)+ 幂等前置 3~6 月进基线。可在任一阶段止损,且任一刀完成后停止,系统都比之前更简单,不留双轨 |
|---|---|
| 人力 | 常驻 12 FTE(后端 6 · DBA/数据 2 · SRE/平台 2 · 前端 1 · QA/契约 1),峰值 14。累计 ≈387 人月。工期人力估算置信度 low 证据支撑"做什么、什么顺序、怎么验收",不支撑"要多久" |
| 可验收收益 | 公网无 WAF host 15 → 0;公网可达 /ms 端点 638 → 0;核心表写入方 4 → 1;phoenix-script-invoice 写表 231 → 0;销项持有进项库连接 4 → 0;prod workload 45 → ≤30;逐字镜像对 163 → 0;有 OpenAPI 的组件 8/34 → 30/34;调度可见率 20.6% → 100%;事务内远程调用 9 → 0;主库剥离 ≥1.4 TB |
| 不承诺 | 不承诺业务吞吐、响应时延或研发交付速度的直接提升 |
| 最大代价 | 并行双轨期的运营成本:双写、影子读、回滚窗口、双份环境,且 30 个月里业务需求不会停。若组织只能投入 6 个月,只做 S0 + 治理刀 + 租户横切止血(约 5 个月),完成后系统严格优于现状且无双轨残留——不要启动 S2 之后的任何一刀 |
1核验修订总表
草案的 12 条关键断言,交由两路独立 refuter 对抗性核验(事实与遗漏 / 可执行性与风险),共 44 次 ontoos-probe 实跑。规则是默认判否,只有亲自查到正面证据才翻案。结果:10 条被推翻,2 条站得住。这不是形式化意见——其中 4 条直接改变了第一刀的形态与路线图结构。
| # | 草案断言 | 判定 | 核验实测与定稿处置 |
|---|---|---|---|
| 1 | 核心表多写方,写权收敛先于表族搬迁 | 口径推翻 | writer=2 实测 73 张不是 44 张(LIMIT 60 截断);原查询按表名聚合、无库限定,而 GRANT 作用于 db.table → ownership.lock 主键改为 (实例, schema, 表)。动作保留,口径重定 |
| 2 | 开票主链路幂等键从未被验证 | 推翻 | 该问题当场就能查清,答案是否定的:inv_seller_make_out_request(7 副本)、inv_seller_pre_invoice(9 副本) 零唯一索引 → P2 立即改判 FALSE,新增 S0.5 幂等前置工程,工期 +3~6 月进基线 |
| 3 | listOfServers 是东西向唯一地址来源 | 推翻 | 42 个 Feign 目标名中仅 20 个(47.6%)有 Apollo 键;PROD 另配 Eureka;8 处硬编码 URL;36 个 RestTemplate 点 → 纯配置可截流上限约 13/45;L0 拆为 L0-a(热生效)/L0-b(需重启) |
| 4 | 各服务独立 DB 账号 → 可做表级 GRANT 收权 | 双重推翻 | 本体有 code_datasources.username(草案称未覆盖);账号是按库不按服务——v4_sellinv_prod 被 4 个 workload 跨 2 个 ns 共用,phoenix-openapi 与 phoenix-seller-invoice 同账号 → S1 步④ REVOKE 必须撤回,新增 S1-b 账号拆分工程 |
| 5 | phoenix-openapi 不在开票同步主链路上 | 推论推翻 | 字面成立,但它自带开票写入口(bizorder/task/upload-and-issue、invoice/action/generate-redletter-preinvoice、invoice/action/reverse,还有 prod 上的 mock 回调端点)——它是一条独立并行的开票链路,切错即漏票;端点数 290 → 194(斜杠伪影),连带 3701 → ≤3400 |
| 6 | phoenix-seller-open-api 能承接 phoenix-openapi | 推翻 | 两者路径命名空间几乎不相交(/st/+/{tenantId}/ vs /open/+/bizorder/);openapi 194 个里 46 个是进项端点,新服务只有 2 个;且替代品 prod 读的是另一台未纳管 PolarDB 的 bill 库、账号 v4_bill_dvlp → 第一刀改为「薄壳保契约」,不做"迁移到新服务" |
| 7 | /ms 平面存在等价应用层鉴权 | 两路分歧 | 真相是按 cluster 分裂:xplat-common-config SAAS/default token.decode.enable=true,CRC/CCAG = false → SaaS 侧风险降级、私有化侧升级。更关键:164/212 Feign client 走 /ms 且 SAAS 下解析到公网域名,原定的「/ms 公网 deny」会切断东西向 → 拆两步执行 |
| 8 | CDC 无契约、改表即静默断流 | 站得住 | 逐条复跑成立,但计数偏低:触达销项数据的 canal 实测 16 个(草案 11),且大量用 库.* 通配 → 失败模式相反:新增列静默外泄而非断流;4 条零可见消费者的 topic 补证前不得动 |
| 9 | 私有化是主体,25.9% 已不可改 | 站得住 | 规模上修:私有化 435 wl / 595 副本 vs SaaS 149/217(2.92×);华润 3 套独立销项库实例(含 MySQL 5.6)比断言更严重。但"源码已消失"降级为"溯源待恢复"——多个失联仓在其它 group 同名在世 |
| 10 | 租户 cluster 机制已在产 → 华润 A 档可行 | 推翻 | 用错了平面:私有化租户各自跑自己的 Apollo(94-local-crc-prod = x-apollo-v1-36905-*),中央 Apollo 的 CRC/CCAG cluster 不是华润读的配置面;SAAS↔CRC 单 app 252 处分叉且 CRC PROD 值已污染 → 降级为「归因可做、归并待证」 |
| 11 | 「开票状态」三胞胎 = 三个概念压进一个字段 | 根因判错 | 枚举原文显示三者是同一概念(单据开票覆盖度)的三份复制,码 0/1/2/4/5/6/7 语义一致,仅码 3 文案漂移 → "拆成三概念"不解决冲突;改为「统一为一份规范码 + 有日落期的翻译层」,日落判据改为 CI 可查三条 |
| 12 | 不做 SB 3.x 跨代抬版,目标 2.7.x | 内部矛盾 | 前半成立,后半与方案自身冲突:Spring Cloud 2020.0 起 Ribbon 已被移除,抬到 2.7.x 即废掉 listOfServers——而 S1 门面与 L0 回滚杠杆正建立在它上面(实测 137 个 PROD 键、9 个服务在跑 ribbon 2.0.1),且 S1/S2 窗口重叠 → S0 内必须决策 |
2方案完全未覆盖的在产资产
| 资产 | 实测事实 | 对方案的影响 |
|---|---|---|
| sa-unify-prod / unify-maintenance-app 2 副本 |
连 sellerinvoice(ds0/ds1)、bill、bill_shard、rednotification——与 20-phoenix-prod 完全相同的生产实例;MyBatis 实测对 inv_seller_invoice / inv_seller_pre_invoice / ord_salesbill 均为 read,write;账号 bill_shard_prod 与 phoenix-bill 相同 |
核心表真实写入方是 4 个不是 3 个 → S1/S4 的写方收敛指标(3→2→1)不可达;S2 逐表 REVOKE 会打掉一个无人知晓的生产维护系统;服务去向表缺一行。同族还有 athena-maintenance-project、athena-maintenance-walmart 各 2 副本 |
| xf-coop-ant-coop-prod / ant-coop-system-service 2 副本 · 活跃迭代 |
分支 release-20260519(4 个月前);持 v4_sellinv_prod 连 sellerinvoice——与 phoenix-seller-invoice 同账号同实例同库;无 MyBatis 记录 ≠ 不写(可能走 JPA/JdbcTemplate),权限上能写 |
REVOKE v4_sellinv_prod 任何权限都会波及它;"销项-进项数据层耦合归零"未涵盖"销项-协同"这条边;两条 ant-coop Feign 边用配置占位符当服务名,门面接管不到 |
| BI 汇聚 topic tower-invoices 18 canal 共用 |
janus-prod 里 18 个 canal 实例把销项 + 进项 + 租户中心 + JIRA + 4 个大客户 OQS 专库的 binlog 全推向同一个 topic;sellerinvoice.* 通配 |
任何 A 级表 DDL 都经此影响下游 BI;它在数据层把销项与进项再次焊死,不会随第一刀消失;通配意味着新增列(含税号、金额、身份信息)自动外流,直接关联合规 |
3四条必改项(P0)
以下四条若不先改,执行方案会直接导致生产事故或大规模返工。
| # | 必改 | 依据与后果 | 阻塞 |
|---|---|---|---|
| 1 | S1 第一刀步④「REVOKE phoenix-openapi 账号」撤回,直到账号拆分完成。新增 S1-b「按服务拆 DB 账号」主线 | v4_sellinv_prod / v4_bill_prod 由 3–4 个服务共用(含开票主服务与两个域外系统),执行即开票中断 | S1④ · S2 全部 |
| 2 | S0「/ms 公网 deny」拆两步:e1 改 SAAS cluster listOfServers 为集群内地址 → e2 才 deny |
164/212 Feign client 走 /ms 且 SAAS 下经公网域名回环,直接 deny 会切断东西向调用。回滚级别从 L0 上调,「638 → 0」验收后移 | S0 |
| 3 | P2「有可靠幂等键」改判 FALSE,新增 S0.5 幂等前置工程 | 两张请求表零唯一索引已实测确认。在幂等落地前,第一刀只做只读端点,写端点禁止生产流量镜像 | S1 写路径 · S3 · S4 |
| 4 | S0 内决策 Ribbon 去留:二选一——放弃 2.7.x 抬版保住门面,或改用 Spring Cloud LoadBalancer + 显式地址映射作为截流平面 | 门面机制(S1)与抬版目标(S2)互斥且窗口重叠。拖到 S2 才发现将导致 S1 全部返工 | S1 · S2 |
4现状全景
边界破坏图:数据层的真实耦合
flowchart TB
subgraph PUB["公网入口 · 32 host 中 15 个不过 WAF"]
ING["Ingress / ALB
path: / 全量转发"]
end
subgraph SAAS["20-phoenix-prod · 45 workload / 94 副本"]
INV["phoenix-seller-invoice
461 端点 · 94% POST"]
BILL["phoenix-bill
调用图枢纽 · 7 条 3-环必经"]
CFG["phoenix-seller-config"]
NEWAPI["phoenix-seller-open-api"]
OLDAPI["phoenix-openapi
停更 21 个月 · 11 库"]
SCRIPT["phoenix-script-invoice
单副本 · 231 写表 · 99 job"]
end
subgraph OUT["名册之外的写入方 · 草案 0 次提及"]
UNIFY["unify-maintenance-app
read,write 三张核心表"]
ANT["ant-coop-system-service
持 v4_sellinv_prod"]
end
subgraph DB["共享库 · 17.3 TB / 217 亿行"]
SI[("sellerinvoice
9 服务直连")]
BL[("bill / bill_shard")]
PUR[("进项 pim / purcfg")]
end
subgraph CDC["CDC 外流 · 无契约"]
CANAL["16 canal 实例
库.* 通配"]
TOWER["tower-invoices
18 canal 单一出口"]
end
TAX["taxware 通道
33 wl / 123 副本
4 种寻址写法"]
TEN["私有化 5 租户
435 wl / 595 副本
2.92× SaaS"]
ING --> INV
ING --> BILL
ING --> OLDAPI
INV --> SI
BILL --> BL
CFG --> SI
NEWAPI --> BL
OLDAPI --> SI
OLDAPI --> PUR
SCRIPT --> SI
SCRIPT --> BL
SCRIPT --> PUR
UNIFY --> SI
UNIFY --> BL
ANT --> SI
SI --> CANAL
BL --> CANAL
PUR --> CANAL
CANAL --> TOWER
INV --> TAX
OLDAPI --> TAX
SI -.-> TEN
BILL <--> INV
phoenix-openapi 与 phoenix-script-invoice 同时触达销项与进项库;16 个 canal 汇入单一 BI 出口——销项与进项在数据层被焊死两次规模数字(全部可 SQL 复跑)
| 维度 | 数字 | 证据 |
|---|---|---|
| SaaS 生产 | 45 workload / 94 副本(20-phoenix-prod) | evidence/01 §1 |
| 私有化 5 个 ns | 435 workload / 595 副本 / 1806 GiB = SaaS 三环境的 2.92 倍 | evidence/06 §1 · verify 实测 |
| prod 去重端点 | ≤3400,其中 66.3% 是 POST(核心服务 92–97%)——不是 REST,是 RPC-over-HTTP | evidence/02 §2.2 |
其中 /ms 内部平面 | 638 个,与 /api 同端口同 Ingress 同公网域名;163 对逐字镜像 | evidence/02 D1/D2 |
| OpenAPI 契约 | 仅 8 / 34 个 prod 组件有;四大核心服务合计 1550 端点零契约 | evidence/02 A4 |
| 公网无 WAF | 15 / 32 host,含开票主服务 phoenix-seller-invoice.saas.xforceplus.com | evidence/02 A5 |
| Feign 调用图 | 84 条边 / 212 接口 / 48 个目标名;5 个 2-环 + 7 个 3-环 + 1 个自调用 | evidence/02 §4 |
| 数据表 | 472 张表 / 6217 条表引用 / 14 个有 MyBatis 的 prod 服务 | evidence/03 §3.1 |
| 跨服务共写 | 18 张表有 ≥3 个独立部署的写方;73 张有 2 个写方(核验修正,原 44) | evidence/03 §3.3 · verify/facts §1 |
| 数据量 | ≈17.3 TB / 217 亿行;sellerinvoice 主库 7.2 TB + shard1 3.8 TB,bill 4.9 TB | evidence/03 §4.1 |
| 最大单表 | ord_salesbill_item 2193 GB / 24.1 亿行;inv_seller_invoice 184 列 / 1023 GB / 3.94 亿行 / 5 个物理副本 | evidence/03 §4.2/§4.4 |
| 定时任务 | 204 个,其中 139 个(68%)调度真相只在 XXL-Job 控制台;phoenix-script-invoice 以 1 副本承载 99 个 | evidence/04 §4 |
| MQ | 5 套中间件并存;receive 具名通道 219 / send 具名仅 27;53% 发送点通道名动态;销项内异步闭环仅 6 条(3.3%) | evidence/04 §1.2/§2.1 |
| 前端 | seller-main-fe / seller-config-fe 各 53 条生产菜单,但 code_archives 0 个 archive(OSS 静态托管,机制性不可见);功能散在 ≥12 个前端仓;42 条菜单归属不明 | evidence/05 F1/F4 |
| 交付底座 | 15 种基础镜像,主流是 2022 年已 deprecated 的 openjdk:8-jre(71 个服务);173/173 单阶段构建;prod 跑 12 个 Spring Boot 大版本;CI 绑在停维护的 cibase:0.5.0 | evidence/06 §5/§6 |
| 溯源 | SaaS prod gitlab_matched 75.6%,华润 prod 4.3%,阳光城/沃尔玛 0;25.9% 的私有化副本溯源已断 | evidence/06 §1/§2.5 |
5架构债诊断
分级标准:阻塞级 不解决它,其他任何重构动作都无法验证或无法安全执行;重要 直接产生业务风险或高额持续成本;一般 影响可维护性,可随主线顺带处理。
阻塞级(5 条)
| # | 债 | 量化影响 |
|---|---|---|
| X1 | 核心表有多个独立写入方,而幂等已确证缺失——inv_seller_invoice / ord_salesbill 各 4 个写方(含 2 个名册外系统),18 张核心表同构;主服务 9 处事务内挂远程调用(1 处吞异常) | 开票状态机一致性无单一责任方;"疑似成功重试"与"金额订正"是常态运维手段;任何双写窗口都可能产生重复开票 |
| X2 | 拓扑不可发现、不可静态校验——Feign 名真值在 Apollo Ribbon listOfServers 但只覆盖 47.6%;命名有大小写/后缀/拼写/裸 URL 四类不一致;异步侧 53% 发送点动态 | 改一个服务地址要改 N 份 Apollo 配置,没有编译期或部署期保护;任何"先画拓扑再拆"的方案在同步侧漏边、异步侧漏一半 |
| X3 | 契约不存在,且契约面虚高近一倍——1550 端点零 OpenAPI;163 对逐字镜像(靠复制 Ms 前缀 Controller 实现)+ 102 组近似重复;1532/4380 端点定位不到源码类 | 没有契约就无法度量"这一刀切干净了没有";改一处行为要改两处;爆炸半径不可计算 |
| X4 | 改动没有验证面——18 个生产服务无 fat/sit 环境(含 athena 家族 23 副本),且 prod 与 fat 跑不同分支;开票主服务 root 级 skipTests=true、无可见集成测试、12+ SNAPSHOT 直接依赖 | 灰度在这 18 个服务上物理不可行;"改了什么导致线上变化"无法归因 |
| X5 | 私有化是第二条产品线,且四分之一溯源已断——435 workload 对 149;27 条定制分支/109 副本;118 workload/146 副本源码失联;反向污染已发生 | "改主干即全网生效"不成立;对 146 个副本,"改代码"这个选项不存在 |
重要(8 条)
| # | 债 | 量化影响 |
|---|---|---|
| Y1 | phoenix-script-invoice:单副本 + 全域写权限 + 96 个不可见 cron——234 表 / 231 写表 / 2567 SQL / 275 mapper / 跨 10+ RDS(含进项、中台、蚂蚁、备份库)/ 99 定时任务 | 它出现在 18 张 ≥3 写方表中的 17 张上。只要它还在,任何"按服务拆库"在写路径上一条也落不了地;该 pod 挂掉 96 个 job 全停;handler 名直接带客户名 = 大客户定制逻辑沉淀在无边界无契约的单副本批处理里 |
| Y2 | /ms 平面在网络层零隔离——638 个内部端点与 /api 同端口、同 Ingress(path: / 全量转发)、同公网 ALB、无 WAF;含 /ms/api/v1/invoice/devOps 运维后门 | 核验确认应用层鉴权按 cluster 分裂:SAAS 有、CRC/CCAG 为 false → SaaS 侧降为架构债,私有化侧仍是安全暴露面 |
| Y3 | phoenix-openapi 是销项-进项在数据层的焊点——11 个 datasource 横跨销项 5 库 + 进项 purcfg/pim + 跨域 auth/matching;27 张写表含两张主表;停更 21 个月;公网无 WAF | 它绕过进项服务的任何 API 边界,在数据层把两个域焊死。核验进一步发现它自带完整开票写入口,是并行主链路而非旁路 |
| Y4 | CDC 外流无契约——inv_seller_invoice 5 个物理副本 + 16 个 canal 实例监听;CANAL_FILTER 写在容器环境变量里逐条枚举,改表名/拆表会静默断流且零审计留痕;大量 库.* 通配则导致新增列静默外泄 | 一次 inv_seller_invoice 的 DDL,影响面 = 4 写服务 + 4 读服务 + 5 物理副本 + 多条 prod CDC 链路 + 其下游 ES/数仓/AI 库 + tower-invoices BI 出口 |
| Y5 | PreciseShardingAlgorithmImpl 是正在运行的静默数据错置通道——分片键=租户 id,映射 init() 一次性加载内存不刷新,查不到映射静默回落 ds0 | 新增/迁移租户分片必须重启全部 6 副本;配置漏配不报错,会把数据写进错误的库(源码直证) |
| Y6 | 「开票状态」三胞胎——BillMakeOutStatus(9) / SalesBillMakeOutStatusEnum(8) / BizOrderMakeOutStatusEnum(8) 三仓独立定义 | 核验修正根因:三者是同一概念的三份复制,仅码 3 文案漂移、码 9 只有一方认识 → 不是"三个概念",而是缺少单一权威定义;销项 15 个后端合计 1360 枚举类 / 307 状态枚举 |
| Y7 | athena-elastic-consumer:分支当部署单元——1 仓 × 5 条分支 = prod 10 workload / 23 副本,身份 100% 由 2 个配置键决定;其 Apollo app_id 拼写错、在 113 个 app 中不存在,真值在 K8s ConfigMap,零审计 | 改错一个值,consumer 会消费另一张表的 binlog 并写进错误的 ES 索引;每个变体暴露相同的 7 个运维端点含无鉴权 /stop |
| Y8 | yl-ops-agent-service 爆炸半径最大——2 副本直连 12 个库(销项 6 + 进项 + 中台 + 租户中心),公网域名无 WAF;声明只读却用各库 owner 账号,权限上可写 | 单点拥有跨域写能力且无审计约束;"降权 + 审计化"的第一步其实是换账号而非改代码 |
一般(6 条)· 展开
| # | 债 | 说明 |
|---|---|---|
| Z1 | 交付底座断代 | 15 种基础镜像 / 71 服务用已 deprecated 的 openjdk:8-jre / 173 全单阶段构建 / 12 个 SB 大版本 / fastjson 1.2.7 在 10 workload 23 副本 / CI 绑停维护镜像 |
| Z2 | 资源配置无分级 | 26 个 prod workload 的 cpu limit 恰好 = 8.0/8.1(模板默认),而真正的大服务只有 4.0/2.0;私有化侧 65 个容器无 memory limit;10 个 prod workload 无 readiness probe |
| Z3 | 前端三代 IA 并存 | 2023/2024/2026 三代导航共存,7+ 模块各出现 2 次;seller-main-fe 同仓两代微前端框架并存;42 条菜单归属不明 |
| Z4 | 错误码体系失衡 | phoenix-seller-invoice 仅 62 个错误码却有 354 throw-literal + 498 log-error;两代 openapi 双份维护 605 + 579 |
| Z5 | 路径与命名规范缺失 | 208 条无前导斜杠路径(123 条与带斜杠版本双注册);6 种不共存的路径前缀体系;51 个 ≥15 op 的巨型 Controller 承载 2051 op |
| Z6 | OQS 四胞胎泛化数据面 | 4 个服务各自暴露同一套 67 端点,含 27 个 /bos/{boId}/entities 泛化写接口 + /graphql + /altair + /voyager IDE 在 prod |
6目标与路线图
核心机制:每一刀都走同一套五步
flowchart LR A["① 影子读
新路径只读
diff 量化覆盖率"] --> B["② 双写
硬上限 90 天
幂等前置为准入"] B --> C["③ 切读
按租户/接口族灰度
可秒级回切"] C --> D["④ 收写
GRANT 物理兑现
需账号拆分完成"] D --> E["⑤ 拆旧
旧路径 30 天零流量
方可下线"] E --> F["出口标志
旧路径零流量
≠ 新路径上线"]
阶段总览
| 阶段 | 月份 | 一句话目标 | 出口标志(= 旧路径零流量) | 最高回滚 |
|---|---|---|---|---|
| S0 | 0–3 | 止血 + 看得见 | 核实项有结论;六个 lock 存在且 CI 强制;分片静默错置已修;99 job 单点已消除;Ribbon 去留已决策 | L0 |
| S0.5 | +3~6 | 幂等前置 新增 | 三张核心请求表具备请求级幂等键;重复度普查清洗完成 | L1 |
| S1 | 2–9 | 门面就位 + 第一刀 | 南北东西流量经门面(覆盖率分母按 45 目标名重定义);phoenix-openapi 连续 30 天零流量。第一刀改为薄壳保契约 | L1 |
| S1-b | 并行 | 按服务拆 DB 账号 新增 | 一服务一账号;共用账号持有者清单归零 | L2 |
| S2 | 7–15 | 写权收口 + 租户耦合切断 | A 级 18 张表非授权写方 GRANT 清零;CCAG 专库与 SaaS 解耦 | L4 |
| S3 | 12–20 | 接口族搬迁:红冲 → 预制发票 | 两域在新服务上跑,旧路径连续 30 天零流量 | L3 |
| S4 | 18–26 | 开票/单据解环 + 大表减负 | bill→invoice 同步 Feign 归零;1.49 TB 日志表剥离完成 | L3+L5 |
| S5 | 24–30 | 遗留下线 + 租户收敛 | 遗留服务下线;租户按 A/B/C/D 定级完成 | L5 |
7风险登记册(节选)
| # | 风险 | 概率 | 影响 | 缓解措施 |
|---|---|---|---|---|
| R1 | 开票主链路无可靠幂等键,双写/切流期出现重复开票 | 已确证 | 极高 | 升级为 S0.5 独立工程;幂等落地前禁止开票写路径双写与流量镜像;重复开票率设为一票否决指标 |
| R4 | /ms 平面 638 个内部端点含 devOps 后门在公网无 WAF 暴露 | 中 | 极高 | 核验确认 SAAS 有应用层鉴权(风险下调)、CRC/CCAG 为 false(私有化侧仍需紧急处置);deny 拆两步执行 |
| R5 | phoenix-script-invoice 的 99 个批处理含未文档化的大客户业务逻辑,拆分时丢失行为 | 高 | 高 | 拆分不重写,原样搬运 + 逐 job 对账;按客户拆使每客户可独立回滚;拆前 30 天输出基线快照;每个 job 停用前"空跑 + diff"2 周 |
| R6 | 租户侧(尤其华润)不配合跟随,主线成果被永久分叉稀释 | 高 | 高 | 四条跟随契约写入商务协议;CI 硬规则堵反向污染;核验推翻了"中央 Apollo cluster 可作合流抓手",A 档降为「归因可做、归并待证」 |
| R7 | CDC 静默断流或静默外泄导致 ES/数仓/AI 库停更、或新增列自动外流 | 中 | 高 | cdc.lock(扩至 16 canal + tower-invoices)+ DDL 门禁四问 + 位点快照 + 24h 下游同比校验;4 条零可见消费者 topic 补证前不得动 |
| R10 | 18 个生产服务无测试环境,合并/搬迁无法灰度验证 | 已是事实 | 中高 | S0 强制补齐,优先 athena 家族,且要求 prod 与 fat 同分支;未补齐的服务不得进入任何合并/搬迁动作 |
| R13 | 项目中途停止,系统永久停在"新旧双轨 + 双写 + 门面多一跳"——比重构前更复杂 | 中高 | 高 | 每阶段出口标志是"旧路径零流量";按可独立交付的刀组织;双写期硬上限 90 天;翻译表宿主在版本化 kernel |
| R14 | 税局通道在代码侧完全不可见,重构中意外打断开票执行链路 | 低 | 极高 | 该通道只在网络层可见(2 个 ExternalName + 2 条 Ingress),代码侧检索 0 行。补证完成前不得触碰 taxware 相关配置 |
| R17 | 归档/删除动作触及税务法定留存义务 | 中 | 极高 | 四条合规硬规则;不可逆动作门禁新增合规评审;在法务口径明确前维持"只冷存不删" |
8落地前待验证清单
核验阶段已用现有湖仓数据当场解决了 4 项原定核实(幂等键、DB 账号、/ms 鉴权、listOfServers 唯一性)。下列 12 项仍然悬空,且会改变方案动作。标「阻塞」的,在验证完成前对应阶段不得启动。
| # | 待验证 | 验证方法(可立即执行) | 阻塞 |
|---|---|---|---|
| V1 | 是否还有更多 namespace 名册之外的核心表写入方 | 放弃 ns 关键词过滤,从数据面反查:按 jdbc_url 中的实例 id 全库反查持有连接的 component,再 ⋈ code_mybatis_table_refs 取 write | S1④ · S2 · S4 |
| V2 | 三个共用账号的完整持有者名单与实际权限 | 问 DBA 取 SELECT user,host FROM mysql.user + SHOW GRANTS;用连接来源 IP 反查实际使用方 | S1-b |
| V3 | 两张请求表建唯一索引的在线可行性(存量可能已有重复行) | 先跑重复度普查(按候选幂等键 GROUP BY … HAVING count(*)>1),再定"先清洗后建索引"还是"幂等表旁路" | S0.5 |
| V4 | Ribbon 去留:9 个服务能否在不抬 SB 版本的前提下长期维持 | 盘 SB/SC 版本矩阵;评估 Spring Cloud LoadBalancer + 显式地址映射作为替代截流平面的改造量 | S0 决策门 |
| V5 | 三个隐性下游(ods / ai-tm-ai-invoice / sellerinvoice_bak)的消费方与 SLA | 以 binlog dump 连接数反查订阅方(数据库侧可观测),而非依赖配置登记 | S4 · A 级 DDL |
| V6 | tower-invoices 的下游消费方清单 | 查 MQ 侧消费组注册(本体只覆盖发送侧,消费方需从 MQ 控制台取) | S4 · 合规 |
| V7 | phoenix-seller-open-api 的 prod 数据源真值与 PolarDB 纳管状态 | code-probe 取证 + 问 DBA。湖仓显示它用 dvlp 账号连一台未纳管实例 | 第一刀定稿 |
| V8 | 94-local-crc-prod 的 Apollo 指向(本地 vs 中央) | 查该 ns 的 APOLLO_META / config-service env | 租户 A 档 |
| V9 | tenant-security-server-starter 的装配语义(默认拦截 vs 默认放行) | 读 jar 的 spring.factories 与自动装配条件 | /ms 风险定级 |
| V10 | 三个无源码取证服务的真实形态 | ontoos-code-probe 对部署 commit 取证(命令已就绪,本次因额度中断未完成) | 服务去向表 |
| V11 | 华润 3 套独立销项库实例(含 MySQL 5.6)的版本、容量与同步关系 | dms_instances ⋈ dms_databases 按租户过滤;容量用 store_capacity(单位 MB) | 租户分档 |
| V12 | 税务合规:归档/删除/列裁剪在监管上是否可行 | 非技术验证——需财税合规与法务给出留存年限与可删除范围的书面口径 | S4 大表减负 |
9本次调研自身暴露的方法论教训
unify-maintenance-app 被漏掉,恰恰印证了资产盘点阶段自己写下的警告,而调研在更高一层重犯了同样的错误。资产盘点的结论 C1 说的是:在一个 namespace 内用服务名关键词(seller)圈定会系统性漏服务,必须按 namespace 圈定。但调研随后用 ILIKE '%phoenix%|%lmt%|%crc%|…' 给 namespace 本身做了关键词过滤——sa-unify-prod 一个模式都不匹配,于是一个对三张核心表有写权限的生产系统整体消失在视野外。
教训:资产圈定不能靠命名,只能靠关系。对下一轮调研与 S0 的资产普查,圈定入口应改为:
- 先锚定物理数据面(RDS 实例 id / 库名),而非服务名或 namespace 名;
- 从数据面反查所有持有连接的 component(
code_datasources.jdbc_url全库匹配); - 再从 component 反查 workload 与 GitLab 归属;
- 命名关键词只用于交叉验证,不用于圈定。
同理适用于 MQ(从 topic 反查而非从服务反查)与前端(从门户菜单反查而非从仓库反查——证据已表明镜像抽取对 OSS 发布的前端天然失明)。
10证据与口径
| 数据源 | ontoos 研发本体湖仓(AnalyticDB for PostgreSQL),逻辑层 ods_current / dwd_current / dws;批次以 meta.batch_status 活跃批次为准(抽取激活 2026-09-06) |
|---|---|
| 调研执行 | 2026-09-11 ~ 2026-09-13 |
| 证据规模 | evidence/ 7 份共 3093 行 · sql/ 161 个可复跑 SQL · verify/ 2 份共 119 KB(含 44 次核验期实跑) · design/ 3 套候选方案共 3258 行 |
| 覆盖缺口 | ① 源码取证仅完成 phoenix-seller-invoice 1/4,其余三服务结论均为元数据推断;② 私有化租户是抽取盲区(94-local-crc-prod 覆盖率 13.7%、p1099-wmt-prod 0%);③ MQ 消费侧、前端 OSS 发布物、XXL-Job 调度面本体均不覆盖 |
| 复跑方式 | cd ontoos-probe && python scripts/probe.py --format csv "<SQL>";SQL 直接写逻辑层名,probe 自动解析为实名 |
| 文档版本 | 定稿 v2.0——由 v1.0 草案吸收两路对抗性核验修订而成。完整方案见 REFACTOR-PLAN.md(1208 行) |
产出文件索引 · 展开
| 文件 | 内容 |
|---|---|
REFACTOR-PLAN.md | 最终方案定稿(1208 行 / 13 节),含核验修订总表、执行摘要、架构债诊断、目标架构、路线图、数据迁移、租户策略、风险登记册、度量体系、待验证清单、证据索引 |
evidence/00-digest.md | 证据索引与六视角核心结论浓缩(347 行) |
evidence/01-inventory.md | 资产总盘点:服务、仓库、部署位点、抽取覆盖率、溯源缺口(343 行) |
evidence/02-api-topology.md | 接口与调用拓扑:端点规模、契约面、Feign 调用图与环、跨域边界、公网入口(651 行) |
evidence/03-data-assets.md | 数据资产与血缘:共享库热力、跨服务共写、库表规模、CDC 外流(297 行) |
evidence/04-async-config.md | 异步集成与配置治理:MQ 拓扑、孤儿通道、定时任务、Apollo 漂移(342 行) |
evidence/05-frontend-portal.md | 前端与业务入口:功能地图、状态机、契约漂移、错误码(300 行) |
evidence/06-deploy-tenancy.md | 部署形态与多租户:环境矩阵、租户分叉、资源足迹、交付链路(687 行) |
evidence/code-1-*.md | phoenix-seller-invoice 部署 commit 源码取证(473 行,含 file:line 级证据) |
design/option-1..3-*.md | 三套独立视角候选方案:DDD 边界重划 / 绞杀者渐进 / 地基工程(共 3258 行) |
design/scorecard.md | 三视角评委评分与嫁接建议(263 行) |
verify/facts.md | 对抗性核验 · 事实正确性与遗漏(57 KB,24 次实跑) |
verify/executable.md | 对抗性核验 · 可执行性与风险(62 KB,20+ 次实跑) |
sql/ | 161 个可复跑 SQL,按视角前缀命名 |