Skip to content

feat(cli): add approval order workflow#428

Open
Jinghao-coding wants to merge 3 commits into
raids-lab:mainfrom
Jinghao-coding:feat/cli-approval-orders
Open

feat(cli): add approval order workflow#428
Jinghao-coding wants to merge 3 commits into
raids-lab:mainfrom
Jinghao-coding:feat/cli-approval-orders

Conversation

@Jinghao-coding

@Jinghao-coding Jinghao-coding commented Jun 15, 2026

Copy link
Copy Markdown
Member

Summary / 摘要

English

This PR adds a complete approval-order workflow to the Crater CLI on a separate branch. It covers user submission/cancellation and admin review actions, including optional job locking before approval.

中文

本 PR 在独立分支中为 Crater CLI 增加完整审批工单流程,覆盖用户提交/取消工单,以及管理员审核工单;其中作业类工单支持在批准前先锁定作业。

Added Commands / 新增命令

User workflow / 用户流程

  • crater order submit
    • Submit an approval order.
    • 提交审批工单。
  • crater order edit <id>
    • Edit approval order fields or status through the existing update API.
    • 通过现有更新接口编辑审批工单字段或状态。
  • crater order ls
    • List orders created by the current user.
    • 查看当前用户创建的工单。
  • crater order get <id>
    • View an order detail.
    • 查看工单详情。
  • crater order by-name <name>
    • Find orders by target name, typically by job name.
    • 按目标名称查询工单,通常用于按作业名查询。
  • crater order cancel <id> --yes
    • Cancel/delete an order.
    • 取消/删除工单。

Admin workflow / 管理员流程

  • crater order ls --admin
    • List all approval orders.
    • 查看所有审批工单。
  • crater order get <id> --admin
    • View an order through the admin detail API.
    • 使用管理员详情接口查看工单。
  • crater order approve <id>
    • Approve an approval order.
    • 批准审批工单。
  • crater order approve <id> --lock ...
    • Lock the job first, then approve the job approval order. If locking fails, the order is not approved.
    • 先锁定作业,再批准作业审批工单;如果锁定失败,不会批准工单。
  • crater order reject <id> --review-notes <reason>
    • Reject an order with review notes.
    • 带审核备注拒绝工单。
  • crater order check --yes
    • Ask the backend to cancel invalid pending job approval orders.
    • 调用后端检查并取消已失效的待审批作业工单。

Implementation Details / 实现说明

English

  • Adds an approval-order API client under cli/internal/api.
  • Adds active credential/token loading helper for authenticated API commands.
  • Adds crater order command implementation under cli/cmd.
  • Adds local validation for required fields, enum values, edit status, review notes, and job-lock duration.
  • Adds --json --no-interactive support for all commands.
  • Adds shell completion candidates for approval order type flags.
  • Adds i18n strings for command help, flags, table output, and validation errors.
  • Adds a dedicated crater-cli-approval-order Skill for AI-agent usage.
  • Updates cli/docs/COMMANDS.md with the approval-order command contract.

中文

  • cli/internal/api 中新增审批工单 API client。
  • 新增读取当前 active credentials/token 的认证 API helper。
  • cli/cmd 中新增 crater order 命令实现。
  • 对必填字段、枚举值、审核备注、作业锁定时长做本地校验。
  • 所有命令支持 --json --no-interactive
  • 为审批类型等枚举 flag 增加 shell 补全。
  • 补充命令帮助、flag、表格输出、校验错误的 i18n 文案。
  • 新增 crater-cli-approval-order Skill,方便 AI Agent 正确执行审批工作流。
  • 更新 cli/docs/COMMANDS.md 中的审批工单命令契约。

Test Coverage / 测试覆盖

Added tests / 新增测试

  • Added approval-order snapshot tests in cli/test/snapshots/order/order_test.go.
  • Added English and Chinese golden snapshots:
    • cli/testdata/snapshots/order/order.en.txtar
    • cli/testdata/snapshots/order/order.zh-CN.txtar

Snapshot cases cover / 快照覆盖场景

  • Unknown subcommand.
  • Missing and invalid order ID.
  • Missing submit/edit fields.
  • Invalid edit status.
  • Invalid order type.
  • Reject without review notes.
  • Approve with --lock but no lock duration.
  • Cancel/check without --yes in non-interactive mode.
  • API 404 paths for list, submit, and approve.

Verification / 验证

  • UPDATE_SNAPSHOTS=1 go test ./test/snapshots/order
  • go test ./...
  • go build ./...

Notes / 备注

English

This PR intentionally implements the write workflow for approval orders only. It does not include the broader read-resource CLI from the other branch.

中文

本 PR 只实现审批工单写流程,不包含另一个分支中的通用资源读取 CLI。

@AkashiSensei AkashiSensei left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

可以先不着急改,还有个比较费劲的问题,目前 --admin 作为管理员操作不合理,我先统一约定下这个问题吧,管理员命令使用统一的 admin 前缀,然后管理员和用户 skill 分离。

Comment thread cli/cmd/order.go Outdated
return nil
}

func runOrderReject(cmd *cobra.Command, args []string) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

order reject 会调用普通 PUT /api/v1/approvalorder/{id} 更新工单为 Rejected。但后端当前只拦截非管理员设置 Approved,没有拦截 Rejected / Canceled / 修改内容,也没有校验工单创建者。这样普通用户只要知道 order id,就可能通过 CLI 拒绝或改写他人的工单。建议先在后端补齐权限校验,或在 CLI 合并前不要暴露审核类写操作。

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AkashiSensei 已补后端权限边界,并同步调整 CLI。普通 PUT /api/v1/approvalorder/{id} 现在只允许创建者更新自己的 Pending 工单,且不允许通过该接口变更为审核状态;管理员审核拆到新的 PUT /api/v1/admin/approvalorder/{id}/review,只允许管理员调用。CLI 侧也不再把审核动作暴露在普通 crater order ... 下,改为 crater admin order approve/reject/check。对应代码:backend/internal/handler/approvalorder.gocli/cmd/order.gocli/internal/api/approvalorder.go。验证:go test ./...(cli)、go test ./internal/handler/operations ./internal/bizerr ./internal/resputil(backend)、go build ./cmd/crater/main.go(backend)。

Comment thread cli/cmd/order.go Outdated
lockEnabled, _ := cmd.Flags().GetBool("lock")
permanent, _ := cmd.Flags().GetBool("permanent")
days, _ := cmd.Flags().GetInt("days")
lockHours, _ := cmd.Flags().GetInt("hours")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--hours 在 flag 注册处是 Uint,但这里用 GetInt("hours") 读取。pflag 会返回类型不匹配错误;当前代码忽略错误后 lockHours 变成 0,因此 crater order approve ... --lock --hours 6 会被误判为没有锁定时长。建议改成 GetUint 后显式转换,或拆出独立的 --lock-hours

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AkashiSensei 已修复。CLI 的 order edit 现在会先读取工单详情并保留未显式传入的字段;管理员审核命令改走 review-only API,只发送 status/reviewNotes。后端从 token 填充 reviewerID,并且新审核 API 不覆盖原 content。对应代码:cli/cmd/order.gocli/internal/api/approvalorder.gobackend/internal/handler/approvalorder.go。覆盖测试:新增 cli/test/snapshots/order/order_test.go,并更新 cli/test/snapshots/read/read_matrix_test.go 覆盖完整 order/admin order 命令。

Comment thread cli/cmd/order.go
return api.ApprovalOrderRequest{}, errUsageFromIssues(issues)
}

return api.ApprovalOrderRequest{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审核命令会完整构造 update payload;reviewerID 默认 0,reason / type-id / hours 不传也会以空值或 0 发送。后端会按这个 payload 覆盖工单字段,因此批准/拒绝时可能清空原申请原因、延期小时数,并把审核人写成 0。建议审核前先读取工单详情,默认保留原 content,并从 active auth info 自动填 reviewerID,或强制要求所有会被覆盖的字段。

Comment thread cli/cmd/order.go Outdated
}
lockEnabled, _ := cmd.Flags().GetBool("lock")
permanent, _ := cmd.Flags().GetBool("permanent")
days, _ := cmd.Flags().GetInt("days")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--days / --minutesInt,当前校验只判断三段时长是否全为 0。--lock --days -1 这类输入会通过本地校验,后端会直接把负 duration 加到锁定时间上。建议本地拒绝任意负值,后端也补一层保护。

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AkashiSensei 已在 CLI 和后端都补了保护。CLI 会拒绝 --lock --days -1 / --minutes -1 等负数输入,也会在非永久锁定时要求正 duration;后端 AddLockTime 同样拒绝任意负数 duration,并要求非永久锁定必须提供正 duration。对应代码:cli/cmd/order.gobackend/internal/handler/operations/lock.go。覆盖测试:cli/test/snapshots/order/order_test.go 中包含 admin order approve --lock 缺少 duration 和负数 duration 两类场景。

@Jinghao-coding Jinghao-coding force-pushed the feat/cli-approval-orders branch from 60d66d2 to 41afd94 Compare June 17, 2026 12:38
@Jinghao-coding

Copy link
Copy Markdown
Member Author

@AkashiSensei 已按本轮 review 完成调整并推送到 PR 分支(最新提交 41afd945)。

主要修改:

  1. 管理员命令统一改为 crater admin order ...

    • 用户侧保留:crater order ls/get/by-name/submit/edit/cancel
    • 管理员侧新增/保留:crater admin order ls/get/approve/reject/check
    • 不再使用 --admin 做管理员视图切换。
  2. 后端补齐审批安全边界

    • 普通 PUT /api/v1/approvalorder/{id} 只允许创建者更新自己的 Pending 工单。
    • 普通用户不能通过该接口设置 Approved/Rejected/Canceled
    • 新增 PUT /api/v1/admin/approvalorder/{id}/review,管理员审核只更新 status/reviewNotes/reviewerID,不覆盖原工单内容。
    • reviewerID 由后端从 active token 自动写入。
  3. 修复审核 payload 覆盖问题

    • crater order edit 会先读取工单详情,保留未显式传入的字段。
    • crater admin order approve/reject 走 review-only API,只发送审核状态和备注。
  4. 补齐锁定时长校验

    • CLI 拒绝负数 lock duration。
    • 后端 AddLockTime 也拒绝负数,并要求非永久锁定必须提供正 duration。
  5. 文档、Skill、测试同步

    • 更新 cli/docs/COMMANDS.md
    • 新增 cli/skills/crater-cli-approval-order/SKILL.md
    • 新增 cli/skills/crater-cli-admin-approval-order/SKILL.md
    • 新增 cli/test/snapshots/order/order_test.go 和中英文 golden。
    • 更新 cli/test/snapshots/read/read_matrix_test.go,覆盖所有 order/admin order 子命令 help。
    • 重新生成 Swagger docs,包含新的 admin review API。

验证:

  • go test ./...(cli)
  • go test ./test/snapshots/order(cli)
  • go test ./test/snapshots/read(cli)
  • go test ./internal/handler/operations ./internal/bizerr ./internal/resputil(backend)
  • go build ./cmd/crater/main.go(backend)
  • make docs(backend)

@Jinghao-coding

Copy link
Copy Markdown
Member Author

@AkashiSensei 又补了一轮后端 order lifecycle 修复,并修掉了之前 backend-lint-check 失败的问题(最新提交 79e468ea)。

这轮主要处理:

  1. 自动审批判断不再依赖 ReviewNotes 文案字符串

    • 原来通过固定 review note 字符串判断 48 小时内是否自动审批过,容易被文案变更影响。
    • 现在改为用结构化条件判断:type=jobstatus=ApprovedreviewerID=0 表示系统自动审批。
  2. 检查待审批 job 工单时补全活跃状态

    • 原实现注释提到 Running/Pending/Inqueue,但实际只保留 Running。
    • 现在 isJobRunning 覆盖 RunningPending 和 Crater 自定义 Prequeue,避免误取消仍在排队/预排队的工单。
  3. 删除/取消工单加状态限制

    • 现在创建者只能删除自己的 Pending 工单。
    • 已审核工单不再允许被普通删除,保留审计记录。
  4. 修正 request struct JSON tag

    • 修正 approvalorderTypeID / approvalOrderReason 的 struct tag 格式,避免反射/文档工具受到异常 tag 影响。
  5. 修复 backend lint workflow

    • 新增/改动行里的错误返回已迁到 bizerr + resputil.HandleError,不再触发 diff lint 的 deprecated resputil.Error/BadRequestError 检查。

验证:

  • make lint(backend,full + diff,0 issues)
  • make lint-diff(backend,0 issues)
  • go test ./internal/handler/operations ./internal/bizerr ./internal/resputil(backend)
  • go build ./cmd/crater/main.go(backend)
  • make docs(backend)
  • go test ./...(cli)

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.

2 participants