From 4f8b5b86c6a1235d660960fddb57fab9a38e7e29 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 3 Sep 2026 03:27:06 +0000 Subject: [PATCH] docs: drop the per-work-item process files from docs/; they live on the work items Method grading notes, the compliance checklist and the self-test report are per-work-item artifacts and are kept on the corresponding GitHub issues, as the dev-design / dev-issue / dev-test skills prescribe. Keeping copies under docs/ forced a rewrite every time the baseline moved. README and CLAUDE.md pointers updated; docs/ now holds the requirement, the design baseline, the intake report, the blueprint and the Word deliverables. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01VR2khJ3Me96btawVsfG6jb --- CLAUDE.md | 6 +- README.md | 3 - ...71\346\241\210\345\210\206\347\272\247.md" | 116 ----- ...10\345\272\246\346\270\205\345\215\225.md" | 63 --- ...52\346\265\213\350\256\260\345\275\225.md" | 463 ------------------ 5 files changed, 3 insertions(+), 648 deletions(-) delete mode 100644 "docs/03-\346\226\271\346\241\210\345\210\206\347\272\247.md" delete mode 100644 "docs/04-\351\234\200\346\261\202\347\254\246\345\220\210\345\272\246\346\270\205\345\215\225.md" delete mode 100644 "docs/05-\350\207\252\346\265\213\350\256\260\345\275\225.md" diff --git a/CLAUDE.md b/CLAUDE.md index c6e0c44..d823011 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,8 +2,8 @@ 本仓库是基于 ObjectStack 平台(`@objectstack/*` 17.x)开发的 KPI 考核管理系统。 流程资产采用 `os-project-*` skill 库(baozhoutao/os-project-skills);领域知识 skill -(`os-project-kb-<行业>`)待需求定稿后再建。**开工前必读**:`docs/需求/KPI考核管理系统-功能性需求文档-V1.3.md`(客户原始需求)、`docs/00-设计方案.md`(V1.0 客户确认版,唯一需求基准)、`docs/01-需求解读报告.md`、`docs/02-总体方案蓝图.md`、 -`docs/03-方案分级.md`、`docs/04-需求符合度清单.md`。 +(`os-project-kb-<行业>`)待需求定稿后再建。**开工前必读**:`docs/需求/KPI考核管理系统-功能性需求文档-V1.3.md`(客户原始需求)、`docs/00-设计方案.md`(V1.0 客户确认版,唯一需求基准)、`docs/01-需求解读报告.md`、`docs/02-总体方案蓝图.md`; +方案分级要点、需求符合度清单、测试报告一律挂在对应工作项(GitHub issue)评论上,不再作为 `docs/` 文件维护(维护者 2026-09-03 决定)。 ## 构建与验证 @@ -50,5 +50,5 @@ pnpm dev # http://localhost:3000 ;Console 在 /_console/ ;管理员 adm | F2 合并权限 | 无——仅库级授权生效(AI 不自行合并) | | G1 失败升级 | 工作项评论 @ 技术负责人 | | G2 平台问题上报 | objectstack-ai/objectstack 仓库 issue(现象、最小复现、期望能力、平台版本);**只上报,不修复**——不认领平台 issue、不在平台仓开 PR(维护者 2026-09-03 要求) | -| H1 开工前必读 | 本文 + `docs/` 四份文档 | +| H1 开工前必读 | 本文 + 需求文档 + `docs/00`~`02` | | H2 测试执行 | 暂不具备双实例条件:同会话自测,计划先落盘、逐条对照(dev-test 最低限度档) | diff --git a/README.md b/README.md index 2bc7138..0ebb16e 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,6 @@ | [docs/00-设计方案.md](docs/00-设计方案.md) | 设计方案 V1.0(客户 2026-09-02 确认,唯一需求基准);Word 成品在 [docs/交付/](docs/交付/)(V0.9 送审稿、V1.0 客户确认版),由 `pnpm docs:docx` 从本源稿生成 | | [docs/01-需求解读报告.md](docs/01-需求解读报告.md) | 场景地图、平台能力覆盖度、风险、疑点(疑点已由设计方案 V1.0 关闭) | | [docs/02-总体方案蓝图.md](docs/02-总体方案蓝图.md) | os 能力映射、对象模型总图、模块依赖与开发顺序 | -| [docs/03-方案分级.md](docs/03-方案分级.md) | 三问分级;三处多路径选型已确认路径 A | -| [docs/04-需求符合度清单.md](docs/04-需求符合度清单.md) | 需求逐条 ✅/⚠️/❌ 对账 | -| [docs/05-自测记录.md](docs/05-自测记录.md) | 静态门禁 + 端到端流程用例结果与问题记录 | | [CLAUDE.md](CLAUDE.md) | 开发约定与 dev-issue 启用清单 | ## 快速开始 diff --git "a/docs/03-\346\226\271\346\241\210\345\210\206\347\272\247.md" "b/docs/03-\346\226\271\346\241\210\345\210\206\347\272\247.md" deleted file mode 100644 index 9b215f3..0000000 --- "a/docs/03-\346\226\271\346\241\210\345\210\206\347\272\247.md" +++ /dev/null @@ -1,116 +0,0 @@ -# KPI 考核管理系统 方案分级(dev-design) - -> 依据 os-project-dev-design 三问判定树逐模块分级。本项目是**空仓库新建应用**,不存在 -> 「已有资产」,第 1 问(改已有)与第 2 问(跨模块)对所有工作项均为「否」;第 3 问 -> (多路径)命中 3 处,按 proposal-template 挂方案要点。 -> -> **执行说明**:三处方案要点 D-01 / D-02 / D-03 已由客户在《设计方案》V1.0 表 7 -> 第 1 / 2 / 3 项确认为推荐路径 A(2026-09-02)。本文为方案分级留痕副本(方案要点 -> 早于工作项系统建立,故以文件留存)。 - -## 一、低风险分级声明(免审开工,留痕) - -### 方案分级:低风险 —— M1 组织与权限 -- 需求理解:管理员维护部门 / 分公司 / 分管关系;六类角色按四级数据范围看到并操作自己范围内的数据(需求 §2)。 -- 实现方式:新增岗位 5、权限集 6(全部新增);复用平台 `sys_business_unit`、`sys_user`,不改其结构;岗位↔权限集绑定在 `onEnable` 幂等写入(新增)。 -- 三问:改已有=否(平台对象只引用不改);跨模块=否(本项目内无既有模块);多路径=是 → 见 D-01。 - -### 方案分级:低风险 —— M2 指标库 -- 需求理解:人力审核集中维护指标定义、计分规则与版本(§1.2、§1.4)。 -- 实现方式:新增对象 `kpi_indicator`、`kpi_indicator_step`,列表 / 表单视图,状态机校验;全部新增。 -- 三问:改已有=否;跨模块=否;多路径=否(单对象台账做法唯一)。 - -### 方案分级:低风险 —— M3 考核方案(除发布生成) -- 需求理解:人力审核建方案版本、下达指标、配考核关系与到人分工,发布后冻结(§3-1、§3-2)。 -- 实现方式:新增对象 `kpi_plan`、`kpi_plan_step`、`kpi_plan_subject`、`kpi_plan_indicator`、`kpi_dispute`、`kpi_staff_assignment`、`kpi_personal_item` 及视图;发布 hook(C-3)见 D-02 附带说明。 -- 三问:改已有=否;跨模块=否;多路径=否(主从建模做法唯一)。 - -### 方案分级:低风险 —— M5 汇总与归档(展示层) -- 需求理解:四维汇总可钻取、归档快照不可变(§3-6、§3-7)。 -- 实现方式:新增 `kpi_result`、`kpi_snapshot`、数据集 3、报表 3、看板 2;快照只读 hook(C-5);全部新增。 -- 三问:改已有=否;跨模块=否;多路径=否。 - -## 二、高风险方案要点(第 3 问命中,已确认) - -### 方案要点:D-01 考核主体的承载 —— 平台组织单元 vs 自建组织对象 - -**需求理解**:考核主体是总公司各业务部门 + 14 家分公司,角色数据范围按「全部 / 分管 / 本部门 / 仅本人」控制(§1.4、§2.2)。 - -**风险分级依据**:改已有=否;跨模块=否;多路径=**是**(两条建模路径)。 - -**影响范围** - -| 类型 | 名称 | 动作 | 影响说明 | -|---|---|---|---| -| 对象/字段 | `sys_business_unit`(平台) | 引用 | 作为主体、责任板块、下达对象的查找目标 | -| 对象/字段 | `kpi_plan_subject.subject / assessor_unit`、`kpi_indicator.owner_unit`、`kpi_entry_sheet.subject` 等 9 处查找字段 | 新增 | 全部指向 `sys_business_unit` | -| 权限点 | 6 个权限集 + 按方案动态写入的共享规则 | 新增 | 本部门 / 分管范围由共享规则放宽 | -| 共享资产 | 无 | — | — | - -**备选方案** - -| 方案 | 思路 | 优点 | 缺点/风险 | AI 建议 | -|---|---|---|---|---| -| A | 复用平台 `sys_business_unit` 作为考核主体 | 数据范围(unit / unit_and_below)原生按它解析;组织维护走平台 Setup;无重复主数据 | 分管领导关系需放在方案参与主体上(平台单元只有一个负责人字段) | ✅ 推荐:数据范围是硬需求,自建对象无法接入平台权限深度 | -| B | 自建 `kpi_org_unit` 对象 | 字段完全自定(主体类型、分管领导) | 权限深度无法按自建树解析,需自写行级安全;组织数据两份 | | - -**待确认点**:[x] 分管领导按方案版本配置在参与主体上——已确认(表 7 第 1 项)。 - -**兼容与回归**:存量数据不涉及;老功能回归点无(新建项目)。 - -### 方案要点:D-02 计分口径的承载 —— 服务端计分引擎(hook)vs 平台公式字段 - -**需求理解**:填报即时出分、口径唯一可重复计算、过程透明可复核,现行公式复杂且跨表(§1.1、§1.2、§4)。 - -**风险分级依据**:改已有=否;跨模块=否;多路径=**是**。 - -**影响范围** - -| 类型 | 名称 | 动作 | 影响说明 | -|---|---|---|---| -| 对象/字段 | `kpi_entry_line.completion_rate / score_rate / score / calc_trace` | 新增(只读,系统写) | 由引擎回写 | -| 对象/字段 | `kpi_indicator.scoring_method / cap_rate / floor_rate / score_formula`、`kpi_indicator_step` | 新增 | 规则数据 | -| 流程/Hook | `kpi_score_entry_line`(before insert / update) | 新增 | 明细任何写入路径(表单、内联网格、导入、调整落地)都触发 | -| 代码点 | `src/lib/scoring.ts` 纯函数 + 单元测试 | 新增 | 口径唯一真值 | - -**备选方案** - -| 方案 | 思路 | 优点 | 缺点/风险 | AI 建议 | -|---|---|---|---|---| -| A | 服务端计分引擎(纯函数)+ before 写入 hook 回写 | 支持阶梯 / 区间 / 公式;可单元测试;计算说明可落库;导入也走同一口径 | 定制代码 1 处;公式方法需引入平台 CEL 引擎 | ✅ 推荐:平台公式字段不能跨对象、不能表达阶梯,只能覆盖线性一种 | -| B | 平台公式字段 | 零代码 | 只能引用本记录字段、无法表达阶梯与自定义公式;计算过程不可落库复核 | | - -**待确认点**:[x] 公式表达式语言采用平台 CEL——已确认(表 7 第 2、12 项);[x] 计分口径 Q-01~Q-05 已按表 7 第 2 / 4 / 5 / 6 / 7 项确认。 - -**兼容与回归**:不涉及。 - -### 方案要点:D-03 审核流程的承载 —— 自建审核状态机 vs 平台审批节点 - -**需求理解**:部门填报 → 分公司并行核对 → 人力审核 → 领导审批;驳回原因必填、退回上一节点、全程留痕;流程可配置且随方案版本冻结(§1.2、§3)。 - -**风险分级依据**:改已有=否;跨模块=否;多路径=**是**。 - -**影响范围** - -| 类型 | 名称 | 动作 | 影响说明 | -|---|---|---|---| -| 对象/字段 | `kpi_entry_sheet.status / current_step / last_reject_reason` | 新增 | 状态机载体 | -| 对象/字段 | `kpi_plan_step`、`kpi_check_task`、`kpi_review_record` | 新增 | 节点定义、并行核对、留痕 | -| 流程/Hook | `kpi_sheet_transition`(before / after update) | 新增 | 所有改状态的入口(按钮、API)都经它 | -| 按钮 | 提交 / 确认核对 / 提出争议 / 审核通过 / 驳回 / 归档 | 新增(script action,驳回带必填原因参数) | 只改状态字段,规则在 hook | - -**备选方案** - -| 方案 | 思路 | 优点 | 缺点/风险 | AI 建议 | -|---|---|---|---|---| -| A | 自建状态机:状态字段 + state_machine 校验 + hook 推进/校验 + 记录对象留痕 | 驳回原因必填可服务端强制;节点顺序与审核岗位随方案版本冻结;并行核对按分公司拆记录聚合;留痕对象可直接查询 | 定制代码 1 处(状态机),不复用平台审批收件箱 | ✅ 推荐:平台审批节点不能声明式强制驳回原因、流程元数据不随方案版本变化 | -| B | 平台审批节点(approval,unanimous / per_group)+ 回退边 | 平台审批收件箱、委托、升级开箱即得 | 驳回原因不可强制;节点顺序写死在流程元数据,「流程随方案版本冻结」做不到;并行核对的「争议」语义无对应 | | -| C | A + B 混合:自建状态机推进,同时用平台通知节点提醒 | 兼得留痕与提醒 | 二期考虑 | | - -**待确认点**:[x] 审核人从「待我处理」队列进入——已确认(表 7 第 3 项);[x] 驳回口径按表 7 第 8 项。 - -**兼容与回归**:不涉及。 - ---- - -*放行记录:客户方 2026-09-02 确认《设计方案》V1.0 表 7 第 1 / 2 / 3 项,D-01 / D-02 / D-03 均按推荐路径 A 执行;无未关闭决策项。* diff --git "a/docs/04-\351\234\200\346\261\202\347\254\246\345\220\210\345\272\246\346\270\205\345\215\225.md" "b/docs/04-\351\234\200\346\261\202\347\254\246\345\220\210\345\272\246\346\270\205\345\215\225.md" deleted file mode 100644 index 62fd8a7..0000000 --- "a/docs/04-\351\234\200\346\261\202\347\254\246\345\220\210\345\272\246\346\270\205\345\215\225.md" +++ /dev/null @@ -1,63 +0,0 @@ -# KPI 考核管理系统 需求符合度清单 - -> os-project-dev-issue 步骤 4.5:逐条对照**《设计方案》V1.0**(`docs/00-设计方案.md`,客户方 -> 2026-09-02 确认第 10 章全部 17 项 —— 第 1~12 项同意产品默认,第 13 / 14 / 15 项选 A), -> 标 ✅ 完整实现 / ⚠️ 有偏差 / ❌ 未实现;非 ✅ 条目只有三个出口(退回细化 / 平台受限挂起 / -> 升级裁决),没有「先简化实现」。需求条目沿用需求 V1.3 的编号,口径以设计方案为准。 -> 章节引用一律指向设计方案(如「设计 5.2」「第 10 章第 10 项」「表 10」),不再引用疑点编号。 -> 证据列指向代码与自测记录(`docs/05-自测记录.md`,用例编号 T-xx;每个编号都能在该文件 §3 总表查到)。 - -## 一、核心业务流程(需求 §3 / 设计第 4 章) - -| # | 需求 | 态 | 实现落点 / 证据 | 偏差与出口 | -|---|---|---|---|---| -| 1 | 配置方案:新建版本 → 选指标 → 分权重与目标 → 配考核关系与参与范围 | ✅ | `kpi_plan` + 主从 `kpi_plan_step / kpi_plan_subject / kpi_plan_indicator / kpi_staff_assignment / kpi_personal_item`,内联网格录入(设计 5.2);T1、T2 | — | -| 2 | 发布前完整性检查 | ✅ | `PlanPublishHook`:流程节点合法、主体非空、每主体有指标、权重合计 100%、目标值齐、争议关闭,逐条列出问题;T3 | — | -| 3 | 发布:冻结参与范围、流程与口径,生成指标下达与到人分工,发起流程 | ✅ | 发布后子配置冻结(`PlanChildFreezeHook`,T7);为每个主体生成填报单与明细并冻结目标 / 权重 / 计分方式副本(T5、T6);到人分工随方案配置,发布后冻结 | 「发起流程」= 生成填报单进入「填报中」;站内通知与到期提醒在设计表 10 列为二期候选 | -| 4 | 填报:按填报视图录入或批量导入 → 自动保存 → 即时算分 → 提交冻结 | ✅ | 填报单表单内嵌明细网格 + 明细列表内联编辑;`EntryLineScoreHook` 保存即算分(T8、T9、T11);提交后明细冻结(T16);导入走平台 CSV 向导、xlsx 导出 | xlsx **直接导入按钮**按设计表 10 列为二期候选(现走平台通用导入),不计为本期偏差 | -| 5 | 核对与审核:分公司并行核对 → 人力审核 → 领导审批;驳回填原因、退回上一环节 | ✅ | 提交按分公司生成并行核对任务(T15),争议阻断推进(T18),全部确认自动推进(T19);驳回原因必填(T20),退回上一节点并重置核对(T21,设计表 7 第 8 项);人力 → 领导 → 已通过(T22、T23) | — | -| 6 | 计分:按既定口径自动算每个指标得分 | ✅ | `src/lib/scoring.ts` 四种方式 + 单元测试 14 条;计算说明落库;逆向指标完成率 = 目标 ÷ 实际(第 10 章第 4 项)、个人得分 = 板块折算 + 承接项相加不封顶(第 5 项)、计分方式清单四种(第 12 项),`src/lib/aggregate.ts` 顶部注释已改引设计第 8 章表 6;T8、T9、T11、T37 | — 口径已由第 10 章第 4 / 5 / 12 项确认,无偏差 | -| 7 | 汇总:部门 / 分公司 / 到人 / 分管领导四维,可钻取到指标得分明细 | ✅ | `kpi_result` 由 `results-service` 在通过 / 归档 / 调整落地时生成(T24、T27);加减分审批落定后由 `BonusAfterDecideHook` 立即重算(T48、T49、T60);计算明细 JSON 逐指标带得分率、得分与「已调整」标记(T63);看板 2、矩阵报表可钻取 | — | -| 8 | 归档:审批通过后生成不可变快照,数据锁定 | ✅ | `kpi_snapshot`(SHA-256 校验和)+ `SnapshotImmutableHook`(T28~T30);归档后明细、加减分锁定(T31、T32);归档不可改、下一周期修正(第 10 章第 11 项) | — | - -## 二、范围功能(需求 §1.3 / 设计第 5 章) - -| # | 需求 | 态 | 实现落点 / 证据 | 偏差与出口 | -|---|---|---|---|---| -| 9 | 指标管理(定义、来源、计分方式、权重、版本可追溯) | ✅ | `kpi_indicator` + `kpi_indicator_step`(设计 5.1);字段历史跟踪;版本号 + 替代版本;状态机 | — | -| 10 | 考核方案多版本并存、发布后冻结、复制上一版 | ✅ | `kpi_plan` 状态机;`PlanCloneHook` 复制节点 / 主体 / 下达 / 分工(T35);同周期唯一生效版本见 #26 | — 「草稿与已发布并存、同周期只有一个生效版本」= 第 10 章第 10 项确认口径,已按此实现 | -| 11 | 流程可配置、随方案版本冻结 | ✅ | `kpi_plan_step` 数据化节点;两节点流程验证(T36) | — 粒度 = 可增删节点、可改审核岗位(第 10 章第 9 项);「不同部门走不同流程」在设计表 10 列为二期候选 | -| 12 | 加减分 | ✅ | `kpi_bonus` 主从填报单,审批后计入最终得分(T25、T60);**提出 = 人力审核、审批 = 人力负责人**(第 10 章第 6 项,本工作项落地:`src/hooks/bonus.hook.ts` 的 `requiredBonusPosition`;真实岗位账号取证 T65~T68,反例 T59) | — 不封顶不保底 = 第 10 章第 6 项确认口径;两个人力岗位账号都已能亲自操作(T65~T67),部门填报人员的新建权限已同步收窄(T58) | -| 13 | 数据调整(源数据 / 计算结果) | ✅ | `kpi_adjustment`:记录调整前值、审批、批准即落地并重算(T26、T27);落地标记见 #25(设计 5.7) | — | -| 14 | 审计查询 | ✅ | `kpi_review_record` 只增不改(T33、T34)+ 平台审计日志 / 记录历史页签(设计 5.9) | — | -| 15 | 组织与权限 | ✅ | 复用平台组织单元 / 用户;岗位 6、权限集 6;共享规则不再是元数据,由方案发布按参与主体动态写入(`src/services/sharing-service.ts`,T39) | 见 §三 #17、#18 | -| 16 | 数据接入与归档 | ✅ | 填报 / 导入 / 系统计算三类来源在指标上声明;归档快照 | 「系统计算」来源的自动取数需对接客户数据源,零外部集成;Excel 自由公式与跨表引用不进系统 = 第 16 项确认的范围边界 | -| 25 | 结果调整改某指标得分并标记「已调整」(设计 5.7、第 10 章第 14 项 = A) | ✅ | `kpi_entry_line.is_adjusted` / `adjust_type_applied`(只读,由 `AdjustmentHook` 以系统上下文写入,源数据与结果调整都标);列入填报明细列表视图与填报单内嵌网格;结果计算明细 JSON 每条指标带 `is_adjusted`;T62、T63 | — 网格列为**显式声明**(显式声明会整体替换平台派生的列集):19 列 = 子对象除主从字段外的全部字段,顺序沿用字段定义顺序、两个新列排最后,计算说明 / 最近调整 / 指标三列用 `defaultHidden` 折叠而非删除,派生列一列未少(评审 🟡-1,见 `docs/05` 问题记录 #4) | -| 26 | 同一周期只有一个生效版本(设计 5.2、第 10 章第 10 项) | ✅ | `PlanPublishHook` 发布前校验同 `period_type + year + period_no` 是否已有「已发布」方案,已关闭 / 已归档不阻断;拒绝文案点名生效版本;候选只按状态取、租户判定在纯函数里做(组织为空的种子 / 导入方案同样算生效版本,T71 + 消融对照);T61、T71 + 单元测试 4 条 | — | - -## 三、角色与安全(需求 §2 / 设计第 9 章) - -| # | 需求 | 态 | 实现落点 / 证据 | 偏差与出口 | -|---|---|---|---|---| -| 17 | 六类角色职责 | ✅ | 岗位 `kpi_admin / kpi_hr_reviewer / kpi_hr_head / kpi_dept_reporter / kpi_branch_checker / kpi_exec_leader`,流程节点按岗位把关(hook 经 `sys_user_position` 查询);加减分登记 / 审批的岗位分离已落地(T59、T66);**人力岗位的记录级写范围**由方案发布写入的岗位共享规则(收件方类型 = `position`)授予 —— 人力审核登记加减分(T65)、人力负责人审批(T67)、人力审核在本节点驳回与审核通过(T69)均由真实岗位账号跑通,方案关闭后同样降为只读(T70) | — 第一轮曾因人力岗位无记录级写权限记为偏差,已按调度员 2026-09-02 的裁定(选项 A)修复并重测,详见 `docs/05-自测记录.md` 问题记录 #1 | -| 18 | 数据范围:全部 / 分管 / 本部门 / 仅本人 | ✅ | 全部 = org、仅本人 = own 原生;本部门 / 本分公司 / 分管范围 = private 基线 + 方案发布时按参与主体、**人力岗位**、分管领导、到人分工写入的**动态共享规则**(`src/services/sharing-service.ts`),组织变化零元数据改动;到人结果行级安全只挡他人的到人维度(T41、T44、T46、T51)。不再依赖企业版 `hierarchy-security`(设计第 17 项) | 四点实现说明:① 规则**按方案**建(条件带方案 id、规则名按方案加前缀),收件方三类:组织单元(`unit_and_subordinates`)、岗位(`position`,人力审核 / 人力负责人对本方案填报单与数据调整可编辑)、个人(`user`),发布时对账停用本方案下已不该存在的规则 —— 换掉分管领导、撤主体、撤到人分工都当场失效,历史方案的授权原样保留(T54~T56);② 方案**已关闭 / 已归档**后,填报单 / 核对任务 / 调整由 `edit` 降为 `read`(留读、去写),结果本就只读(T57);③ 本应用的业务记录全部由 hook 以系统上下文创建,而平台对 `isSystem` 写入跳过记录共享物化(官方补偿手段是重新求值规则),因此每个业务触发点都重新声明一次相关规则;④ 演示种子的组织单元 `organization_id` 为空,`src/data/align-demo-units.ts` 在启动时把它们补成与 Setup 新建单元一致 —— 这是 objectstack-ai/objectstack#14547 的**临时夹具**修补,平台修复后删除(T52);⑤ **历史行回填**:升级前建的核对任务 / 数据调整 `plan` 为空,落不进按方案的规则条件,`src/data/backfill-plan-links.ts` 在启动时幂等补齐(只写 `plan`、只补空值) | -| 19 | 越权访问拒绝、经办人以登录账号为准 | ✅ | 权限集对象级 + 字段级;经办人 / 审批人字段只读由 hook 盖章;越权反例 T42、T58、T59、T64、T66、T68 | — | -| 20 | 账号安全(密码策略、停用即失效、失败限制、白名单、敏感操作留痕) | ✅ | 平台账号体系(`sys_user` 失败计数 / 锁定 / 封禁 / 强制改密);审批、驳回、归档留痕 | 访问来源白名单归部署层(反向代理 / 网关)配置 | -| 27 | 分公司核对只能确认 / 提出争议,不能改值(第 10 章第 13 项 = A) | ✅ | 核对人员权限集对填报明细只有读 / 导出,改值在权限层即被拒(T64);确认与争议走 `kpi_check_task`(T45、T17);改值由填报部门经数据调整流程完成并留痕 | — | - -## 四、非功能(需求 §4) - -| # | 需求 | 态 | 实现落点 / 证据 | 偏差与出口 | -|---|---|---|---|---| -| 21 | 得分口径唯一、可重复计算、历史不漂移 | ✅ | 计分只在 `scoring.ts`、汇总只在 `aggregate.ts`(口径依据设计第 8 章表 6);明细冻结指标口径副本;快照不可变 | — | -| 22 | 遵循 Excel 习惯,即时出分,校验提示,逐层钻取 | ✅ | 内联网格、即时算分、三段式提示、报表钻取 | 自由公式 / 跨 sheet 引用不进系统 —— 第 16 项已确认为范围边界,不再计为偏差 | -| 23 | 性能:大数据量表格可正常浏览编辑 | ⚠️ | 填报单按主体拆分(每表数十行),列表分页 | **出口:升级** —— 实施期用真实数据量验证,见设计表 10「试运行」阶段(一个考核周期真实跑一遍,值守与缺陷修复) | -| 24 | 可扩展:新增计分口径 / 考核关系 / 视图通过配置实现 | ✅ | 计分方式数据化(阶梯 / 区间 / 公式);考核关系与流程节点是方案数据;视图为元数据 | 新计分「方式」需加代码分支(口径参数不需要);清单四种 = 第 12 项确认 | - -## 五、统计 - -- 共 27 条:✅ 完整实现 **26** 条;⚠️ 有偏差 **1** 条(#23 未压测);❌ 未实现 **0** 条。 -- ⚠️ 出口: - - **#23 大数据量性能** → 升级:实施期真实数据量验证,见设计表 10「试运行」阶段。 -- 已闭环:#17 第一轮的偏差(人力岗位无记录级写权限)按调度员 2026-09-02 裁定的选项 A 修复,并以真实岗位账号重测通过(T65~T70),故本轮改 ✅。 -- 说明:#18 已完整实现,但其中的演示夹具对齐是临时的,随 objectstack-ai/objectstack#14547 的平台修复删除。 diff --git "a/docs/05-\350\207\252\346\265\213\350\256\260\345\275\225.md" "b/docs/05-\350\207\252\346\265\213\350\256\260\345\275\225.md" deleted file mode 100644 index 543f01f..0000000 --- "a/docs/05-\350\207\252\346\265\213\350\256\260\345\275\225.md" +++ /dev/null @@ -1,463 +0,0 @@ -# KPI 考核管理系统 自测记录(开发侧) - -> 按 os-project-dev-test「最低限度档」执行:同会话自测,计划先落盘、逐条对照回填。 -> **降级声明**:本轮为**接口级(REST)真机验证** —— 驱动脚本以真实账号登录运行中的 dev 实例逐条调用 -> 数据接口,不是浏览器 UI 走查,也没有截图。按 os-project-std-evidence,接口级证据只能作为写操作 -> 落库的第二观察点,不替代 UI 功能证据;UI 走查与截图留待有浏览器环境的验收轮次(pm-test)。 -> 本文件是挂在工作项 #10 上的测试报告的仓库内落盘版,两者同一份内容。 -> 本记录含四轮:第一轮落地已确认设计口径(T58~T64);第二轮按调度员 2026-09-02 的裁定补上人力岗位的 -> 记录级写范围(T65~T70),并以**真实岗位账号**重测第一轮遗留的阻塞问题;第三轮修 PR 评审提出的内嵌 -> 网格列集回归(问题记录 #4);第四轮修第三轮自己引入的租户过滤漏判(问题记录 #5,新增 T71)。 -> 全文数据取自**第四轮**真跑。 - -## 第 1 页:结论 - -**结论:✅ 通过** - -共 73 条用例:通过 **73**。静态门禁 3/3 绿(validate / typecheck / vitest 68 条)。 -工作项 #10 的七条验收标准全部达成 —— 其中验收标准 1、2(以人力审核账号登记、以人力负责人账号批准) -在第一轮受数据范围所限无法演示,第二轮补上岗位共享规则后由真实岗位账号取证通过(T65、T66、T67)。 - -**关键观察**(接口级,替代截图) - -- T65 登记成功(人力审核账号):`share=true register=201`,加减分落库状态为「待审批」。 -- T66 拒绝原文:`{"error":"审批加减分失败:只有人力负责人岗位可以审批加减分。请转交人力负责人处理。","code":"VALIDATION_ERROR","declaredCode":"KPI_BONUS_APPROVE_POSITION","object":"kpi_bonus"}` -- T67 人力负责人批准后填报单最终得分 `before=103 after=105`,审批人 = 该账号本人。 -- T69 人力审核账号推进流程:`reject=200→draft approve=200→approved`。 -- T70 方案关闭后:岗位规则 4 条全部降为 `read`,该账号读填报单 200、写加减分 403。 -- T61 拒绝原文:`{"error":"发布失败:该考核周期已有生效版本「2026 年 11 月 月度考核」。请先关闭该版本,或改用复制出的新版本替换。","code":"VALIDATION_ERROR","declaredCode":"KPI_PLAN_PERIOD_CONFLICT","object":"kpi_plan"}` -- T63 计算明细原文:`{"plan_indicator":"...","indicator_name":"客户满意度","score_rate":90,"score":12.5,"is_adjusted":true,"adjust_type_applied":"result"}` - -## 2. 测试环境与账号 - -| 项 | 值 | -|---|---| -| 被测版本 | commit `69a33c6f1bafc57254862d89b8b355235edc78ce`(分支 `issue-10-align-confirmed-design`,第四轮代码提交;第三轮 `6ba334f`、第二轮 `98cf887`、第一轮 `79437b2`)。核实方式(开测前三级核对):① `/proc/net/tcp` 中处于 LISTEN 的端口含 3110,且无 3100 / 3111;② 占用 3110 的进程 cwd = `/home/user/kpi-issue-10`;③ 该 worktree `git rev-parse HEAD` = 69a33c6,`git status` 干净,`src/hooks/plan.hook.ts` 的 blob 与 `HEAD:` 版本哈希一致(`dae1c9f8…`)。另核对构建产物:填报单内嵌网格 19 列(3 列默认隐藏)与子对象字段一一对应;⚠️ 打包器会剥离注释,**不能**用注释串做新鲜度探针,本轮改动的新鲜度由 T71 的行为(消融对照)证明 | -| 平台 | @objectstack/* 17.2.0(npm 发布版),SqlDriver(better-sqlite3),单租户 | -| 环境 / 启动 | `OS_PORT=3110 OS_DATABASE_URL=file:./.objectstack/issue-10.db pnpm dev`(空库启动,自动加载 46 行演示种子),地址 `http://localhost:3110`。**改元数据 / 服务后必须先 `rm -rf dist` 再重启**,否则 `objectstack dev` 会沿用上次的构建产物(问题记录 #3) | -| 端 | REST 接口(PC 端服务侧)。UI 未走查 —— 见首段降级声明 | -| 测试账号 | 管理员 `admin@objectos.ai / admin123`(组织 owner,等同考核系统管理员岗位)。无权对照账号由脚本当场创建:以管理员经 REST `POST /auth/sign-up/email` 建号 → `sys_business_unit_member` 入单元 → `sys_user_position` 分配岗位,口令统一 `Passw0rd!23`;本轮建了部门填报人员(市场部)、分公司核对人员(华东)、分管领导(前任 / 新任)、**人力审核、人力负责人**六个账号,邮箱带时间戳后缀,每次运行重建 | -| 前置数据 | 仅演示种子(组织单元 8、指标库、2026 年 8 月月度考核方案及其下达);其余数据(9 / 10 / 11 / 12 月方案、账号、加减分、调整)由脚本按序创建,凭本表可从空库重建同等环境 | -| 驱动脚本 | `scripts/e2e-flow.mjs`(目标地址取环境变量 `KPI_BASE_URL`,本轮 `http://localhost:3110`);每条用例的预期只来自需求 §3、蓝图与《设计方案》V1.0,不从实现反推 | -| 测试日期 | 2026-09-02 | -| 未覆盖 | 浏览器 UI 走查与截图;大数据量性能 | - -## 3. 测试计划与结果总表 - -四类齐全:主流程 38、边界 3、异常 15、权限 17(合计 73)。 - -| 编号 | 标题 | 类型 | 端 | 角色 | 结果 | 证据 | -|---|---|---|---|---|---|---| -| T0 | 管理员登录 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T0 | -| T1 | 种子方案已加载 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T1 | -| T2 | 指标下达 18 条且名称由 hook 填充 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T2 | -| T3 | 权重合计≠100 时发布被拒绝并列出问题 | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T3 | -| T4 | 完整性通过后发布成功 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T4 | -| T5 | 发布生成 5 张填报单 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T5 | -| T6 | 生成 18 行明细并冻结目标/权重/计分方式副本 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T6 | -| T7 | 发布后修改指标下达被冻结拒绝 | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T7 | -| T8 | 线性:实际 1320/目标 1200 → 完成率 110%,得分 55(权重 50) | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T8 | -| T9 | 区间插值:144/180=80% → 得分率 50%,得分 15(权重 30) | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T9 | -| T10 | 有指标未填实际值时提交被拒绝 | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T10 | -| T11 | 阶梯:87.3/90=97% → 命中「达标」得分率 90%,得分 18(权重 20) | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T11 | -| T12 | 填报单汇总:指标得分合计 88、最终得分 88 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T12 | -| T13 | 直接改状态被拒绝 | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T13 | -| T14 | 提交后进入分公司核对中 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T14 | -| T15 | 按 3 家分公司生成并行核对任务 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T15 | -| T16 | 提交后修改明细被冻结拒绝 | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T16 | -| T17 | 提出争议但无意见被拒绝 | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T17 | -| T18 | 有争议未解决时不能推进 | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T18 | -| T19 | 全部分公司确认后自动推进到人力审核中 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T19 | -| T20 | 驳回原因为空被拒绝 | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T20 | -| T21 | 驳回退回上一节点(分公司核对),核对任务重置,原因留痕 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T21 | -| T22 | 人力审核通过 → 领导审批中 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T22 | -| T23 | 领导审批通过 → 已通过,盖章 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T23 | -| T24 | 通过后生成考核结果(部门维度) | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T24 | -| T25 | 加减分批准后计入最终得分 91 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T25 | -| T26 | 调整申请记录调整前值 144 与主体 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T26 | -| T27 | 源数据调整落地并重算:利润 180 → 完成率 100%,得分 30;结果重新汇总 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T27 | -| T28 | 归档生成不可变快照(含校验和、总分) | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T28 | -| T29 | 修改快照被拒绝 | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T29 | -| T30 | 删除快照被拒绝 | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T30 | -| T31 | 归档后明细连备注也不可改(已冻结) | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T31 | -| T32 | 归档后不能再加减分 | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T32 | -| T33 | 审核记录完整留痕(生成/提交/争议/确认/驳回/通过/调整/归档) | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T33 | -| T34 | 审核记录不可篡改 | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T34 | -| T35 | 复制上一版本:流程节点 4、指标下达 18 已复制 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T35 | -| T36 | 两节点流程(填报→人力审核):提交直达人力审核,通过即已通过 | 边界 | REST | 管理员 | ✅ 通过 | §6 运行输出 T36 | -| T37 | 逆向指标:目标 12 实际 10 → 完成率 120%,封顶 110% → 得分 55(权重 50) | 边界 | REST | 管理员 | ✅ 通过 | §6 运行输出 T37 | -| T38 | 发布第三版方案(市场部已配分管领导)生成 5 张填报单 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T38 | -| T39 | 发布按方案配置写入动态共享规则,条数与方案配置精确相符,元数据零改动 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T39 | -| T39b | 规则条件按方案隔离:每条规则的条件里都带本方案 id | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T39b | -| T40 | 规则求值把单元共享展开成逐人记录共享行(市场部填报单 → 用户 A) | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T40 | -| T41 | 部门填报人员只看到本部门(市场部)填报单,含本次新发布的那张 | 权限 | REST | 部门填报人员 | ✅ 通过 | §6 运行输出 T41 | -| T42 | 打开本部门以外(华东分公司)的填报单被拒绝 | 权限 | REST | 部门填报人员 | ✅ 通过 | §6 运行输出 T42 | -| T43 | 部门填报人员能填写实际值并提交本部门填报单 | 主流程 | REST | 部门填报人员 | ✅ 通过 | §6 运行输出 T43 | -| T44 | 分公司核对人员只看到本分公司(华东)的核对任务 | 权限 | REST | 分公司核对人员 | ✅ 通过 | §6 运行输出 T44 | -| T45 | 分公司核对人员能确认本分公司的核对任务 | 主流程 | REST | 分公司核对人员 | ✅ 通过 | §6 运行输出 T45 | -| T46 | 分管领导只看到分管主体(市场部)的填报单,看不到运营部 | 权限 | REST | 分管领导 | ✅ 通过 | §6 运行输出 T46 | -| T47 | 分管领导能在「领导审批中」执行审核通过 | 主流程 | REST | 分管领导 | ✅ 通过 | §6 运行输出 T47 | -| T48 | 已通过填报单批准加减分后,部门结果立即等于新的最终得分 | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T48 | -| T49 | 否决加减分后立即重算,被否决的分不计入部门结果 | 边界 | REST | 管理员 | ✅ 通过 | §6 运行输出 T49 | -| T50 | 不存在方案发布之外来源的 KPI 共享规则(静态规则文件已删除,也没有旧的按单元规则) | 权限 | REST | 管理员 | ✅ 通过 | §6 运行输出 T50 | -| T51 | 填报人员看到本部门结果与本人到人结果,看不到他人的到人结果 | 权限 | REST | 部门填报人员 | ✅ 通过 | §6 运行输出 T51 | -| T53 | 分管领导看到所分管主体(市场部)的部门结果,看不到非分管主体的结果 | 权限 | REST | 分管领导 | ✅ 通过 | §6 运行输出 T53 | -| T54 | 换人后新方案只为新任分管领导建规则,老领导没有新方案的规则 | 权限 | REST | 管理员 | ✅ 通过 | §6 运行输出 T54 | -| T55 | 换掉分管领导:老领导看不到新方案的填报单,历史方案的仍看得到 | 权限 | REST | 分管领导(前任) | ✅ 通过 | §6 运行输出 T55 | -| T56 | 新任分管领导看到新方案的市场部填报单 | 权限 | REST | 分管领导(新任) | ✅ 通过 | §6 运行输出 T56 | -| T57 | 方案关闭后填报单 / 核对任务 / 调整的共享降为只读(留读、去写) | 权限 | REST | 管理员 | ✅ 通过 | §6 运行输出 T57 | -| T52 | 演示种子组织单元在启动时被补上组织归属(与 Setup 里新建的单元一致) | 主流程 | REST | 系统(启动) | ✅ 通过 | §6 运行输出 T52 | -| T58 | 部门填报人员登记加减分被拒绝(权限集不再声明新建权限) **(本工作项新增)** | 权限 | REST | 部门填报人员 | ✅ 通过 | §6 运行输出 T58 | -| T59 | 非人力负责人岗位批准加减分被拒绝(第 10 章第 6 项:人力负责人审批) **(本工作项新增)** | 权限 | REST | 部门填报人员 | ✅ 通过 | §6 运行输出 T59 | -| T60 | 人力负责人岗位批准加减分成功,最终得分随之重算 **(本工作项新增)** | 主流程 | REST | 管理员(等同人力负责人岗位) | ✅ 通过 | §6 运行输出 T60 | -| T61 | 同周期已有生效版本时发布被拒并点名该版本;关闭后可发布(第 10 章第 10 项) **(本工作项新增)** | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T61 | -| T62 | 结果调整落地后该行标记「已调整」= 是、调整类型 = 计算结果,未调整行保持否 **(本工作项新增)** | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T62 | -| T63 | 考核结果的计算明细 JSON 逐指标带 is_adjusted,被调整的那条为 true **(本工作项新增)** | 主流程 | REST | 管理员 | ✅ 通过 | §6 运行输出 T63 | -| T64 | 分公司核对人员直接修改填报明细实际值被拒绝(第 10 章第 13 项:只能确认 / 提出争议) **(本工作项新增)** | 权限 | REST | 分公司核对人员 | ✅ 通过 | §6 运行输出 T64 | -| T65 | 人力审核账号登记加减分成功(岗位规则已授予记录级编辑权) **(本工作项第二轮新增)** | 主流程 | REST | 人力审核 | ✅ 通过 | §6 运行输出 T65 | -| T66 | 人力审核账号批准自己登记的加减分被拒绝(第 10 章第 6 项:人力负责人审批) **(本工作项第二轮新增)** | 权限 | REST | 人力审核 | ✅ 通过 | §6 运行输出 T66 | -| T67 | 人力负责人账号批准加减分成功,填报单最终得分随之重算 **(本工作项第二轮新增)** | 主流程 | REST | 人力负责人 | ✅ 通过 | §6 运行输出 T67 | -| T68 | 人力负责人账号登记加减分被拒绝(权限集不声明新建权限) **(本工作项第二轮新增)** | 权限 | REST | 人力负责人 | ✅ 通过 | §6 运行输出 T68 | -| T69 | 人力审核账号在人力审核节点驳回与审核通过均成功(真实岗位账号推进流程) **(本工作项第二轮新增)** | 主流程 | REST | 人力审核 | ✅ 通过 | §6 运行输出 T69 | -| T70 | 方案关闭后人力岗位对该方案填报单降为只读(留读、去写) **(本工作项第二轮新增)** | 权限 | REST | 人力审核 | ✅ 通过 | §6 运行输出 T70 | -| T71 | 组织为空的已发布方案仍阻断同周期发布(租户判定不靠查询过滤) **(本工作项第三轮新增)** | 异常 | REST | 管理员 | ✅ 通过 | §6 运行输出 T71 | - -## 4. 逐用例明细(本工作项新增 / 改动的用例) - -### T58:部门填报人员登记加减分被拒绝(权限集不再声明新建权限) - -- **前置条件**:2026 年 11 月方案已发布(市场部为参与主体),部门填报人员账号 A 已入市场部并持岗位 `kpi_dept_reporter`;等待该方案的市场部填报单共享到 A 后再操作。 -- **操作步骤**:以账号 A 登录 → `POST /api/v1/data/kpi_bonus`,body:`{ sheet: 11 月市场部填报单, title: '越权登记', bonus_type: 'add', points: 1, reason: '岗位反例' }`。 -- **预期结果**:① 请求被拒绝(4xx);② 记录拒绝发生在哪一层。 -- **实际观察**:① HTTP 403;② 拒绝层 = 权限集(`PERMISSION_DENIED`)—— 第二轮按裁定把 `kpi_dept_reporter_set` 的 `kpi_bonus.allowCreate` 改为 false,声明与执行一致后,拒绝比业务规则更早发生。业务规则本身的文案(校验规则 1)由单元测试 `requiredBonusPosition` 取证。 -- **结果**:✅ 通过 -- **截图**:无(接口级证据,见首段降级声明) - -### T59:非人力负责人岗位批准加减分被拒绝 - -- **前置条件**:同 T58;管理员先在同一张填报单上登记一条待审批加减分(事项「安全生产先进」,加分 2)。 -- **操作步骤**: - 1. 以账号 A(部门填报人员,对该填报单有记录级编辑权)登录 → `PATCH /api/v1/data/kpi_bonus/`,body:`{ status: 'approved' }`。 -- **预期结果**: - 1. 请求被拒绝(4xx); - 2. 提示:「审批加减分失败:只有人力负责人岗位可以审批加减分。请转交人力负责人处理。」 -- **实际观察**: - 1. HTTP 422; - 2. `{"error":"审批加减分失败:只有人力负责人岗位可以审批加减分。请转交人力负责人处理。","code":"VALIDATION_ERROR","declaredCode":"KPI_BONUS_APPROVE_POSITION","object":"kpi_bonus"}`。 -- **结果**:✅ 通过 -- **补充说明**:本用例用部门填报人员做反例,是因为人力审核账号连这一步都够不到(问题记录 #1)—— 它对填报单没有记录级写权限,会先被平台的 403 挡下,看不到业务规则的提示。用 A 做反例反而能证明「唯一被拒的原因是岗位判定」。 -- **截图**:无 - -### T60:人力负责人岗位批准加减分成功,最终得分随之重算 - -- **前置条件**:2026 年 10 月方案的市场部填报单已通过(状态 approved),批准前最终得分 106、加减分合计 3。管理员账号是组织 owner,岗位判定上等同人力负责人(与考核系统管理员同口径)。 -- **操作步骤**: - 1. `POST /api/v1/data/kpi_bonus`:事项「重点工作表彰」,加分 2; - 2. `PATCH /api/v1/data/kpi_bonus/`:`{ status: 'approved' }`; - 3. `GET /api/v1/data/kpi_entry_sheet/<市场部填报单>`。 -- **预期结果**: - 1. 批准成功(2xx),加减分状态为「已批准」且盖上审批人; - 2. 填报单加减分合计 3 → 5; - 3. 填报单最终得分 106 → 108。 -- **实际观察**: - 1. 批准返回 2xx,`status=approved`、`approved_by` 非空; - 2. `bonus_total=5`; - 3. `before=106 after=108`。 -- **结果**:✅ 通过 -- **截图**:无 - -### T61:同周期已有生效版本时发布被拒;关闭后可发布 - -- **前置条件**:2026 年 11 月月度考核已处于「已发布」。 -- **操作步骤**: - 1. 复制该版本新建方案「2026 年 11 月 月度考核(重复周期)」(period_type=month、year=2026、period_no=11); - 2. `PATCH /api/v1/data/kpi_plan/<新方案>`:`{ status: 'published' }`; - 3. 把原 11 月方案改为「已关闭」; - 4. 再次对新方案执行发布。 -- **预期结果**: - 1. 第 2 步被拒绝,提示点名已生效的方案名,并给出下一步(关闭该版本或用复制版替换); - 2. 第 4 步发布成功,新方案状态变为「已发布」。 -- **实际观察**: - 1. HTTP 422,`{"error":"发布失败:该考核周期已有生效版本「2026 年 11 月 月度考核」。请先关闭该版本,或改用复制出的新版本替换。","code":"VALIDATION_ERROR","declaredCode":"KPI_PLAN_PERIOD_CONFLICT","object":"kpi_plan"}`; - 2. 关闭后再发布返回 200,`status=published`。 -- **结果**:✅ 通过 -- **截图**:无 - -### T62:结果调整落地后标记「已调整」 - -- **前置条件**:2026 年 10 月方案的市场部填报单已通过,明细「客户满意度」已有得分。 -- **操作步骤**: - 1. `POST /api/v1/data/kpi_adjustment`:`{ line: <客户满意度明细>, adjust_type: 'result', new_value: 12.5, reason: '复核后按第 14 项调整该指标得分' }`; - 2. `PATCH` 该调整 → `submitted` → `approved`; - 3. `GET` 被调整的明细行与同张单里另一行未被调整的明细。 -- **预期结果**: - 1. 被调整行「已调整」= 是、「调整类型」= 计算结果; - 2. 该行调整后得分与最终得分 = 12.5; - 3. 未被调整的行「已调整」保持否、「调整类型」为空。 -- **实际观察**: - 1. `is_adjusted=true type=result`; - 2. `final=12.5`(`adjusted_score` 同为 12.5); - 3. 未调整行 `is_adjusted=false`、`adjust_type_applied` 为空。 -- **结果**:✅ 通过 -- **截图**:无 - -### T63:考核结果计算明细 JSON 逐指标带 is_adjusted - -- **前置条件**:T62 的调整已落地并触发结果重算。 -- **操作步骤**: - 1. `GET /api/v1/data/kpi_result?plan=<2026 年 10 月方案>&limit=100`,取维度 = 部门、单元 = 市场部的那一行,读 `breakdown`。 -- **预期结果**: - 1. `breakdown.lines` 逐指标列出,条数 = 该填报单的明细行数; - 2. 被调整的那条 `is_adjusted: true`、`adjust_type_applied: 'result'`; - 3. 未被调整的那条 `is_adjusted: false`。 -- **实际观察**: - 1. `lines=3`; - 2. `{"plan_indicator":"1GLVXvVPxDO9xY2G","indicator_name":"客户满意度","score_rate":90,"score":12.5,"is_adjusted":true,"adjust_type_applied":"result"}`; - 3. 未调整行 `is_adjusted:false`。 -- **结果**:✅ 通过 -- **截图**:无 - -### T64:分公司核对人员改填报明细实际值被拒绝 - -- **前置条件**:分公司核对人员账号 B 持岗位 `kpi_branch_checker`、属华东分公司;T45 已证明它能确认本分公司的核对任务。 -- **操作步骤**: - 1. 以账号 B 登录 → `PATCH /api/v1/data/kpi_entry_line/<客户满意度明细>`:`{ actual_value: 1 }`; - 2. 以管理员重新读该明细行。 -- **预期结果**: - 1. 请求被拒绝(403); - 2. 明细实际值未被改动。 -- **实际观察**: - 1. HTTP 403,`{"error":"You do not have permission to perform this action. Contact your administrator if you need access.","code":"PERMISSION_DENIED",...}`; - 2. 明细实际值与操作前一致。 -- **结果**:✅ 通过 -- **补充说明**:拒绝发生在权限层(核对人员的权限集对填报明细只有读 / 导出),不是业务 hook —— 「只能确认 / 提出争议、不能改值」由权限模型直接兑现。 -- **截图**:无 - -### T65:人力审核账号登记加减分成功 - -- **前置条件**:第二轮新建 2026 年 12 月方案(两节点流程:部门填报 → 人力审核),发布后由管理员填完市场部填报单并提交,填报单处于「人力审核中」;人力审核账号持岗位 `kpi_hr_reviewer`,不属于任何考核主体单元、也不是分管领导。 -- **操作步骤**: - 1. 轮询 `sys_record_share`,等方案发布写入的岗位共享规则求值出该账号对该填报单的 `edit` 行; - 2. 以人力审核账号登录 → `POST /api/v1/data/kpi_bonus`:`{ sheet: 12 月市场部填报单, title: '专项攻关表彰', bonus_type: 'add', points: 2, reason: '按第 6 项由人力审核登记' }`; - 3. 重读该加减分。 -- **预期结果**: - 1. 共享行出现(证明岗位收件方被展开成了该账号的记录共享); - 2. 登记成功(2xx); - 3. 加减分状态为「待审批」。 -- **实际观察**: - 1. `share=true`; - 2. HTTP 201; - 3. `status=draft`。 -- **结果**:✅ 通过 -- **截图**:无 - -### T66:人力审核账号批准自己登记的加减分被拒绝 - -- **前置条件**:T65 登记的那条加减分处于「待审批」,操作账号仍是登记它的人力审核账号(此时它对填报单已有记录级编辑权,所以拒绝只可能来自岗位判定)。 -- **操作步骤**:`PATCH /api/v1/data/kpi_bonus/`:`{ status: 'approved' }`。 -- **预期结果**:① 被拒绝(4xx);② 提示「审批加减分失败:只有人力负责人岗位可以审批加减分。请转交人力负责人处理。」 -- **实际观察**:① HTTP 422;② 文案一致,`declaredCode` = `KPI_BONUS_APPROVE_POSITION`。 -- **结果**:✅ 通过 —— 这条同时兑现了「人力审核不能审批自己登记的加减分」。 -- **截图**:无 - -### T67:人力负责人账号批准加减分成功,填报单最终得分随之重算 - -- **前置条件**:T65 的加减分待审批;批准前填报单最终得分 103、加减分合计 0。 -- **操作步骤**:以人力负责人账号(持 `kpi_hr_head`)登录 → `PATCH /api/v1/data/kpi_bonus/`:`{ status: 'approved' }` → 以管理员重读填报单与该加减分。 -- **预期结果**:① 批准成功;② 审批人 = 该人力负责人账号;③ 加减分合计 +2;④ 最终得分 103 → 105。 -- **实际观察**:① 2xx;② `approved_by` = 该账号 id;③ `bonus_total` = 2;④ `before=103 after=105`。 -- **结果**:✅ 通过 -- **截图**:无 - -### T68:人力负责人账号登记加减分被拒绝 - -- **操作步骤**:以人力负责人账号登录 → `POST /api/v1/data/kpi_bonus`(同一张填报单)。 -- **预期结果**:① 被拒绝;② 记录拒绝发生在哪一层。 -- **实际观察**:① HTTP 403;② 拒绝层 = 权限集(`kpi_hr_head_set` 不声明 `kpi_bonus` 的新建权限)—— 与「登记 = 人力审核」的确认口径一致,拒绝早于业务规则。 -- **结果**:✅ 通过 -- **截图**:无 - -### T69:人力审核账号在人力审核节点驳回与审核通过 - -- **前置条件**:12 月方案的市场部填报单处于「人力审核中」,该方案人力审核节点的审核岗位 = `kpi_hr_reviewer`。 -- **操作步骤**: - 1. 以人力审核账号登录 → `PATCH /api/v1/data/kpi_entry_sheet/`:`{ pending_action: 'reject', action_reason: '数据口径请复核' }`; - 2. 以管理员重新提交; - 3. 以人力审核账号 → `{ pending_action: 'approve' }`。 -- **预期结果**:① 驳回成功,填报单退回「填报中」并留下驳回原因;② 审核通过成功,两节点流程下直接变为「已通过」。 -- **实际观察**:① `reject=200 → status=draft`,`last_reject_reason=数据口径请复核`;② `approve=200 → status=approved`。 -- **结果**:✅ 通过 —— 人力审核节点第一次由真实岗位账号(而非管理员)推进。 -- **截图**:无 - -### T70:方案关闭后人力岗位对该方案填报单降为只读 - -- **前置条件**:12 月方案的填报单已通过;把该方案改为「已关闭」。 -- **操作步骤**: - 1. 管理员 `PATCH /api/v1/data/kpi_plan/`:`{ status: 'closed' }`,轮询该方案的岗位规则降为 `read`; - 2. 以人力审核账号 `GET` 该填报单; - 3. 以人力审核账号 `POST /api/v1/data/kpi_bonus`(同一张填报单)。 -- **预期结果**:① 该方案的 4 条岗位规则全部 `read`;② 读得到(留读);③ 写被拒(去写)。 -- **实际观察**:① `岗位规则 4 条全部 read=true`;② HTTP 200;③ HTTP 403。 -- **结果**:✅ 通过 -- **截图**:无 - -### T71:组织为空的已发布方案仍阻断同周期发布 - -- **前置条件**:演示种子写入的「2026 年 8 月 月度考核」在 T4 已发布,其 `organization_id` 为空(种子 / 导入写进来的行都是这样)。 -- **操作步骤**: - 1. 读该种子方案,确认状态「已发布」且组织为空; - 2. 复制它新建同周期方案(月度 / 2026 / 第 8 期); - 3. 对新方案执行发布。 -- **预期结果**: - 1. 发布被拒,提示点名种子方案; - 2. 新方案仍停在「草稿」。 -- **实际观察**: - 1. `seed_org=null seed_status=published`,发布返回 HTTP 422,`发布失败:该考核周期已有生效版本「2026 年 8 月 月度考核」。…`; - 2. 新方案 `status=draft`。 -- **结果**:✅ 通过 -- **反向验证(消融)**:把第三轮那条 `where.organization_id` 过滤加回去、清 `dist` 重建后重跑,本用例转红(`dup=200`,重复发布成功),而 T61(两条方案都有组织)保持绿 —— 与评审判断的「数据相关漏判」完全吻合。随后按 `git checkout HEAD --` 还原、再次清 `dist` 重建重跑,73/73 恢复全绿;还原后工作树与 `HEAD` 的 blob 哈希一致。 -- **截图**:无 - -## 5. 问题记录 - -| # | 用例 | 现象 | 复现步骤 | 原因分析 | 处理建议 | 截图 | 处置 | -|---|---|---|---|---|---|---|---| -| 1 | 第一轮:验收标准 1 / 2(当时无对应用例);第二轮:T65~T70 | 人力审核账号登记不了加减分、人力负责人账号批准不了加减分,连改填报单备注都被拒;两个账号都看得到填报单列表 | ① 管理员建号并 `POST sys_user_position {position:'kpi_hr_reviewer'}`;② 以该账号登录;③ `GET /data/kpi_entry_sheet` 能读到 25 张;④ `POST /data/kpi_bonus {sheet: 任一已发布方案的填报单, ...}` → 403;⑤ `PATCH /data/kpi_entry_sheet/<同一张> {remark:'x'}` → 403。**必现**(第一轮 3 次 3 次出现) | 填报单 OWD 为 private,记录级写权限只来自方案发布写入的动态共享规则(收件人 = 参与主体单元、核对分公司、分管领导、被下达员工),人力岗位不在其列;权限集里的 org 范围只放开了读。平台原文:`[Security] Access denied: insert on 'kpi_bonus' requires edit access to its master record (master 'kpi_entry_sheet' not editable by this user (row-level security))` | 【要改代码】按裁定选项 A:方案发布时为 `kpi_hr_reviewer` / `kpi_hr_head` 建按方案的共享规则(收件方类型 = `position`),授予本方案全部填报单与数据调整 `edit`;加减分作为填报单的主从子记录随之可写 | 无(接口级) | **已修复并重测**:修复提交 `98cf887`,重测所在提交 `98cf887`,证据 T65~T70(登记、审批、流程推进、关闭后只读)全部通过 | -| 2 | T58 / T68 | 部门填报人员的权限集仍声明了加减分的新建权限,而业务规则已只允许人力审核登记 —— 声明与执行不一致(按钮点了才报错) | 见 T58 | `kpi_dept_reporter_set` 的 `kpi_bonus.allowCreate` 为 true | 【要改代码】改为 `allowCreate: false`;`kpi_hr_head_set` 保持不可新建 | 无 | **已修复并重测**:修复提交 `98cf887`,重测所在提交 `98cf887`,证据 T58(拒绝层 = 权限集)、T68 | -| 3 | 第二轮首次运行 T39 / T65~T70 | 改完共享规则代码重启 dev 后,岗位共享规则一条都没写出来,T39 规则数仍是旧值 25、T65~T70 全红,而服务端日志里没有任何报错 | ① 改 `src/services/sharing-service.ts`;② 按 PID 停 dev、删数据库文件后重启;③ 跑 e2e → 新规则不存在;④ `grep _pos_ dist/*.mjs` = 0 命中,`dist/` 构建产物时间戳仍是上一轮的 | `objectstack dev` 沿用了 `dist/` 里上一轮的构建产物,新源码没有进入运行实例 —— 症状是「代码看着改了、运行的还是旧的」,且不报错 | 【环境问题】改元数据 / hook / 服务后,重启前先 `rm -rf dist`(项目 CLAUDE.md D3 的清理项);重启后用 `grep` 确认新产物含新代码再开测。**绕行**:同上,清理即可 | 无 | 已解决:清 `dist/` 后重启,同一份 e2e 脚本 72/72 通过 | -| 4 | 填报单内嵌网格(评审 🟡-1,无对应用例) | 填报单里的指标网格少了六列:来源下达(必填)、指标、指标方向、计分方式、计算说明、最近调整 —— 本工作项只要求加一列「已调整」,却把原有列挤掉了 | 打开任一填报单的「指标填报」内嵌网格,对照 `kpi_entry_line` 的字段清单逐列核对 | 给主从字段显式声明 `inlineColumns` 会**整体替换**平台从子对象派生的列集,新列表只列了 13 列 | 【要改代码】补回全部 19 列(= 子对象除主从字段外的全部字段),顺序沿用字段定义顺序,新列排最后;冗长的计算说明 / 最近调整 / 指标用平台原生 `defaultHidden` 折叠而不是删除。**绕行**:无需绕行(列表视图仍完整) | 无 | **已修复并重测**:修复与重测所在提交 `6ba334f`,构建产物核对为 19 列(3 列默认隐藏),`pnpm validate` 绿、e2e 72/72 | -| 5 | T71(本轮新增) | 第三轮为租户隔离加的 `where.organization_id` 等值条件会把**组织为空**的已发布方案挡在判定之外 —— 种子 / 导入写进来的方案行就是空组织,于是「同周期只有一个生效版本」对这类数据静默失效(第三轮之前没有这个问题) | ① 空库起 dev,种子方案(2026 年 8 月)在 T4 发布,其 `organization_id` 为空;② 复制它新建同周期方案并发布;③ 期望被拒,实际发布成功(HTTP 200)。**必现** | 等值条件不匹配 NULL,候选在进入 `findPublishedConflict` 之前就被查询滤掉,`sameTenant()` 里「缺组织时退回只按周期判定」的兜底永远轮不到它 | 【要改代码】候选只按 `status: 'published'` 取,租户判定全部交给纯函数 `sameTenant`;补纯函数用例(空组织候选)与 e2e T71。**绕行**:无需绕行(仅第三轮提交存在该缺陷,未发布) | 无 | **已修复并重测**:修复与重测所在提交 `69a33c6`,证据 T71 + 消融对照(加回过滤即转红,还原即恢复绿) | - -## 6. 运行输出(当次真跑原样) - -### 6.1 `pnpm verify`(validate + typecheck + vitest) - -```text - -> @objectstack-ai/kpi@0.1.0 verify /home/user/kpi-issue-10 -> pnpm validate && pnpm typecheck && pnpm test - - -> @objectstack-ai/kpi@0.1.0 validate /home/user/kpi-issue-10 -> objectstack validate - - -◆ Validate -──────────────────────────────────────── - → Loading configuration... - Config: /home/user/kpi-issue-10/objectstack.config.ts - Load time: 274ms - → Validating against ObjectStack Protocol... - → Running author-time rules (41)... - → Checking capability providers (#3366)... - → Checking package docs (ADR-0046)... - - ✓ Validation passed (655ms) - - KPI 考核管理 v0.1.0 - KPI assessment management: indicator library, plan versions, entry with instant scoring, parallel branch checks, configurable multi-step review, adjustments, four-dimension aggregation and immutable archives. - - Data: 17 Objects 184 Fields - UI: 1 Apps 17 Views 1 Pages 2 Dashboards 3 Reports 16 Actions - Logic: 0 Flows - Security: 6 Positions 6 Permissions - - -> @objectstack-ai/kpi@0.1.0 typecheck /home/user/kpi-issue-10 -> tsc --noEmit - - -> @objectstack-ai/kpi@0.1.0 test /home/user/kpi-issue-10 -> vitest run - - - RUN v4.1.11 /home/user/kpi-issue-10 - - - Test Files 6 passed (6) - Tests 68 passed (68) - Start at 18:33:30 - Duration 978ms (transform 806ms, setup 0ms, import 1.53s, tests 70ms, environment 1ms) - -``` - -单元测试 68 条分布:计分引擎 14、状态机 / 流程配置 11、数据范围规则 26(第二轮 +3:人力岗位规则生成、锚定与关闭后降级)、汇总 4、CEL 求值 3、已确认设计口径 10(第三轮 +1:同周期唯一性的租户隔离)。 - -### 6.2 `pnpm e2e`(`KPI_BASE_URL=http://localhost:3110 node scripts/e2e-flow.mjs`) - -```text -PASS T0 管理员登录 — status 200 -PASS T1 种子方案已加载 — 2026 年 8 月 月度考核 -PASS T2 指标下达 18 条且名称由 hook 填充 — 18 rows, sample: 成本费用率 · 运营部 -PASS T3 权重合计≠100 时发布被拒绝并列出问题 — {"error":"发布失败,发布前完整性检查未通过:\n1. 指标下达:主体「市场部」的权重合计为 60%,必须等于 100%","code":"VALIDATION_ERROR","declaredCode":"KPI_PLAN_INCOMPLETE","object":"kpi_plan"} -PASS T4 完整性通过后发布成功 — {"object":"kpi_plan","id":"I-z-8wRXLurbl6a6","record":{"id":"I-z-8wRXLurbl6a6","created_at":"2026-09-02T18:37:14.730Z","updated_at":"2026-09-02T18:37:40.432Z","organization_id":null,"created_by":null,"updated_by":"qXGjlT -PASS T5 发布生成 5 张填报单 — 2026 年 8 月 月度考核 · 运营部[draft], 2026 年 8 月 月度考核 · 华南分公司[draft], 2026 年 8 月 月度考核 · 华东分公司[draft], 2026 年 8 月 月度考核 · 市场部[draft], 2026 年 8 月 月度考核 · 华北分公司[draft] -PASS T6 生成 18 行明细并冻结目标/权重/计分方式副本 — 18 rows, sample: 客户满意度 w=20 target=90 -PASS T7 发布后修改指标下达被冻结拒绝 — {"error":"修改失败:方案已发布,配置已冻结。如需调整指标、权重、目标或流程,请新建方案版本。","code":"VALIDATION_ERROR","declaredCode":"KPI_PLAN_FROZEN","object":"kpi_plan_indicator"} -PASS T8 线性:实际 1320/目标 1200 → 完成率 110%,得分 55(权重 50) — rate=110 score=55 trace=实际 1320 / 目标 1200(越高越好)→ 完成率 110%;权重 50%;线性:得分率 = 完成率限定在 [0%, 120%] = 110%;得分 = 50% × 110% = 55 -PASS T9 区间插值:144/180=80% → 得分率 50%,得分 15(权重 30) — rate=80 score_rate=50 score=15 -PASS T10 有指标未填实际值时提交被拒绝 — {"error":"提交失败:还有 1 个指标未填写实际值。请补齐后再提交。","code":"VALIDATION_ERROR","declaredCode":"KPI_SUBMIT_INCOMPLETE","object":"kpi_entry_sheet"} -PASS T11 阶梯:87.3/90=97% → 命中「达标」得分率 90%,得分 18(权重 20) — rate=97 score=18 trace=实际 87.3 / 目标 90(越高越好)→ 完成率 97%;权重 20%;阶梯:命中「达标」得分率 90%;得分 = 20% × 90% = 18 -PASS T12 填报单汇总:指标得分合计 88、最终得分 88 — indicator_score=88 total=88 weight_total=100 -PASS T13 直接改状态被拒绝 — {"error":"修改填报单状态失败:状态由流程推进,不能直接修改。请使用提交、审核通过、驳回或归档按钮。","code":"VALIDATION_ERROR","declaredCode":"KPI_SHEET_DIRECT_STATUS","object":"kpi_entry_sheet"} -PASS T14 提交后进入分公司核对中 — status=branch_checking {"object":"kpi_entry_sheet","id":"u4KvzmsUHkHZLA0k","record":{"id":"u4KvzmsUHkHZLA0k","created_at":"2026-09-02T18:37:40.011Z","updated_at":"2026-09-02T18:37:41.284Z","organization_id":"org_mtkfufl3h3opu052","created_by": -PASS T15 按 3 家分公司生成并行核对任务 — 2026 年 8 月 月度考核 · 市场部 · 华东分公司 核对 | 2026 年 8 月 月度考核 · 市场部 · 华南分公司 核对 | 2026 年 8 月 月度考核 · 市场部 · 华北分公司 核对 -PASS T16 提交后修改明细被冻结拒绝 — {"error":"修改填报明细失败:填报单已提交,数据已冻结。如需更正,请发起数据调整申请。","code":"VALIDATION_ERROR","declaredCode":"KPI_LINE_LOCKED","object":"kpi_entry_line"} -PASS T17 提出争议但无意见被拒绝 — {"error":"提出争议失败:核对意见不能为空。请填写争议内容后再提交。","code":"VALIDATION_FAILED","fields":[{"field":"_record","code":"rule_violation","message":"提出争议失败:核对意见不能为空。请填写争议内容后再提交。"}],"object":"kpi_check_task"} -PASS T18 有争议未解决时不能推进 — {"error":"推进失败:还有 2 家分公司未核对、1 家分公司有争议未解决。请待全部确认后再推进。","code":"VALIDATION_ERROR","declaredCode":"KPI_CHECK_INCOMPLETE","object":"kpi_entry_sheet"} -PASS T19 全部分公司确认后自动推进到人力审核中 — status=hr_reviewing -PASS T20 驳回原因为空被拒绝 — {"error":"驳回失败:驳回原因不能为空。请填写原因后再驳回。","code":"VALIDATION_ERROR","declaredCode":"KPI_REJECT_REASON_REQUIRED","object":"kpi_entry_sheet"} -PASS T21 驳回退回上一节点(分公司核对),核对任务重置,原因留痕 — status=branch_checking reason=利润数据请复核 -PASS T22 人力审核通过 → 领导审批中 — status=leader_approving {"object":"kpi_entry_sheet","id":"u4KvzmsUHkHZLA0k","record":{"id":"u4KvzmsUHkHZLA0k","created_at":"2026-09-02T18:37:40.011Z","updated_at":"2026-09-02T18:37:42.621Z","organization_id":"org_mtkfufl3h3opu052","created_by": -PASS T23 领导审批通过 → 已通过,盖章 — status=approved -PASS T24 通过后生成考核结果(部门维度) — department:88#1 -PASS T25 加减分批准后计入最终得分 91 — bonus_total=3 total=91 {"object":"kpi_bonus","id":"xkcStxstEnj5aW-O","record":{"id":"xkcStxstEnj5aW-O","created_at":"2026-09-02T18:37:42.924Z","updated_at":"2026-09-02T18:37:42.982Z","organization_id":"org_mtkfufl3h3opu052","created_by":"qXGjl -PASS T26 调整申请记录调整前值 144 与主体 — {"object":"kpi_adjustment","id":"6wcDh21bAGgm8_6O","record":{"id":"6wcDh21bAGgm8_6O","created_at":"2026-09-02T18:37:43.132Z","updated_at":"2026-09-02T18:37:43.132Z","organization_id":"org_mtkfufl3h3opu052","created_by":" -PASS T27 源数据调整落地并重算:利润 180 → 完成率 100%,得分 30;结果重新汇总 — actual=180 score=30 total=106 results=106 -PASS T28 归档生成不可变快照(含校验和、总分) — snap=2026 年 8 月 月度考核 · 市场部 · 归档快照 checksum=6926cde940c4 total=106 -PASS T29 修改快照被拒绝 — {"error":"操作失败:归档快照由系统在归档时生成,不能手工新建、修改或删除。","code":"VALIDATION_ERROR","declaredCode":"KPI_SNAPSHOT_IMMUTABLE","object":"kpi_snapshot"} -PASS T30 删除快照被拒绝 — {"error":"操作失败:归档快照由系统在归档时生成,不能手工新建、修改或删除。","code":"VALIDATION_ERROR","declaredCode":"KPI_SNAPSHOT_IMMUTABLE","object":"kpi_snapshot"} -PASS T31 归档后明细连备注也不可改(已冻结) — {"error":"修改填报明细失败:填报单已提交,数据已冻结。如需更正,请发起数据调整申请。","code":"VALIDATION_ERROR","declaredCode":"KPI_LINE_LOCKED","object":"kpi_entry_line"} -PASS T32 归档后不能再加减分 — {"error":"修改加减分失败:填报单已归档,数据已锁定不可再改。","code":"VALIDATION_ERROR","declaredCode":"KPI_BONUS_ARCHIVED","object":"kpi_bonus"} -PASS T33 审核记录完整留痕(生成/提交/争议/确认/驳回/通过/调整/归档) — reject,confirm,confirm,adjust,submit,confirm,confirm,archive,approve,approve,approve,approve,confirm,dispute,confirm,generate -PASS T34 审核记录不可篡改 — {"error":"操作失败:审核记录是流程留痕,不能修改或删除。","code":"VALIDATION_ERROR","declaredCode":"KPI_REVIEW_IMMUTABLE","object":"kpi_review_record"} -PASS T35 复制上一版本:流程节点 4、指标下达 18 已复制 — steps=4 pis=18 {"object":"kpi_plan","id":"FUcyHYEqQkPKXTwE","record":{"id":"FUcyHYEqQkPKXTwE"," -PASS T36 两节点流程(填报→人力审核):提交直达人力审核,通过即已通过 — after submit=hr_reviewing final=approved -PASS T37 逆向指标:目标 12 实际 10 → 完成率 120%,封顶 110% → 得分 55(权重 50) — rate=120 score_rate=110 score=55 -PASS T38 发布第三版方案(市场部已配分管领导)生成 5 张填报单 — {"object":"kpi_plan","id":"vobGz2SXdJ7xzwAi","record":{"id":"vobGz2SXdJ7xzwAi"," sheets=5 -PASS T39 发布按方案配置写入动态共享规则,条数与方案配置精确相符,元数据零改动 — rules=29 expected=29 -PASS T39b 规则条件按方案隔离:每条规则的条件里都带本方案 id — 29 条 -PASS T40 规则求值把单元共享展开成逐人记录共享行(市场部填报单 → 用户 A) — shares=10 -PASS T41 部门填报人员只看到本部门(市场部)填报单,含本次新发布的那张 — 3 张:2026 年 9 月 月度考核 · 市场部 | 2026 年 10 月 月度考核 · 市场部 | 2026 年 8 月 月度考核 · 市场部 -PASS T42 打开本部门以外(华东分公司)的填报单被拒绝 — status=404 {"error":"Record aqb2KjB-ceDe7CFP not found in kpi_entry_sheet","code":"RECORD_NOT_FOUND","object":" -PASS T43 部门填报人员能填写实际值并提交本部门填报单 — status=branch_checking score=103 {"object":"kpi_entry_sheet","id":"Z3Tl5rlPXx_G8FVT","record":{"id":"Z3Tl5rlPXx_G8FVT","created_at":" -PASS T44 分公司核对人员只看到本分公司(华东)的核对任务 — 2 条:2026 年 8 月 月度考核 · 市场部 · 华东分公司 核对 | 2026 年 10 月 月度考核 · 市场部 · 华东分公司 核对 -PASS T45 分公司核对人员能确认本分公司的核对任务 — {"object":"kpi_check_task","id":"yaDbkaBS3t2xtGjd","record":{"id":"yaDbkaBS3t2xtGjd","created_at":"2026-09-02T18:37:48.0 -PASS T46 分管领导只看到分管主体(市场部)的填报单,看不到运营部 — 1 张:2026 年 10 月 月度考核 · 市场部 -PASS T47 分管领导能在「领导审批中」执行审核通过 — beforeLeader=leader_approving? status=approved {"object":"kpi_entry_sheet","id":"Z3Tl5rlPXx_G8FVT","record":{"id":"Z3Tl5rlPXx_G8FVT","created_at":"2026-09-02T18:37:46. -PASS T48 已通过填报单批准加减分后,部门结果立即等于新的最终得分 — before=103 after=106 sheet_total=106 -PASS T49 否决加减分后立即重算,被否决的分不计入部门结果 — after_reject=106 expected=106 sheet_total=106 {"object":"kpi_bonus","id":"nHyutqigPselMkGu","record":{"id":"nHyutqigPselMkGu", -PASS T50 不存在方案发布之外来源的 KPI 共享规则(静态规则文件已删除,也没有旧的按单元规则) — 77 条,示例 kpi_pvobgz2sxdj7x_qxpeji_result_bu_market:admin | kpi_piz8wrxlurbl6_17ug4yf_adjust_bu_south:admin -PASS T51 填报人员看到本部门结果与本人到人结果,看不到他人的到人结果 — dept=true own=true other=false | A 可见 3 条:person,department/bu_market,department/bu_market -PASS T53 分管领导看到所分管主体(市场部)的部门结果,看不到非分管主体的结果 — 2 条:leader,department/bu_market -PASS T54 换人后新方案只为新任分管领导建规则,老领导没有新方案的规则 — 新方案 27 条规则 -PASS T55 换掉分管领导:老领导看不到新方案的填报单,历史方案的仍看得到 — 老领导可见 1 张:2026 年 10 月 月度考核 · 市场部 -PASS T56 新任分管领导看到新方案的市场部填报单 — 新领导可见 1 张:2026 年 11 月 月度考核 · 市场部 -PASS T57 方案关闭后填报单 / 核对任务 / 调整的共享降为只读(留读、去写) — 29 条规则,其中可编辑 0 条 -PASS T52 演示种子组织单元在启动时被补上组织归属(与 Setup 里新建的单元一致) — bu_east:org_mtkfufl3h3opu052 bu_hq:org_mtkfufl3h3opu052 bu_hr:org_mtkfufl3h3opu052 bu_market:org_mtkfufl3h3opu052 bu_north:org_mtkfufl3h3opu052 bu_ops:org_mtkfufl3h3opu052 bu_south:org_mtkfufl3h3opu052 bu_west:org_mtkfufl3h3opu052 -PASS T58 部门填报人员登记加减分被拒绝(权限集不再声明新建权限) — status=403 拒绝层=权限集 {"error":"You do not have permission to perform this action. Contact your administrator if you need access.","code":"PERMISSION_DENIED","obj -PASS T59 非人力负责人岗位批准加减分被拒绝(第 10 章第 6 项:人力负责人审批) — status=422 {"error":"审批加减分失败:只有人力负责人岗位可以审批加减分。请转交人力负责人处理。","code":"VALIDATION_ERROR","declaredCode":"KPI_BONUS_APPROVE_POSITION","object":"kpi_bonus"} -PASS T60 人力负责人岗位批准加减分成功,最终得分随之重算 — before=106 after=108 bonus_total=5 -PASS T61 同周期已有生效版本时发布被拒并点名该版本;关闭后可发布(第 10 章第 10 项) — dup=422 {"error":"发布失败:该考核周期已有生效版本「2026 年 11 月 月度考核」。请先关闭该版本,或改用复制出的新版本替换。","code":"VALIDATION_ERROR","declaredCode":"KPI_PLAN_PERIOD_CONFLICT","object":"kpi_plan"} | after_close=200 status=published -PASS T62 结果调整落地后该行标记「已调整」= 是、调整类型 = 计算结果,未调整行保持否 — adjusted: is_adjusted=true type=result final=12.5 | untouched: is_adjusted=false {"object":"kpi_adjustment","id":"uS21LjGDIZ5PSmD9","record":{"id":"uS21LjGDIZ5PS -PASS T63 考核结果的计算明细 JSON 逐指标带 is_adjusted,被调整的那条为 true — lines=3 adjusted={"plan_indicator":"9XF-OUURYgyL99km","indicator_name":"客户满意度","score_rate":90,"score":12.5,"is_adjusted":true,"adjust_type_applied":"result"} -PASS T64 分公司核对人员直接修改填报明细实际值被拒绝(第 10 章第 13 项:只能确认 / 提出争议) — status=403 {"error":"You do not have permission to perform this action. Contact your administrator if you need access.","code":"PERMISSION_DENIED","obj -PASS T65 人力审核账号登记加减分成功(岗位规则已授予记录级编辑权) — share=true register=201 {"object":"kpi_bonus","id":"5t_S0omXRCdAOG71","record":{"id":"5t_S0omXRCdAOG71","created_a -PASS T66 人力审核账号批准自己登记的加减分被拒绝(第 10 章第 6 项:人力负责人审批) — status=422 {"error":"审批加减分失败:只有人力负责人岗位可以审批加减分。请转交人力负责人处理。","code":"VALIDATION_ERROR","declaredCode":"KPI_BONUS_APPROVE_POSITION","object":"kpi_bonus"} -PASS T67 人力负责人账号批准加减分成功,填报单最终得分随之重算 — approved_by=true before=103 after=105 -PASS T68 人力负责人账号登记加减分被拒绝(权限集不声明新建权限) — status=403 拒绝层=权限集 {"error":"You do not have permission to perform this action. Contact your administrator if you need access.","code":"PERMISSION_DENIED","obj -PASS T69 人力审核账号在人力审核节点驳回与审核通过均成功(真实岗位账号推进流程) — reject=200→draft approve=200→approved -PASS T70 方案关闭后人力岗位对该方案填报单降为只读(留读、去写) — 岗位规则 4 条全部 read=true read=200 write=403 -PASS T71 组织为空的已发布方案仍阻断同周期发布(租户判定不靠查询过滤) — seed_org=null seed_status=published dup=422 {"error":"发布失败:该考核周期已有生效版本「2026 年 8 月 月度考核」。请先关闭该版本,或改用复制出的新版本替换。","code":"VALIDATION_ERROR","declaredCode":"KPI_PLAN_PERIOD_CONFLICT","object":"kpi_p -{"passed":73,"failed":0} -```