feat(platform): close AI expense value loop

Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
This commit is contained in:
caoxiaozhu
2026-07-17 14:14:08 +08:00
parent 242d68c36f
commit 787bc3a481
507 changed files with 82072 additions and 6344 deletions

View File

@@ -0,0 +1,141 @@
# 节省事实账本与 CFO 经营价值看板 开发 TODO
更新时间2026-07-17
## 使用规则
- 每条 TODO 必须回链 `CONCEPT.md` 的章节或语义段落。
- 只有代码、接口或容器验证提供真实证据后才能勾选 `[x]`
- 现金节省、工时价值、风险暴露和预计机会始终分开,不以演示数据代替缺失事实。
- 实施顺序为:安全前置 → 账本 → 首条闭环 → 基线/分析 → CFO 前端 → PostgreSQL/E2E → 文档收口。
## 1. 调研与边界
- [x] [CONCEPT: 背景与问题] 盘点财务看板、预算、Claim、Expense Case、Business Event、风险、审批、付款和标准重算事实源。
证据:`finance_dashboard.py``finance_dashboard_snapshot.py``financial_record.py``expense_cases.py``expense_claim_standard_adjustment.py``expense_claim_approval_flow.py` 的只读审计。
- [x] [CONCEPT: 目标与非目标] 确认风险金额、未用预算、预计机会和未确认结果不得进入已确认节省。
证据CONCEPT「目标与非目标」「算法与公式」。
- [x] [CONCEPT: 用户与场景] 选择住宿职级标准重算作为首条纵向闭环,不选择证据不足的重复支付或供应商议价。
证据:现有标准重算和付款业务事件可形成最短可信证据链;`AccountsPayableRecord` 已有租户字段,但仍缺合同价、数量、单位价格和外部付款事实。
- [x] [CONCEPT: 风险与开放问题] 识别旧财务聚合全表读取、快照键缺租户、接口缺角色限制和标准重算信任客户端原金额问题。
证据:`FinanceDashboardMetricMixin._fetch_claims()``_fetch_budget_allocations()``FinanceDashboardSnapshotService._cache_key()``accept_standard_adjustment()`
## 2. 契约与设计
- [x] [CONCEPT: 数据与契约] 定义 baseline、opportunity、realization、evidence 和 append-only event 的职责与关键字段。
证据CONCEPT「数据与契约」。
- [x] [CONCEPT: 算法与规则] 定义 identified → accepted → in_progress → realized → verified → reversed 状态和 rejected/expired 终态。
证据CONCEPT「状态转换」。
- [x] [CONCEPT: 算法与规则] 定义 benefit key、canonical realization、归因比例和追加冲回规则。
证据CONCEPT「收益去重」。
- [x] [CONCEPT: 权限] 定义租户、finance/executive、预算范围、管理员只读、自证禁止和数据范围。
证据CONCEPT「权限」。
- [x] [CONCEPT: 算法与公式] 定义 3 个主 KPI、驱动指标、风险护栏和缺数据边界。
证据CONCEPT「算法与公式」。
## 3. 安全前置
- [x] [CONCEPT: 后端] 让旧财务看板显式接收可信租户与数据范围Claim/预算查询不得全表混算。
证据:`finance_dashboard_access_policy.py``finance_dashboard_scope.py``finance_dashboard_budget.py``finance_dashboard.py`Claim 直接按结构化 `ExpenseClaim.tenant_id` 首层过滤,预算历史兼容仅限 default 租户。
- [x] [CONCEPT: 后端] 给财务快照缓存键和后台任务加入租户/数据范围,禁止跨租户复用。
证据:`finance_dashboard_snapshot.py``finance_dashboard_scheduler.py`;快照键包含 tenant 与 scope fingerprint调度入口必须显式携带租户。
- [x] [CONCEPT: 权限] 给财务与 CFO 分析接口增加后端角色和范围校验,管理员身份不自动获得业务确认权。
证据:`finance_dashboard_access_policy.py``savings_access_policy.py``agent_run_access_policy.py``GET /api/v1/analytics/cfo-value`;容器租户/缓存/角色回归 17 项通过。
- [x] [CONCEPT: 第一条可信机会] 标准重算只使用锁定数据库明细原金额,客户端原金额不能影响结果和节省。
证据:`expense_claim_standard_adjustment.py`;原金额只读锁定 `ExpenseClaimItem.item_amount`,政策结果只由服务端重算,陈旧版本使用内容指纹并降低基线质量等级。
- [x] [CONCEPT: 后端] 将标准重算、审计和账本写入收口到同一事务边界。
证据:`expense_claim_standard_adjustment.py``commit=False` 写审计,同一 API 事务写 Claim、Baseline、Opportunity、Evidence、SavingsEvent 和 BusinessEvent事务失败整体回滚。
## 4. Savings Ledger 后端
- [x] [CONCEPT: 数据与契约] 新增 `profile_baseline_snapshots``savings_opportunities``savings_realizations``savings_evidence_links``savings_events` 模型。
证据:`savings.py`(模型)及 `test_savings_models.py`;五类事实分离保存基线、机会、结果、证据和不可变操作。
- [x] [CONCEPT: 数据与契约] 新增 Alembic 0015、迁移所有权、复合租户外键、唯一键、检查约束、索引和 append-only 触发器。
证据:`20260716_0015_savings_value_ledger.py``migration_preflight.py``schema_ownership.py`;一次性 PostgreSQL 17 空库完整迁移循环通过。
- [x] [CONCEPT: 后端] 实现 discovery、action、realization、query、access policy 和 response builder 独立服务。
证据:`savings_discovery.py``savings_actions.py``savings_realization.py``savings_query.py``savings_access_policy.py``savings_read_projection.py``savings_protocol.py`
- [x] [CONCEPT: 后端] 实现分页、筛选、排序、详情、可用动作、证据和事件 DTO。
证据:`GET /api/v1/savings/opportunities``GET /api/v1/savings/opportunities/{id}``savings.py`schema/API`test_savings_endpoints.py`
- [x] [CONCEPT: 算法与规则] 实现版本锁、请求指纹、不可变响应重放、收益去重和归因比例约束。
证据:`SavingsRequestProtocol`、数据库唯一/检查约束与 `test_savings_concurrency_postgres.py`;同 request 并发仅一次写入,同 benefit 双确认仅一个 canonical winner。
- [x] [CONCEPT: 状态转换] 实现 accept/start/record/verify/reject/expire/reverse 合法与非法转换。
证据:`savings_actions.py``savings_realization.py``test_savings_ledger_services.py`;负向 reversal 只追加,不改写原已确认金额。
- [x] [CONCEPT: 证据与审计] 把 Savings 关键动作写入同 Case 的 `BusinessEvent` 并保留 correlation/causation。
证据:`ExpenseCaseService` 资源链接与 `savings_*` 服务;端到端测试核验 opportunity/payment/action/confirm/reverse 事件同 Case 可回放。
- [x] [CONCEPT: 后端] 新增历史标准重算机会 dry-run/apply 回填脚本;缺 Case/租户/政策证据只输出数据质量报告。
证据:`savings_standard_adjustment_backfill.py``backfill_standard_adjustment_savings.py`;显式租户/目标库、指纹重算、批次锁、稳定键重放,容器直接测试 8 项通过。
## 5. 首条真实纵向闭环
- [x] [CONCEPT: 第一条可信机会] 接受住宿标准重算时冻结服务端原金额、政策输入/版本、目标金额、差额、维度和证据。
证据:`SavingsDiscoveryService`冻结 `ProfileBaselineSnapshot` 与内容指纹,政式发布版本为 complete内容指纹版本显式标记 partial。
- [x] [CONCEPT: 第一条可信机会] 同事务以稳定 opportunity key 创建或重放机会,并进入 in_progress。
证据:稳定键由 tenant + claim + item + policy version + calculation fingerprint 生成;重试返回原机会。
- [x] [CONCEPT: 后端] 付款业务事件后同事务创建 actual realization重复付款事件不重复计入。
证据:`expense_claim_approval_flow.py`调用 `realize_paid_claim()`PostgreSQL 同付款事件并发结果为 `[0, 1]`,仅一条 realization 和一条完成事件。
- [x] [CONCEPT: 财务确认] 实现独立财务 verify/reject未确认实际结果不得进入主 KPI。
证据:所有人工实际结果必须至少一条可追溯证据;独立确认人会固化证据复核人/时间pending 结果在 CFO 口径中为 0。
- [x] [CONCEPT: 冲回能力] 实现补付/申诉/归因修正的负向 reversal原确认不可删除。
证据:原 actual 保留 `finance_confirmed`,新增负向 canonical reversal`as_of` 可回放冲回前结果。
- [x] [CONCEPT: 权限] 验证申请人、机会负责人、结果填报人、纯管理员和跨租户用户不能自证或越权。
证据:`SavingsAccessPolicy`与 PostgreSQL 并发安全测试owner/recorder/admin-only 自证拒绝,独立 finance 可确认,跨租户无副作用。
## 6. 费用基线与经营分析
- [x] [CONCEPT: 基线快照] 按员工、部门、费用类型、城市、项目和流程从租户安全真实数据生成基线窗口、样本量和版本。
证据:`savings_fact_scope.py``savings_baseline_generation.py``savings_insights.py`;金额五维使用已归档明细中位数,流程维度只使用同租户报销提交时间与首个 `payment_completed` 业务事件,冻结 elapsed minutes、窗口、样本量、算法版本、查询指纹、质量、证据和审计事件`source_workflow_cycle_count` 明确披露覆盖,活跃工时保持 unavailable。
- [x] [CONCEPT: 基线快照] 供应商数据缺少租户/合同事实时显示 coverage gap不从应付模拟种子生成可信基线。
证据:基线和洞察 API 均返回 `supplier_dimension_unavailable` / `supplier_price_drift_unavailable`,要求核验供应商、数量和单位价格;不会读取 `AccountsPayableRecord` 模拟事实或创建货币化机会。
- [x] [CONCEPT: 算法与规则] 实现预算预测、描述性异常归因、重复小额浪费、历史偏离和只读政策模拟准备项;证据不足时不自动货币化。
证据:`savings_insight_budget.py` 复用预算配置/核销事实并以 `min(as_of, window_end)` 截止;`savings_insight_analysis.py``savings_insight_attribution.py` 输出历史偏离、部门/费用类型/城市/项目异常集中和版本化政策模拟必需输入。所有缺少正式反事实的候选 `estimated_savings=None``created_opportunity_ids=[]`,稳定重放不写 `SavingsOpportunity`
- [x] [CONCEPT: 后端] 实现 `GET /analytics/cfo-value`,统一时间、维度、币种、状态和 `as_of` 口径。
证据:`cfo_value.py`API/schema`cfo_value_analytics.py`;租户、角色、预算范围、时间和维度过滤均在服务端执行。
- [x] [CONCEPT: 算法与公式] 实现确认现金、工时待采集、安全直通率资格、漏斗、兑现周期、逾期、来源和护栏聚合。
证据:确认现金只求和 finance_confirmed + canonical + cash工时和安全直通率显式返回 collecting漏斗、趋势、驱动、逾期、冲回和数据质量不与主 KPI 混算。
- [x] [CONCEPT: 降级策略] 多币种、工时、外部付款和审计结果缺失时返回明确数据质量状态,不伪造 0。
证据:多币种按原币分组;无人工活跃时间/审计事实时返回 collecting/unavailable 和 coverage gap不使用 mock 回退。
## 7. CFO 前端
- [x] [CONCEPT: 前端] 在分析看板新增 `value` 入口,并把 dashboard 状态同步 URL。
证据:`useTopBarOverviewRange.js``AppShellRouteView.vue``OverviewView.vue``dashboard=value` 与价值筛选/页码写入 query刷新和返回可恢复。
- [x] [CONCEPT: 前端] 新增独立 CFO 组件、composable、API service 和展示模型,不继续扩大 `useOverviewView.js`
证据:`CfoValueDashboard.vue``CfoValueTrendChart.vue``CfoValueOpportunityDrawer.vue``CfoValueActionDialog.vue``useCfoValueDashboard.js``analyticsValue.js``cfoValueDashboardModel.js`;业务组件和状态职责已拆分,核心文件均低于 800 行。
- [x] [CONCEPT: CFO 看板] 实现主 KPI、护栏、价值漏斗、趋势、来源/组织驱动、机会表和数据质量。
证据:`CfoValueDashboard.vue``cfo-value-dashboard.css`;现金、工时和直通率分卡,趋势按币种切换,预计/实际/确认/冲回不混算,缺数据显式展示。
- [x] [CONCEPT: 前端] 实现时间、部门、费用类型、价值类型筛选和项目/供应商/城市/负责人高级筛选。
证据:顶部时间窗口与价值 query 联动,`createEmptyValueFilters``readValueFiltersFromQuery``writeValueFiltersToQuery` 和 API query 白名单覆盖全部筛选字段。
- [x] [CONCEPT: 前端] 实现基线、建议、执行、实际、确认、去重和证据详情。
证据:`CfoValueOpportunityDrawer.vue` 展示冻结基线、机会状态、实际净值、记录人、canonical 去重、财务确认人/时间/说明、证据索引和不可变事件;无可追溯凭证时不开放手工实际结果登记。
- [x] [CONCEPT: 前端] 实现单据、风险、预算、维度下钻与返回状态恢复。
证据:`cfoValueSourceLinks.js` 统一构造来源路由Claim 进入 `app-document-detail`,风险携带最小 focus/观察/决策参数和现有锚点,预算进入 `app-budget` 配置视图并应用部门/费用类型焦点,维度返回 `app-overview?dashboard=value` 相应筛选。`useCfoValueDashboard.js``value_opportunity` 恢复抽屉,并在非法 ID、403/404、跨租户不可见或不符合当前筛选/时间窗口时安全清除;`useAppShell.js` 从单据详情恢复 CFO 查询。限制:预算中心仍是演示配置视图,页面明确金额不是当前机会的真实预算事实;未覆盖费用科目显示未配置而不是零预算。
- [x] [CONCEPT: 降级策略] 区分零、无数据、基线不足、无权限、失败、部分数据和快照过期;删除 CFO 演示回退。
证据:`classifyCfoDashboardState``buildValueKpis` 与页面状态区;接口失败不读取 `data/metrics.js` 或 demo/fallback 数字。
- [x] [CONCEPT: 前端] 完成移动端、键盘、焦点、44px 触控和无障碍状态提示。
证据CFO 样式移动断点、44px 按钮、语义化 `label`/`role=alert`/`aria-live`、抽屉关闭标签及趋势表格降级;生产构建通过。
## 8. 测试与验证
- [x] [CONCEPT: 测试方案] 后端状态、金额、权限、租户、幂等、证据、去重、冲回和看板聚合单测通过。
证据:容器组合回归 84 项通过;本轮 Savings/CFO 基线、洞察、端点、账本、回填与 E2E 组合 `34 passed, 6 skipped`6 项为未配置 PostgreSQL 专用 URL 的预期跳过。
- [x] [CONCEPT: 测试方案] 标准重算客户端金额伪造、政策失败、Case 缺失和事务回滚测试通过。
证据:`test_expense_claim_service.py -k standard_adjustment` 8 项加付款集成 1 项通过HTTP 标准重算 1 项通过。
- [x] [CONCEPT: 测试方案] 前端数据归一化、状态、筛选、URL、证据动作和响应式测试通过。
证据:容器内 `cfo-value-dashboard.test.mjs` 14 项通过,新增机会 URL 恢复/清理、筛选上下文、风险/单据/预算/维度链接和预算非事实口径断言;与 App Shell 返回链、路由加载和筛选样式组合回归 37 项通过;带凭证序列化和无证据入口 fail-closed 均有断言。
- [x] [CONCEPT: 测试方案] 一次性 PostgreSQL 空库迁移、重复升级、约束、append-only、无损降级和并发测试通过。
证据:一次性 PostgreSQL 17 迁移循环通过;`test_savings_concurrency_postgres.py` 6 项通过覆盖重放、canonical 竞态、跨租户、独立确认、付款单事实和 append-only DB 触发器。
- [x] [CONCEPT: 集成] 住宿标准重算 → 机会 → 审批 → 付款 → 实际 → 财务确认 → CFO 看板 E2E 通过。
证据:`test_savings_value_e2e.py` 从服务端政策差额、付款动作、待确认排除、独立财务确认到 CFO 金额对账单项通过。
- [x] [CONCEPT: 集成] 确认后负向冲回和报告 `as_of` 回放 E2E 通过。
证据:`test_savings_value_e2e.py``test_cfo_value_analytics.py`同时验证当前净值归零和冲回前历史金额回放。
- [x] [CONCEPT: 容器验证] 相关 pytest、Ruff、前端测试和生产构建均在 `local-x-financial-linux` 内通过。
证据Savings/CFO 相关切片与端到端均通过fresh PostgreSQL 总探针 `87 passed / 0 skipped / 0 failed`,其中 Savings 并发 6 项Web 全量 `815 passed / 0 failed` 与 Vite build 通过;新增 Python 文件 Ruff 和 `git diff --check` 通过。
## 9. 文档收尾
- [x] [CONCEPT: 指标与验收] 逐项核对 A1-A8并把文件、接口、迁移、测试和运行结果写回证据。
证据A1 纵向闭环由 `test_savings_value_e2e.py`A2-A4/A4.1 由 Savings schema、`cfo_value_analytics.py`、基线/分析测试A5-A6 由 CFO 组件、来源下钻和前端状态测试A7 由 0015 迁移与 PostgreSQL 并发A8 由本节最终容器汇总证明。
- [ ] [CONCEPT: 风险与开放问题] 记录付款证据等级、汇率、双人复核、工时口径和试点目标的最终边界。
证据:
- [x] [CONCEPT: 本轮实现记录] 同步更新上位 `ai-expense-closed-loop-and-value-proof` 文档,不删除历史证据。
证据:上位 TODO 已回填 Savings Ledger、状态机、CFO 看板、下钻、证据详情和 E2E历史证据原样保留。