v1.0 · 2026-04-13 技术规格文档 · 后端交付物 Java Spring Boot + MySQL 作者:T.San · INFINITY OS
文档目录
1核心思想
一个原则:INFINITY OS 里所有实体(城市/区/经销商/门店/SKU)都是同一种东西——节点。
每个节点有相同的字段,走相同的下钻逻辑,被同一个前端组件渲染。
数据不同,代码完全相同。这是消灭重复的根本方法。
纵横框架的98个交叉点,本质上是同一张表的98行记录,而不是98个不同的页面。 Universal Node Schema 就是这张表的完整定义。
没有这个 Schema 时
经销商、门店、SKU 各自一套数据结构
每个新模块重写渲染代码
前后端接口字段每次都在谈
v781 → 11,947行,越写越长
有了这个 Schema 之后
所有实体一套字段,一个接口
renderNode(node) 渲染任何层级
后端新城市 = 插入一行记录
前端代码量减少 60% 以上
2节点层级结构
下钻路径:从品牌到 SKU,共6层。每层都是一个节点,每个节点有相同的 Schema。
L0
品牌
r_品牌
BRAND
L1
城市
r_城市
CITY
L2
经销商
r_经销商
DIST
L3
门店
r_门店
STORE
L4
SKU
r_SKU
SKU
🔗
每层 r 值的关系: 门店 r 值汇总 → 经销商 r 值 → 城市 r 值 → 品牌 r 值。
上层 r 值 = 下层所有子节点 r 值的加权聚合,由系统自动计算,不需要人工填写。
3Schema 字段定义
每个节点的完整字段集。绿色 = 心跳公式核心字段,必须实时更新。
字段名类型标记含义
── 身份字段
node_id VARCHAR(64) PK 全局唯一ID,格式:品牌-城市-层级-名称,如 lyx-sh-dist-baocang
node_type ENUM brand / city / district / distributor / sub_dist / store / sku
node_name VARCHAR(128) 显示名称,如「上海宝仓贸易」「大润发金桥店」「遵循自然500ml」
parent_id VARCHAR(64) FK 父节点ID,指向同表。根节点(品牌)parent_id = NULL
brand_code VARCHAR(16) IDX lyx / wdm / hr / cbl / xk,所有层级都带此字段,方便跨品牌查询
channel_type ENUM P1(直供零售)/ P2(餐饮分销)/ both
── 心跳公式字段(核心 · 实时更新)
a_value DECIMAL(10,2) 核心 心跳单元 a = 一个完整周转周期T内的销售量。基准单位,全系统从此派生
T_days TINYINT 核心 周转周期天数。零售S+/S/A = 7天,B/C/D = 14天,经销商 = 30天
r_actual DECIMAL(10,4) 核心 实际日销率 = a ÷ T。来自汉询日销数据,每日更新
r_target DECIMAL(10,4) 核心 目标日销率。由业务团队设定,按年级/季节调整,存入 inf_node_targets
r_achievement DECIMAL(5,2) 自动计算 达成率 = r_actual ÷ r_target × 100,系统自动算,不需要人工填
inventory_a DECIMAL(10,2) 核心 当前库存折算成几个a,来自SFA上报或Dcloud。计算:库存件数 ÷ a_value
water_level ENUM 自动计算 overflow(>3a) / full(3a) / warn(2a) / low(1a) / empty(0)。系统自动判断
digest_rate DECIMAL(5,2) 自动计算 消化率 = 本月出货 ÷ 上月进货。铁律③监控字段:连续2月 <50% 触发警报
── 年级与激励字段
grade TINYINT(1) IDX SKU年级:1=新品 / 2=成长 / 3=成熟 / 4=特级。城市/门店级节点不适用,填NULL
grade_coeff DECIMAL(3,1) 自动计算 激励系数:年级1=×2.0 / 年级2=×1.5 / 年级3=×1.0 / 年级4=×0.7。系统查表填入
── CB公式字段
cb_brandDECIMAL(5,2) CB = Brand分。品牌认知/忠诚度评估
cb_valueDECIMAL(5,2) CB = Value分。产品价值感
cb_relationDECIMAL(5,2) CB = Relation分。渠道/终端关系粘性
cb_costDECIMAL(5,2) CB = Cost分(负向)。转换成本
cb_timeDECIMAL(5,2) CB = Time分(负向)。时间摩擦
cb_total DECIMAL(5,2) 自动计算 CB合计 = Brand + Value + Relation − Cost − Time。系统自动算
── 状态与时间字段
active_alert_count TINYINT 自动计算 当前未处理的蝴蝶效应警报数量,从 inf_alerts 聚合
pending_action_count TINYINT 自动计算 待批准决策数量,从 inf_actions 聚合
last_updated DATETIME 最后一次数据更新时间,用于判断数据新鲜度
data_source VARCHAR(32) 数据来源:hanxun / dcloud / sfa / wms / manual / calculated
4MySQL 建表语句
共5张核心表: inf_nodes(节点注册表)· inf_heartbeat(心跳快照)· inf_r_series(r值时序)· inf_alerts(蝴蝶效应警报)· inf_actions(决策队列)
表 1/5 · 节点注册表MySQL DDL
-- ============================================================ -- 表1: inf_nodes · 节点注册表 -- 所有实体的身份注册,下钻结构由 parent_id 自引用实现 -- ============================================================ CREATE TABLE inf_nodes ( node_id VARCHAR(64) NOT NULL, node_type ENUM('brand','city','district','distributor', 'sub_dist','store','sku') NOT NULL, node_name VARCHAR(128) NOT NULL, parent_id VARCHAR(64) DEFAULT NULL, -- 根节点为NULL brand_code VARCHAR(16) NOT NULL, -- lyx/wdm/hr/cbl/xk channel_type ENUM('P1','P2','both') DEFAULT 'P1', grade TINYINT(1) DEFAULT NULL, -- 仅SKU节点有效 grade_coeff DECIMAL(3,1) DEFAULT NULL, -- 系统自动填入 store_class ENUM('S+','S','A','B','C','D') DEFAULT NULL, -- 仅store节点 T_days TINYINT NOT NULL DEFAULT 30, ipo10_price DECIMAL(8,2) DEFAULT NULL, -- 仅SKU节点 · 铁律①监控 is_active BOOLEAN DEFAULT TRUE, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (node_id), FOREIGN KEY (parent_id) REFERENCES inf_nodes(node_id), INDEX idx_brand (brand_code), INDEX idx_type (node_type), INDEX idx_parent (parent_id) ) ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='节点注册表·全体系实体的身份与层级';
表 2/5 · 心跳快照表MySQL DDL
-- ============================================================ -- 表2: inf_heartbeat · 心跳快照 -- 每天一条记录,存当日r值和库存水位 -- ============================================================ CREATE TABLE inf_heartbeat ( hb_id BIGINT AUTO_INCREMENT PRIMARY KEY, node_id VARCHAR(64) NOT NULL, snapshot_date DATE NOT NULL, -- 心跳公式核心 a_value DECIMAL(10,2) NOT NULL, -- 心跳单元 r_actual DECIMAL(10,4) NOT NULL, -- 实际日销率 r_target DECIMAL(10,4) NOT NULL, -- 目标日销率 r_achievement DECIMAL(5,2) AS (r_actual / r_target * 100) STORED, -- 库存水位 inventory_units INT NOT NULL, -- 实物库存件数 inventory_a DECIMAL(10,2) AS (inventory_units / a_value) STORED, water_level ENUM('overflow','full','warn','low','empty'), -- 触发器自动写 digest_rate DECIMAL(5,2) DEFAULT NULL, -- 消化率,月底计算 -- CB得分快照 cb_total DECIMAL(5,2) DEFAULT NULL, data_source VARCHAR(32) NOT NULL, -- hanxun/dcloud/sfa/manual created_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_node_date (node_id, snapshot_date), FOREIGN KEY (node_id) REFERENCES inf_nodes(node_id), INDEX idx_date (snapshot_date) ) ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='心跳快照·每日r值和库存水位';
表 3/5 · 蝴蝶效应警报表MySQL DDL
-- ============================================================ -- 表3: inf_alerts · 蝴蝶效应警报 -- A类压货/B类促销扰动/C类窜货/D类战略扰动 -- ============================================================ CREATE TABLE inf_alerts ( alert_id BIGINT AUTO_INCREMENT PRIMARY KEY, node_id VARCHAR(64) NOT NULL, alert_type ENUM('A','B','C','D') NOT NULL, -- A压货/B促销/C窜货/D战略 severity ENUM('yellow','orange','red') NOT NULL, iron_law_ref TINYINT DEFAULT NULL, -- 关联铁律编号1/2/3,无关联填NULL title VARCHAR(256) NOT NULL, description TEXT, trigger_value JSON, -- 触发数据快照,如{"digest_rate":0.471} status ENUM('active','acknowledged','resolved','overridden') DEFAULT 'active', triggered_at DATETIME NOT NULL, resolved_at DATETIME DEFAULT NULL, resolved_by VARCHAR(64) DEFAULT NULL, FOREIGN KEY (node_id) REFERENCES inf_nodes(node_id), INDEX idx_status (status), INDEX idx_triggered (triggered_at) ) ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='蝴蝶效应警报·A/B/C/D四类失真';
表 4/5 · 决策队列表MySQL DDL
-- ============================================================ -- 表4: inf_actions · 决策队列 -- Agent 提出行动建议,等待人工批准/拒绝/覆盖 -- ============================================================ CREATE TABLE inf_actions ( action_id BIGINT AUTO_INCREMENT PRIMARY KEY, node_id VARCHAR(64) NOT NULL, agent_source VARCHAR(64) NOT NULL, -- 哪个Agent提出,如 butterfly_agent action_type ENUM('replenish','suspend','grade_change', 'price_check','alert_resolve','custom') NOT NULL, title VARCHAR(256) NOT NULL, description TEXT, confidence_pct TINYINT NOT NULL, -- 0-100,Agent置信度 impact_level ENUM('high','mid','low') NOT NULL, proposed_payload JSON, -- 具体建议参数,如{"qty":240,"sku":"lyx-500ml"} status ENUM('pending','approved','rejected', 'overridden','auto_executed') DEFAULT 'pending', override_reason TEXT DEFAULT NULL, proposed_at DATETIME NOT NULL, decided_at DATETIME DEFAULT NULL, decided_by VARCHAR(64) DEFAULT NULL, -- 操作人工号或 'system' FOREIGN KEY (node_id) REFERENCES inf_nodes(node_id), INDEX idx_status_impact (status, impact_level), INDEX idx_pending (status, proposed_at) ) ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='决策队列·Agent行动建议的审批闭环';
表 5/5 · 目标设定表MySQL DDL
-- ============================================================ -- 表5: inf_node_targets · 目标设定 -- 业务团队填写每个节点的r_target和年级,解锁 Step 2 数据接入 -- ============================================================ CREATE TABLE inf_node_targets ( target_id BIGINT AUTO_INCREMENT PRIMARY KEY, node_id VARCHAR(64) NOT NULL, effective_month DATE NOT NULL, -- 生效月份,格式 YYYY-MM-01 r_target DECIMAL(10,4) NOT NULL, -- 这是当前 SAP Step2 的最大阻塞点 grade TINYINT(1) DEFAULT NULL, set_by VARCHAR(64) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_node_month (node_id, effective_month), FOREIGN KEY (node_id) REFERENCES inf_nodes(node_id) ) ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='目标设定·业务团队填写r_target和年级';
5API Response 格式
前端请求一个节点:GET /api/v1/node/{node_id},返回以下格式。不管请求的是城市还是SKU,格式完全相同。
GET /api/v1/node/{node_id} · 完整响应JSON
{ "node": { "id": "lyx-sh-dist-baocang", "type": "distributor", "name": "上海宝仓贸易", "brand": "lyx", "channel": "P1", "grade": null, // 经销商层级无年级,SKU层级才有 "grade_coeff": null, "T_days": 30 }, "heartbeat": { // 心跳公式核心,前端水位图直接读这里 "a_value": 120, "r_actual": 3.2, "r_target": 4.1, "r_achievement": 78.0, // 百分比,系统计算 "inventory_units": 504, "inventory_a": 4.2, // 504 ÷ 120 = 4.2a,超3a触发铁律③ "water_level": "overflow", "digest_rate": 0.471, // 47.1%,连续2月低于50%触发铁律③ "snapshot_date": "2026-04-13", "data_source": "dcloud" }, "cb": { "brand": 7.2, "value": 6.8, "relation": 8.1, "cost": 3.2, "time": 2.4, "total": 16.5 // 7.2+6.8+8.1-3.2-2.4 }, "alerts": [ // 蝴蝶效应,status=active的全部返回 { "alert_id": 1042, "type": "A", "severity": "red", "iron_law_ref": 3, "title": "连续2月消化率低于50%", "triggered_at": "2026-04-13T07:00:00" } ], "actions": [ // 待批准决策队列,status=pending { "action_id": 205, "type": "suspend", "agent": "butterfly_agent", "title": "建议暂停2周补货", "confidence_pct": 91, "impact_level": "high" } ], "breadcrumb": [ // 前端面包屑导航直接用这个数组 {"id":"lyx", "name":"六月鲜", "type":"brand"}, {"id":"lyx-sh", "name":"上海", "type":"city"}, {"id":"lyx-sh-dist-baocang", "name":"宝仓贸易", "type":"distributor"} ], "children": [ // 下钻列表,只返回id/name/type/heartbeat摘要 { "id": "lyx-sh-dist-baocang-store-001", "name": "大润发金桥店", "type": "store", "r_achievement": 74.0, "water_level": "warn", "active_alert_count": 1 } ] }
6计算规则
以下字段由系统自动计算,不允许人工填写。后端在写入心跳记录时执行。
自动计算逻辑伪代码
// 1. r达成率 r_achievement = r_actual / r_target * 100 // 2. 库存折算 inventory_a = inventory_units / a_value // 3. 水位判断(根据节点类型) IF node_type == 'store': // 零售:3a满仓 IF inventory_a > 3: water_level = 'overflow' IF inventory_a >= 2: water_level = 'full' IF inventory_a >= 1: water_level = 'warn' ELSE: water_level = 'low' | 'empty' IF node_type == 'distributor': // 经销商:2a满仓 IF inventory_a > 2: water_level = 'overflow' IF inventory_a >= 1: water_level = 'full' ELSE: water_level = 'low' | 'empty' IF node_type == 'sub_dist': // 二批商:严控1a IF inventory_a > 1: water_level = 'overflow' // 触发窜货预警 IF inventory_a == 1: water_level = 'full' ELSE: water_level = 'low' // 4. 年级激励系数 grade_coeff = {1:2.0, 2:1.5, 3:1.0, 4:0.7}[grade] // 5. 铁律③触发条件(每月月末执行检查) IF digest_rate < 0.50 AND consecutive_months >= 2: INSERT inf_alerts (type='A', severity='red', iron_law_ref=3) UPDATE inf_actions: 自动暂停该节点的 pending replenish 推单
7数据源映射
每个数据源对应哪些字段,按接入优先级排序。
汉询
日销 POS
rixiao_qty inf_heartbeat.r_actual P1 最高优先
store_id inf_nodes.node_id (需映射表)
这是整个系统的数据血液。r_actual 上线,所有心跳计算即刻启动。
Dcloud
EPP 经销商
stock_qty inf_heartbeat.inventory_units P1 最高优先
shipout_qty digest_rate 计算输入 P1
SFA
库存上报
shelf_qty + warehouse_qty store节点 inventory_units P2
shelf_photo cb_value 输入 + 货架识别训练数据 P2
WMS
工厂仓库
factory_stock brand/sku节点 inventory_units(IPO0-1) P3
业务团队
手动填写
r_target + grade inf_node_targets(SAP Step2 当前阻塞点) P1 解锁 Step2
⚠ 业务团队填完 r_target + grade,Step2 立即解锁。这是当前最紧急的手动动作。
8铁律触发规则
铁律① — IPO10 价格低于 ¥13/瓶 → 立即暂停
监控字段:inf_nodes.ipo10_price
触发条件:ipo10_price < 13.00
自动动作:INSERT inf_alerts(type='D', severity='red', iron_law_ref=1),同时 INSERT inf_actions(type='suspend', confidence=100)
铁律③ — 连续两月消化率低于 50% → 蝴蝶效应A类警报
监控字段:inf_heartbeat.digest_rate,每月最后一天计算
触发条件:当月 digest_rate < 0.50 AND 上月 digest_rate < 0.50
自动动作:INSERT inf_alerts(type='A', severity='red', iron_law_ref=3),暂停该节点所有 pending 推单
当前触发节点:上海宝仓贸易(digest_rate = 47.1%,已连续2月)
9前端对接说明
Schema 确认后,前端只需要一个函数。下钻永远是同一行代码。
前端 renderNode 伪代码JavaScript
// 全系统唯一的渲染函数,接受任何类型节点 async function renderNode(nodeId) { const node = await fetch(`/api/v1/node/${nodeId}`) // 面包屑:直接用 breadcrumb 数组 renderBreadcrumb(node.breadcrumb) // 心跳水位:直接读 heartbeat renderHeartbeat(node.heartbeat) // 颜色/水位条/r达成率 // 警报:直接渲染 alerts 数组 renderAlerts(node.alerts) // 决策队列:直接渲染 actions 数组 renderActions(node.actions) // 子节点列表(下钻入口) node.children.forEach(child => { // 点击任何子节点 → 再次调用 renderNode,无限下钻 child.onclick = () => renderNode(child.id) }) // AI对话:节点自带上下文 callClaude(buildPrompt(node), userQuestion) } // 全系统唯一的 Claude 调用函数 async function callClaude(systemPrompt, userQuestion) { // 12个函数 → 1个,原来的 12 个全部删除 return await fetch('https://api.anthropic.com/v1/messages', { body: JSON.stringify({ model, system: systemPrompt, messages }) }) }
Schema 上线后的系统行为:
新增一个城市 = 插入一行 inf_nodes 记录,前端自动显示,不写任何新代码。
新增一个 SKU = 同上。新增一个经销商 = 同上。
v781 的 11,947 行,可以重构到 3,000 行以内。
交给后端团队的优先级:
第1步:建5张表(DDL 已在本文档,直接执行)
第2步:业务团队填写 inf_node_targets(r_target + grade),解锁 SAP Step2
第3步:汉询日销数据 → inf_heartbeat.r_actual(接通后全系统激活)
第4步:Dcloud 库存 → inf_heartbeat.inventory_units