70131c6161
P0-3: - 批量审核:新增 POST /api/v1/review-candidates/batch-decision,逐条复用单条审核的写入 路径(同一套合法性校验与"最新验收轮次"绑定),逐条回报 decided/skipped/skipped_ids; 不可审核的条目只计入 skipped,不打断整批,其他错误整批回滚(不留半成品)。 界面新增「全选可批准」「批量批准选中」「批量驳回选中」。 - 建批即锁定:建批请求加 seal 字段,服务端在创建后立即执行既有的密封(仍逐项复核门禁, 失败整批回滚),草稿态不再单独占一步;界面按钮改为「创建并锁定清单」,流程说明从 8 步改 7 步。 - 判据修正:「可批准」原先把已驳回的算进去(review_state !== 'approved'),改为只含 pending + 门禁通过。 语义边界(写进测试):批量批准按人工身份记录,人工可推翻自己先前的驳回;system:auto 发起的 批量批准对人工驳回过的那条必须跳过(Decided=0/Skipped=1)——机器永远不覆盖人工。 测试:review_batch_integration_test.go(批量决定/跳报/重复 id 拒绝/机器不覆盖人工、 建批即锁定)、review_batch_test.go(幂等键、400/409/500/501 映射、服务端盖操作人)、 reviewRelease.test.ts(批量请求体与逐条回报、建批带 seal)。前端 181 条通过。
79 lines
2.3 KiB
Go
79 lines
2.3 KiB
Go
package governance
|
|
|
|
import "time"
|
|
|
|
type ReleaseBatchState string
|
|
|
|
const (
|
|
ReleaseBatchDraft ReleaseBatchState = "draft"
|
|
ReleaseBatchSealed ReleaseBatchState = "sealed"
|
|
ReleaseBatchCancelled ReleaseBatchState = "cancelled"
|
|
)
|
|
|
|
func (state ReleaseBatchState) Valid() bool {
|
|
switch state {
|
|
case ReleaseBatchDraft, ReleaseBatchSealed, ReleaseBatchCancelled:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
type ReleaseBatch struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
State ReleaseBatchState `json:"state"`
|
|
PolicyVersion string `json:"policy_version"`
|
|
CreatedBy string `json:"created_by"`
|
|
SealedBy string `json:"sealed_by"`
|
|
ItemCount int `json:"item_count"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
SealedAt *time.Time `json:"sealed_at"`
|
|
ReleaseChangeSetID string `json:"release_change_set_id,omitempty"`
|
|
}
|
|
|
|
type CreateReleaseBatchRequest struct {
|
|
Name string
|
|
CreatedBy string
|
|
PublicationIDs []string
|
|
DeletePublicationIDs []string
|
|
// ReleaseChangeSetID (R1): a formal batch must cite a CONFIRMED persistent
|
|
// release change set. Empty means "freeze the live plan at bind time" (P0-1).
|
|
ReleaseChangeSetID string
|
|
// SealImmediately (P0-3): 建批后立即锁定。清单在冻结时已不可变,草稿态只剩
|
|
// "查看/锁定"两个按钮,等于让操作员多按一次;锁定仍会逐项复核门禁。
|
|
SealImmediately bool
|
|
Now time.Time
|
|
}
|
|
|
|
type SealReleaseBatchRequest struct {
|
|
BatchID string
|
|
SealedBy string
|
|
Now time.Time
|
|
}
|
|
|
|
type ReleaseBatchFilter struct {
|
|
State string
|
|
Limit int
|
|
Offset int
|
|
}
|
|
|
|
type ReleaseBatchPage struct {
|
|
Items []ReleaseBatch `json:"items"`
|
|
Total int `json:"total"`
|
|
Limit int `json:"limit"`
|
|
Offset int `json:"offset"`
|
|
}
|
|
|
|
type ReleaseBatchItemView struct {
|
|
PublicationID string `json:"publication_id"`
|
|
Title string `json:"title"`
|
|
DecidedVisibility Visibility `json:"decided_visibility"`
|
|
Action string `json:"action"`
|
|
}
|
|
|
|
type ReleaseBatchDetail struct {
|
|
ReleaseBatch
|
|
Items []ReleaseBatchItemView `json:"items"`
|
|
}
|