diff --git a/ISOLATION_TEST.md b/ISOLATION_TEST.md deleted file mode 100644 index 13e41fa2..00000000 --- a/ISOLATION_TEST.md +++ /dev/null @@ -1 +0,0 @@ -worktree测试文件 diff --git a/backend/tests/__pycache__/__init__.cpython-312.pyc b/backend/tests/__pycache__/__init__.cpython-312.pyc index 47048b21..3bcf3d6a 100644 Binary files a/backend/tests/__pycache__/__init__.cpython-312.pyc and b/backend/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/backend/tests/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc b/backend/tests/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc index 5a6f172a..80e7ac14 100644 Binary files a/backend/tests/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc and b/backend/tests/__pycache__/conftest.cpython-312-pytest-9.0.3.pyc differ diff --git a/backend/tests/__pycache__/test_auth.cpython-312-pytest-9.0.3.pyc b/backend/tests/__pycache__/test_auth.cpython-312-pytest-9.0.3.pyc index 052169af..f233ffad 100644 Binary files a/backend/tests/__pycache__/test_auth.cpython-312-pytest-9.0.3.pyc and b/backend/tests/__pycache__/test_auth.cpython-312-pytest-9.0.3.pyc differ diff --git a/backend/tests/__pycache__/test_kpis.cpython-312-pytest-9.0.3.pyc b/backend/tests/__pycache__/test_kpis.cpython-312-pytest-9.0.3.pyc deleted file mode 100644 index 73288970..00000000 Binary files a/backend/tests/__pycache__/test_kpis.cpython-312-pytest-9.0.3.pyc and /dev/null differ diff --git a/backend/tests/__pycache__/test_maps.cpython-312-pytest-9.0.3.pyc b/backend/tests/__pycache__/test_maps.cpython-312-pytest-9.0.3.pyc deleted file mode 100644 index 9694f92b..00000000 Binary files a/backend/tests/__pycache__/test_maps.cpython-312-pytest-9.0.3.pyc and /dev/null differ diff --git a/backend/tests/test_kpi_422_baseline.py b/backend/tests/test_kpi_422_baseline.py new file mode 100644 index 00000000..ff809028 --- /dev/null +++ b/backend/tests/test_kpi_422_baseline.py @@ -0,0 +1,40 @@ +""" +基线测试:KPI创建接口缺少必填元数据字段时返回 HTTP 422。 + +场景:POST /api/cma/kpis 请求体不传 formula 字段(数据治理规则2: 元数据必填), +期望返回 HTTP 422,且 errors 中包含 formula 相关提示。 +""" +from fastapi.testclient import TestClient +from sqlalchemy.orm import Session + +from tests.conftest import create_test_user, get_token_for_user, auth_header + + +class TestKpi422Baseline: + """KPI创建缺少必填元数据字段 → 422 基线测试""" + + def test_create_kpi_missing_formula_returns_422(self, client: TestClient, db: Session): + """不传 formula 字段时,创建KPI返回 422""" + create_test_user(db) + token = get_token_for_user(client) + + # 构造请求体:其余必填字段齐全,唯独不传 formula + payload = { + "kpi_code": "F_BASELINE_001", + "kpi_name": "基线测试收入指标", + "dimension": "finance", + "target_value": 1000000, + "unit": "元", + # 注意:故意不传 formula(必填元数据字段) + "data_source": "测试系统", + "data_owner": "测试管理员", + } + resp = client.post("/api/cma/kpis", headers=auth_header(token), json=payload) + assert resp.status_code == 422, f"期望422,实际 {resp.status_code}: {resp.text}" + + # 校验错误信息中包含 formula 字段 + # 注意:FastAPI HTTPException(detail=dict) 时响应体为 {"detail": {...}} + body = resp.json() + detail = body.get("detail", {}) + errors = detail.get("errors", []) if isinstance(detail, dict) else [] + assert any("formula" in e for e in errors), f"errors 应提及 formula: {body}" diff --git a/docs/cma-data-analysis-3-principles.md b/docs/cma-data-analysis-3-principles.md new file mode 100644 index 00000000..87e5c99f --- /dev/null +++ b/docs/cma-data-analysis-3-principles.md @@ -0,0 +1,188 @@ +# CMA 数据分析三原则(找异常 / 读动机 / 理关联)——交付方法论 + +> 版本:v1.0 | 2026-08-26 | 提出:yanxueBot(user-insight-9-perspectives 视角5) +> 落地:项目Bot + 财务Bot | 状态:交付方法论(P2,不新增功能,用现有CMA能力) +> 配套模板:`docs/templates/cma-data-analysis-report-template.md` + +## 0. 为什么做这套方法论 + +客户买了CMA,看到的不该是"一堆报表",而应该是"一个故事"。 +同一个数据,讲法不同,客户价值感知完全不同: + +- 讲数据:营业收入8.08万 → 客户:"哦,知道了"(无感) +- 讲故事:营业收入环比暴跌90%,触发红色预警,根因是渠道补贴冲减、真实毛利51.4%被账面口径掩盖 → 客户:"那怎么办?"(有行动) + +三原则就是把"数据→洞察"做成可复制的标准流程:**找异常(信号)→ 读动机(原因)→ 理关联(对策)**。 + +## 1. 三原则总览 + +| # | 原则 | 一句话 | 对应CMA能力 | 输出 | +|---|------|--------|------------|------| +| 1 | 找异常 | 偏离预期的数字=信号=机会 | 预警规则 alert_rules(static/trend_down)、KPI偏离看板、预测偏差告警 | 异常清单(KPI×偏离度×红黄绿) | +| 2 | 读动机 | 数据背后是活生生的人 | 业务访谈、场景还原、口径拆解(多模型对比) | 动机/口径解释(为什么会这样) | +| 3 | 理关联 | 串联行为与市场,理清因果 | KPI因果链(positive/negative)、模拟推演、四维度联动 | 因果链图+对策建议(所以怎么办) | + +记忆口诀:**"哪儿不对 → 为什么 → 牵一发动哪里"**。 + +--- + +## 2. 原则一:找异常(不只看常态) + +### 2.1 定义 +常态是背景,异常才是信号。客户最值钱的信息不是"这个月正常",而是"这个月不对劲"。 +异常=偏离预期(目标/阈值/趋势/历史同期)的数字。偏离越大,机会越大(无论好坏)。 + +### 2.2 CMA落地工具 +- **预警规则**(alert_rules):static(绿/黄/红阈值)+ trend_down(环比跌幅)双引擎,现行78条 +- **KPI看板/偏离视图**:实际值 vs 目标值,红黄绿一目了然 +- **预测偏差告警**(rule_type=forecast_deviation):实际 vs 模型预测,跑偏即报警 +- **多粒度对比**:月/季/年目标(target_calc_type)齐比,避免单粒度误判 + +### 2.3 落地步骤(30分钟) +1. 拉当期全量KPI实际值,按红黄绿筛出红/黄 +2. 环比/同比/目标三个维度排序,取Top5偏离 +3. 只保留"偏离有业务含义"的,剔除口径噪音(如导入错误、季节性) +4. 输出异常清单:KPI名 | 实际值 | 预期值 | 偏离度 | 红黄绿 | 首次出现时间 + +### 2.4 客户话术 +- 开场:"这个月有X个指标在警报区,我们一个个看。" +- 提问:"这个数字偏离了目标X%,您觉得是市场变了,还是口径变了?" +- 升级:"连续两个月trend_down,这不是偶发,是结构性问题。" + +--- + +## 3. 原则二:读动机(不只看表象) + +### 3.1 定义 +数据是人行为的痕迹。同一个"毛利率低",可能是成本高(真差),也可能是返利未确认(口径差)。 +不访谈、不还原场景,就敢下结论 = 报告是废纸。 + +### 3.2 CMA落地工具 +- **业务访谈**:按KPI问业务负责人"这个数怎么来的、最近变了什么" +- **口径拆解/多模型对比**:账面口径 vs 管理口径(如Model C),还原真实经营 +- **场景还原**:把数字放回业务场景(门店、客户、合同、渠道) +- **数据血缘/来源核查**:确认数字本身没错,再谈动机 + +### 3.3 落地步骤(60分钟) +1. 对异常清单每条,先做"口径自检":数字对了吗?口径对吗? +2. 再问"业务自检":最近有什么动作/事件影响它? +3. 访谈至少1个业务负责人,记录原话 +4. 输出动机解释:异常KPI → 表面原因 → 深层动机(谁、为什么、什么场景) + +### 3.4 客户话术 +- "账面看是XX,但您看这个口径拆解——实际是XX。" +- "我猜是XX原因导致的,对吗?"(给客户一个可确认/可纠正的假设,不要下结论) +- "这个数字背后是哪个业务动作?是主动调整还是被动结果?" + +--- + +## 4. 原则三:理关联(不只盯增长) + +### 4.1 定义 +单点数字会骗人,因果链不会。涨跌不只看自己,要看它牵动谁、被谁牵动。 +理清因果 = 找到真正的驱动杠杆,对策才有落点。 + +### 4.2 CMA落地工具 +- **KPI因果链**(kpi_causality):positive/negative 方向 + strength 强度 + lag_months 滞后(现行28条:positive 20 / negative 8) +- **模拟推演**(POST /api/cma/kpi-causality/simulate):改一个KPI,预测下游影响链 +- **四维度联动**:财务/客户/内部流程/学习成长 跨层传导(BSC四层泳道) +- **战略地图**:节点间连线即因果,从KPI追溯到OKR/战略 + +### 4.3 落地步骤(45分钟) +1. 对每个异常KPI,查上游(谁驱动它)+ 下游(它影响谁) +2. 标注强度(>0.7强相关)与方向,找出Top3驱动链 +3. 用模拟推演验证:"如果修复这个驱动,下游能改善多少" +4. 输出因果链图 + 对策建议:优先动"强驱动、可干预"的杠杆点 + +### 4.4 客户话术 +- "这个KPI不是孤立的——它由X驱动(强度0.9),又牵动Y。" +- "真正的问题不在表面这个数,在它上游的X。" +- "我们建议先动X:按模拟推演,X每改善10%,Y能改善约9%。" + +--- + +## 5. 《CMA数据分析报告》结构模板(含三原则应用位置) + +完整可复用模板见 `docs/templates/cma-data-analysis-report-template.md`,骨架如下: + +``` +一、经营总览(一页纸) + - 核心KPI仪表盘(红黄绿)+ 一句话结论【找异常入口】 + +二、异常发现【原则一·找异常】 + - 异常清单表:KPI | 实际 | 预期 | 偏离 | 红黄绿 + - Top3异常重点展开(趋势图+阈值线) + +三、动机解读【原则二·读动机】 + - 每条Top异常:口径自检结论 + 业务动机(谁/为什么/什么场景) + - 管理口径 vs 账面口径对比(如适用) + +四、因果关联【原则三·理关联】 + - 异常KPI的因果链图(上游驱动/下游影响,标注强度方向) + - 模拟推演结果:动哪个杠杆、影响多大 + +五、对策与行动 + - 按"强驱动+可干预"排序的3条建议(对应责任KPI) + - 下期目标修正建议(如需) + +六、附录 + - 数据口径说明 / 预警规则清单 / 访谈记录要点 +``` + +每章都在"讲一个故事":**先让客户看见异常(信号),再让他理解为什么(动机),最后带他看连锁反应和对策(关联)**。 + +--- + +## 6. 样例:陕酣客(陕西酣客文化传媒,entity 1)三原则试跑 + +> 用CMA现行真实数据(2026-08-26 查询):预警规则78条(static 39 + trend_down 39)、因果链28条(positive 20 + negative 8)、营业收入实际值2026-06~08。 + +### 6.1 找异常 +- 营业收入:2026-06 实际129.32 → 2026-07 81.08 → 2026-08 8.08 +- 环比:7月 -37%,8月 **-90%**;静态规则 red < 4000(触发红色);trend_down 阈值10%(远超) +- 毛利率:账面口径 0.13%(静态规则 red < 30,严重偏离) +- 结论:收入崩盘 + 毛利率异常 = 双红色信号,值得深挖 + +### 6.2 读动机 +- 毛利率账面0.13%的原因(访谈+口径拆解):白酒经销模式下,上游厂返利/补贴挂账未确认、渠道补贴冲减收入,账面口径失真 +- 调整为管理口径(Model C:还原厂补+剔除冲减)后,真实毛利率 **51.4%** +- 动机解读:不是经营变差,是"返利确认节奏"和"渠道补贴政策"两个业务动作主导了账面数字——数据背后的活人是酒厂结算员和渠道客户 + +### 6.3 理关联(因果链实证) +| 因果 | 方向 | 强度 | 含义 | +|------|------|------|------| +| 毛利率 → 净利润 | positive | 0.96 | 毛利是利润的第一驱动(强) | +| 上游厂补率 → 净利润 | positive | 0.9 | 厂补确认=真金白银 | +| 渠补率 → 净利润 | negative | 0.85 | 渠道补贴是利润黑洞 | +| 费用率 → 净利润 | negative | 0.8 | 费用管控第二杠杆 | +| 营业收入 → 净利润 | positive | 0.1 | 收入规模对利润贡献很弱(反直觉!) | + +- 洞察:表面看"收入崩了",因果链显示**利润真正的驱动是毛利率(0.96)和厂补率(0.9)**,而收入规模只贡献0.1——所以对策不是"冲收入",而是"确认厂补、压渠补、管费用" +- 模拟推演示意:厂补率每改善10%,净利润传导约 +9%;渠补率每压缩10%,净利润 +8.5% + +### 6.4 一句话故事 +"这个月收入掉了90%,账面毛利0.13%——但拆开口径,真实毛利51.4%;因果链告诉我们利润的命门是厂补确认和渠道补贴,不是收入规模。所以下月重点:催厂补、砍无效渠补。" + +--- + +## 7. 交付流程(标准三步) + +| 步骤 | 动作 | 工具 | 时间盒 | +|------|------|------|--------| +| Step 1 | 跑异常清单 | 预警/KPI看板 | 30min | +| Step 2 | 访谈+口径拆解 | 业务访谈/多模型 | 60min | +| Step 3 | 因果链+对策 | 因果链/模拟推演 | 45min | + +铁律:**没有访谈就写动机 = 编故事;没有因果链就写对策 = 拍脑袋。** + +## 8. 验收与自检清单 + +- [ ] 报告含异常清单(有红黄绿,不只有常态) +- [ ] 每条Top异常有动机解释(有访谈/口径依据,不是猜测) +- [ ] 每个对策能追溯到因果链(有强度/方向/推演支撑) +- [ ] 客户能一句话复述故事("收入掉了但真实毛利51.4%,命门是厂补") + +## 关联 +- 来源:user-insight-9-perspectives.md(视角5,一组数据一个故事) +- CMA能力:alert_rules(78条)/ kpi_causality(28条)/ simulate / 四维度BSC +- 案例:陕酣客(entity 1)—— 账面毛利0.13% → Model C 51.4% diff --git a/docs/templates/cma-data-analysis-report-template.md b/docs/templates/cma-data-analysis-report-template.md new file mode 100644 index 00000000..e59fb77f --- /dev/null +++ b/docs/templates/cma-data-analysis-report-template.md @@ -0,0 +1,92 @@ +# 《CMA数据分析报告》交付模板 + +> 用法:复制本模板,按客户数据填充。三原则贯穿全篇——找异常(第2章)、读动机(第3章)、理关联(第4章)。 +> 配套方法论:`docs/cma-data-analysis-3-principles.md` + +--- + +## 封面 +- 客户名称 / 报告期间 / 交付方(博海科技)/ 日期 + +## 一、经营总览(一页纸) +- 核心KPI仪表盘(表格或卡片,绿/黄/红三色标记) +- 一句话结论(用"数据+故事"句式,如:"收入-90%但真实毛利51.4%,利润命门是厂补确认") + +| KPI | 实际值 | 目标/阈值 | 状态 | 一句话 | +|-----|--------|-----------|------|--------| +| 营业收入 | 8.08 | red<4000 | 🔴 | 环比-90%,崩盘 | +| 毛利率 | 0.13%(账面) | red<30 | 🔴 | 口径失真,真实51.4% | +| ... | ... | ... | ... | ... | + +## 二、异常发现【原则一:找异常】 +### 2.1 异常清单 +| KPI | 实际值 | 预期值 | 偏离度 | 红黄绿 | 首次出现 | +|-----|--------|--------|--------|--------|----------| +| ... | ... | ... | ... | ... | ... | + +### 2.2 Top3异常展开(每个含:趋势图+阈值线+偏离说明) +1. 异常KPI A:趋势 + 触发规则 + 偏离解读 +2. 异常KPI B:... +3. 异常KPI C:... + +## 三、动机解读【原则二:读动机】 +### 3.1 口径自检 +- 数字准确性:来源表/导入批次/负责人确认 +- 口径说明:账面口径 vs 管理口径差异(如Model C还原) + +### 3.2 业务动机(每条Top异常) +| 异常KPI | 表面原因 | 深层动机(谁/为什么/场景) | 依据(访谈/口径) | +|---------|----------|--------------------------|-------------------| +| ... | ... | ... | ... | + +### 3.3 关键访谈记录要点 +- 访谈对象 / 时间 / 原话要点 + +## 四、因果关联【原则三:理关联】 +### 4.1 异常KPI因果链 +- 上游驱动(谁影响它):KPI | 方向 | 强度 | 滞后 +- 下游影响(它影响谁):KPI | 方向 | 强度 | 滞后 + +### 4.2 模拟推演 +- 场景:调整X KPI → 下游Y预测变化(表格) + +### 4.3 关键洞察 +- 反直觉发现(如"收入规模对利润贡献仅0.1,毛利贡献0.96") + +## 五、对策与行动 +### 5.1 优先行动(按强驱动+可干预排序) +| 序号 | 行动 | 驱动KPI | 因果依据 | 预期效果 | 责任KPI | +|------|------|---------|----------|----------|---------| +| 1 | ... | ... | 强度/方向 | ... | ... | + +### 5.2 下期目标修正建议(如需) +- 目标/阈值调整建议 + 理由 + +## 六、附录 +- 数据口径说明 / 预警规则清单 / 完整因果链图 / 访谈记录 / 数据来源 + +--- + +## 客户沟通话术库(按场景) + +**开场** +- "这个月有X个指标在警报区,我们一个个看。" +- "先看结论:整体X,但有两个信号值得注意。" + +**找异常** +- "这个数字偏离目标X%,您觉得是市场变了,还是口径变了?" +- "连续两个月下降,这不是偶发,是结构性问题。" + +**读动机** +- "账面看是XX,但口径拆解后实际是XX。" +- "我猜是XX导致的,对吗?"(给假设,让客户确认/纠正) +- "这是主动调整还是被动结果?" + +**理关联** +- "这个KPI不是孤立的——它由X驱动(强度0.9),又牵动Y。" +- "真正的问题不在表面这个数,在它上游的X。" +- "按模拟推演,X每改善10%,Y能改善约9%。" + +**收尾** +- "一句话总结:XX。(客户能复述,才算讲明白)" +- "下月重点盯X,我们把它写进预警规则。"