Skip to content

核对任务批量确认;数据调整与加减分表单只留业务字段、状态默认草稿 (#37) - #42

Merged
baozhoutao merged 1 commit into
mainfrom
issue-37-bulk-confirm-forms
Sep 6, 2026
Merged

核对任务批量确认;数据调整与加减分表单只留业务字段、状态默认草稿 (#37)#42
baozhoutao merged 1 commit into
mainfrom
issue-37-bulk-confirm-forms

Conversation

@baozhoutao

@baozhoutao baozhoutao commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

关联工作项 #37

改了什么

核对任务批量确认。 CheckTaskViews.listViews.pending 开多选并挂 bulkActions: ['kpi_check_confirm']。spec 的裸字符串形式是逐记录派发:渲染器按名字取到对象上已有的 kpi_check_confirm,连同它自己的 label、选填参数「核对意见」与 visible 谓词一起提升成批量按钮,对勾选的每条记录各发一次。动作体一个字没改,src/actions/index.ts 零改动,岗位校验 / 填报单状态校验 / 盖章 / 留痕 / 全部确认后自动推进仍全部落在 check-task.hook.ts(本 PR 未改该文件)—— 批量只省点击,不省规则。

「提出争议」故意不进 bulkActions:争议内容必填且逐条不同,一份意见套给多条记录就是伪造留痕。它保持行内单条。

两张表单收敛。 AdjustmentViews / BonusViews 的表单各只留五个业务字段,状态与审批字段移出。其中「审批意见」原来在调整申请表单上是可编辑的,等于让申请人替审批人写意见,一并移出。移出 ≠ 删掉:记录详情页的字段清单来自对象定义、不来自表单视图(与此前收敛填报明细表单时同一条路),这些字段在记录页照常只读可见。

两个 hook 各加一行默认状态。 beforeInsertif (!input.status) input.status = 'draft',只补空缺:带了状态的写入(按钮、脚本、导入)行为一个字不变,状态机 transitions / initialStates、岗位分离、归档锁、已批准锁一律未动。API 负面用例实测:带 status=approved 直接新建/登记仍被 invalid_initial_state 拒(400)。

实测数字(Playwright 1440×900 zh-CN,真实岗位账号)

场景 改前 改后
「待我核对」确认 10 条 30 击 4 击
新建数据调整(部门填报人员) 12 击 10 击
登记加减分(人力审核) 11 击 9 击

批量确认后 10 条全部 confirmed,处理人与处理时间逐条盖章,审核记录逐条写入,3 家分公司全确认的 7 张填报单自动推进到「人力审核中」。

平台受限项(不绕行,只上报)

新建成功后仍会离开列表:对象可见字段数 < 12 弹记录抽屉、≥ 12 跳记录页。工作项第 4 项要求把它设为回列表,17.2.0 上没有元数据开关 —— 实测在 form view 上显式声明 submitBehavior: { kind: 'continue' },validate 通过且无告警,行为完全不变(探针已回滚,不在本 PR 里)。控制台这条路径的 onSuccess 短路了 submitBehavior,去向由 recordSurface(objectDef) 的字段数启发式算出。

已按纪律上报 objectstack-ai/objectstack#16381(现象 / 最小复现 / 期望能力 / 平台版本 17.2.0),不改平台包、不给对象凑字段数绕行。改后行为与改前一致,没有回退。

验证

pnpm verify               → validate ✓ / typecheck ✓ / vitest 139 passed ✓ / i18n 新鲜度 ✓
scripts/software-flow.mjs → {"passed":55,"failed":0}   (含加减分与数据调整用例)
scripts/e2e-flow.mjs      → {"passed":74,"failed":0}

两脚本各在空库重建的实例上跑。工作台「待我核对」区块回归正常。

测试报告(含 26 张改前改后截图、逐击计数)与需求符合度清单已挂 #37 评论;截图在 acceptance-evidence 分支 issue-37/,commit 4ae42dd6fbaa856f548c8d68f3ce2e174897641e

改动面

src/views/index.tsCheckTaskViews / BonusViews / AdjustmentViews 三段、src/hooks/adjustment.hook.tssrc/hooks/bonus.hook.ts、重新生成的 src/translations/zh-CN.objects.generated.ts。未触碰 src/actions/src/objects/src/lib/src/services/src/security/src/pages/sheet.hook.tscheck-task.hook.tsentry-line.hook.tsscripts/docs/

🤖 Generated with Claude Code

@baozhoutao

Copy link
Copy Markdown
Contributor Author

代码评审报告(os-project-dev-review)

档位:全量档(依据:改已有视图三段 + 两个 hook 的 beforeInsert;hook 属状态机所在层,调度员派发时指定全量档并加派调用面抽查)
结论:可合并

评审子 agent 冷启动,只读三样输入:工作项 #37 全文、本 PR 全量 diff、需求符合度清单原文。未读开发方测试报告 / 开工前方案评论 / 交接评论 / 对话记录。全程只读:未改任何文件、未合并、未动标签与处理人。

实跑环境:临时 clone issue-37-bulk-confirm-forms(b27d441,base c197bf4 = main),pnpm install --frozen-lockfile,独立实例端口 3115 + 独立库,OS_SEED_PROFILE=software,岗位账号由 scripts/software-people.mjs 建立。评审完毕后实例按 PID 停止、临时目录删除。


通用质量(/code-review 高档)

无阻塞级 / 应修级发现。 逐处过了四个改动点:

  • adjustment.hook.ts:61 的默认值落在 if (ctx.event === 'beforeInsert') 分支内、return 之前,分支外的审批盖章、approved 锁定、落地重算、归档锁一字未动。
  • bonus.hook.ts:79 的默认值带 ctx.event === 'beforeInsert' 显式闸门,且位于 requiredBonusPosition() 调用之前——该纯函数在 beforeInsert 时无条件返回 BONUS_REGISTER_RULE(bonus.hook.ts:49),不读 input.status,所以补状态不会改变岗位判定的结果;beforeUpdate 分支被闸门整个隔开。两处改动均满足「只在输入未带 status 时补,不替状态机放行任何值」。
  • 视图三段只做「加多选 + 挂批量动作」与「删字段」,没有新增校验、没有改动作体、没有改 list / disputed / pending(加减分、调整)等其余视图段。
  • 移出表单的字段没有孤儿引用:decision_reasonkpi_adjustment_approve(选填)/ kpi_adjustment_reject(必填,动作体内再校一次)两个动作写入,对象级 adjustment_reject_reason 校验因此仍有输入来源;signed_points / old_value 由 hook 计算;列表视图与 snapshot-service 读的是对象字段,不是表单视图。

专属核查(逐条)

1 改动面越界:✅ 无越界
git diff --name-only c197bf4..b27d441 恰为 4 个文件:src/hooks/adjustment.hook.tssrc/hooks/bonus.hook.tssrc/translations/zh-CN.objects.generated.tssrc/views/index.ts
禁触碰面 git diff --stat 为空:src/hooks/sheet.hook.tssrc/hooks/check-task.hook.tssrc/hooks/entry-line.hook.tssrc/servicessrc/securitysrc/objectssrc/pagessrc/appssrc/datasrc/libsrc/actionsscriptsdocs 全部零改动 —— 清单 1.6「未新增动作,src/actions/index.ts 零改动」属实。
src/views/index.ts 的改动只落在 CheckTaskViews.listViews.pendingBonusViews.formViewsAdjustmentViews.formViews 三段,与放行面一致。
翻译包:pnpm i18n:extract:check 新鲜度门禁通过(zh-CN 530/530 in sync),重生成对账无差异;删除的正是随「审批」分区消失的 _sections.decision.label

2 降级对账:✅ 清单相符
逐条 ✅ 定位到代码并实测(见下「实测结论」)。非 ✅ 的两条有据:

  • 2.4 ⚠️「调整前值即时带出」:表单上该字段已按正文要求整体移出(正文点名 old_value 要移出可编辑字段),因此形态上不存在「选中明细后回填本表单」的落点;hook 保存时回填 + 记录页只读展示实测可用(记录页 调整前值 60.0000)。出口是正文 §范围 2 明写的「若平台表单不支持联动带出,如实记录」。
  • 4.1 ❌ / 7.8 ⚠️「新建后不弹抽屉」:出口 console: 列表「新建」成功后的去向只由对象字段数决定,form view 的 submitBehavior 在这条路径上解析通过却静默无效 objectstack#16381 存在且描述一致(open,标题即「列表『新建』成功后的去向只由对象字段数决定,form view 的 submitBehavior 在这条路径上解析通过却静默无效」,含现象 / 最小复现 / 三选一期望能力 / 版本 17.2.0)。旁证两条:① @objectstack/spec@17.2.0FormViewSchema.submitBehavior 文档只把默认去向写给 /_console/f/:slug/_console/forms/:name 两条路径,列表「新建」不在其中;② 本次实测复现了该 issue 描述的字段数启发式 —— kpi_bonus(10 字段)创建后弹抽屉,kpi_adjustment(15 字段)创建后整页跳记录页。走「只上报、不绕行」正确,代码里没有为凑字段数或改平台包留下任何痕迹。

hook 改动严格只补空缺 status,三条负面用例坐实(见下)。清单里没有发现「标 ✅ 而代码是 TODO / 注释掉的校验 / 更少分支」的情形。

3 三禁痕迹:✅ 无
diff 不含 node_modules / 依赖目录 / patch 类文件;批量确认走 bulkActions 的裸字符串形式(spec view.zod.ts:1541 + bulk-action.zod.ts 明确:裸字符串 = 逐记录派发,bulkActionDefsexecution: 'aggregate' 才是一次调用覆盖整个选区),未用 bulkActionDefs、未新增绕过 hook 直写状态的数据面动作。实测坐实逐条走 hook(下文 ①)。探针类临时代码未混入提交(diff 里没有 submitBehavior 声明残留)。

4 硬拍板落地:✅
src/objects/ 零改动 → 本单不新增数字字段,数字四件套无新增核查对象;既有 old_value / new_valuescale/min/max 未被触碰。
用户可见文案零新增:批量按钮复用动作已有 label「确认无误」与参数 label「核对意见(选填)」;表单只删字段、未改 label。必填标记实测正确 —— 数据调整表单五项全部带 *,加减分表单五项全部带 *,再无「必填却无默认值、下拉里摆着会被拒的值」的状态框。std-copy 四条红线:无内部代号、无异常原文、术语沿用需求文档、既有报错仍是三段式(负面用例回显的 invalid_initial_state 提示为中文业务话术)。

调用面抽查(全量档追加)

抽查点 结论 证据
adjustment.hook.ts beforeInsert 只在 status 缺失时补「草稿」 不带 status 新建 → 201 status=draft
带非法初始状态仍被拒、invalid_initial_state 等既有规则未变 status=approved400 invalid_initial_state「调整申请只能按『草稿 → 待审批 → 已批准 / 已否决』推进。」;带 status=submitted 同样 400(证明 hook 没有替状态机放行任何值)
bonus.hook.ts 同上 不带 status 登记 → 201 status=draft signed_points=2;带 status=approved400 invalid_initial_state;人力负责人登记 → 403(岗位分离与权限集未变)
批量确认逐记录派发时 hook 是否逐条执行 一次批量 10 条:10 条全部 confirmed,decided_by / decided_at 逐条盖章且时间戳各不相同;kpi_review_record 新增 10 条 action=confirm 留痕
全部确认后自动推进 三家分公司确认齐后 11 张填报单全部由 branch_checkinghr_reviewing
「提出争议」未被拉进批量 批量条上只有「确认无误」一个按钮
混选「已确认 / 有争议」任务的行为 ✅(不可达) 多选与批量按钮只挂在 pending 这一个视图,该视图硬过滤 status = pending;「全部核对任务」与「有争议」两个视图实测无勾选列。即便行状态在页面打开后被他人改动,逐记录派发仍逐条过 check-task.hook(岗位、填报单状态、撤回禁令)复校,批量只省点击不省规则
表单收敛后审批字段仍在记录页可见、审批动作不受影响 人力审核账号打开调整记录页:状态 / 调整前值 / 申请人 / 审批人 / 审批时间 / 落地时间 全部只读可见;「批准并落地」执行成功并盖章;人力负责人账号「批准」加减分成功并盖章;填报人员「提交审批」成功

实测结论与点击计数(评审员亲自数)

场景 我的点击数 结果
① 分公司核对人员(陈东)在「待我核对」批量确认 10 条 4 击(表头全选 → 确认无误 → 下一步 → 执行) 队列一次清空;10 条逐条盖章 + 10 条审核记录
①' 换分公司核对人员(高北)重跑 10 条 4 击 同上;最后一家确认后 11 张填报单自动推进
② 填报人员(赵敏)新建数据调整,只填五个业务字段 10 击(从列表页起算) 一次建成,状态 = 草稿,调整前值由 hook 回填 60.0000;创建后整页跳记录页(非抽屉)
②' 提交审批 1 击 → 待审批,人力审核可批准并落地
③ 人力审核(马丽)登记加减分,只填五个业务字段 9 击 一次建成,状态 = 待审批,计入分值 3.00 由 hook 计算;创建后弹记录抽屉(#16381 现象)
③' 人力负责人(何平)批准 2 击 成功,审批人盖章

回归:pnpm verify 全绿(validate / typecheck / vitest 139 passed / i18n 新鲜度);scripts/software-flow.mjs 55/55 PASS;scripts/e2e-flow.mjs 74/74 PASS(含 T59 / T66 / T68 岗位分离用例)。

发现清单

级别 位置 问题 处置出口
⚪ 记录 src/views/index.ts:94 注释 代码注释引用了本单以外的工作项编号作为「同一条路」的旁证 无需处理,留痕即可
⚪ 记录 adjustment.hook.ts:61 / bonus.hook.ts:79 判定用 !input.status,除「未带」外也覆盖 null / 空串;这两种取值本就不是合法初始状态,落成草稿比报错更合理,但与注释里「输入未带 status」的措辞略有出入 留痕,不阻塞
⚪ 记录 需求符合度清单 5.2 清单写「删掉 3 个词条」,实际重生成后删除的是 1 个词条(_sections.decision.label,3 行);代码与新鲜度门禁均正确,仅自述措辞不准 留痕,不阻塞
⚪ 记录 批量确认的部分失败反馈 逐记录派发时若个别记录被 hook 拒(并发下填报单已离开「分公司核对中」),失败反馈形态由平台渲染器决定,本次未构造到该竞态;单条路径行为与之相同,非本 PR 引入 留痕;若试运行中出现再按平台受限口径上报

🔴 阻塞:。🟡 应修:

@baozhoutao
baozhoutao merged commit b94a4a6 into main Sep 6, 2026
1 check passed
@baozhoutao
baozhoutao deleted the issue-37-bulk-confirm-forms branch September 6, 2026 17:26
分公司核对人员清空「待我核对」原来要逐条走「更多操作 → 确认无误 → 确认」,
10 条 30 击(UI 实测);数据调整与加减分的新建表单把状态与审批字段摆给填报人员,
「状态」必填、无默认值,下拉里还列着会被状态机拒掉的值,要试错才建得出草稿。

- CheckTaskViews.pending 开多选并挂 bulkActions: ['kpi_check_confirm']。裸字符串
  形式是逐记录派发,动作体一个字没改,岗位校验 / 填报单状态校验 / 盖章 / 留痕 /
  全部确认后自动推进仍全部落在 check-task.hook.ts。10 条实测 30 击 → 4 击。
  「提出争议」故意不进批量:争议内容必填且逐条不同,保持行内单条。
- AdjustmentViews / BonusViews 的表单各收敛到五个业务字段,状态与审批字段移出。
  移出 ≠ 删掉:记录详情页的字段清单来自对象定义,这些字段在记录页照常只读可见。
  「审批意见」原来在申请表单上可编辑,一并移出——审批意见只由审批按钮写。
- 两个 hook 的 beforeInsert 各加一行「输入未带 status 时置草稿」。只补空缺:
  带了状态的写入行为不变,状态机 transitions / initialStates 与岗位规则一个字没动
  (API 实测带 status=approved 直接新建仍被 invalid_initial_state 拒)。

新建成功后仍会离开列表(字段数 < 12 弹记录抽屉、≥ 12 跳记录页):控制台这条路径
不读 form view 的 submitBehavior,声明 { kind: 'continue' } 实测无效,去向只由对象
字段数算出。不绕行、不改平台包,已上报 objectstack-ai/objectstack#16381。

pnpm verify 绿;scripts/software-flow.mjs 55/55、scripts/e2e-flow.mjs 74/74 全 PASS。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant