Files
smartmate/backend/active_scheduler/apply/hash.go
LoveLosita e945578fbf Version: 0.9.59.dev.260430
后端:
1. 主动调度预览确认主链路落地——新增主动调度数据模型、DAO 与事件契约;接入 dry-run pipeline 与任务触发的 job upsert/cancel;新增 preview 查询与 confirm API,支持 apply_id 幂等确认并同步写入 task_pool 日程
2. 同步更新主动调度实施文档的阶段状态与验收记录

前端:
3. AssistantPanel 脚本层继续解耦——私有类型迁移到独立类型文件,并抽离会话、工具轨迹、思考摘要、任务表单等纯函数辅助逻辑;保持助手面板模板与样式不变,降低表现层回归风险
2026-04-30 12:05:15 +08:00

100 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package apply
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"strings"
)
type RequestHash struct {
BodyHash string
ApplyHash string
ApplyID string
}
// BuildConfirmRequestHash 计算 confirm 请求的幂等摘要。
//
// 职责边界:
// 1. body_hash 只覆盖一次确认动作中真正影响 apply 的 body 字段;
// 2. apply_hash 按 preview_id + idempotency_key + body_hash 计算,满足同 key 不同内容可识别;
// 3. 不查询历史记录,是否冲突由 DetectIdempotencyConflict 或接入层唯一约束判断。
func BuildConfirmRequestHash(previewID string, req ConfirmRequest) (RequestHash, error) {
previewID = strings.TrimSpace(firstNonEmpty(req.PreviewID, previewID))
idempotencyKey := strings.TrimSpace(req.IdempotencyKey)
if previewID == "" {
return RequestHash{}, newApplyError(ErrorCodeInvalidRequest, "preview_id 不能为空", nil)
}
if idempotencyKey == "" {
return RequestHash{}, newApplyError(ErrorCodeInvalidRequest, "idempotency_key 不能为空", nil)
}
bodyHash, err := hashJSON(confirmRequestBodyForHash{
CandidateID: strings.TrimSpace(req.CandidateID),
Action: normalizeConfirmAction(req.Action),
EditedChanges: req.EditedChanges,
IdempotencyKey: idempotencyKey,
})
if err != nil {
return RequestHash{}, err
}
applyHash := sha256Text(previewID + "\n" + idempotencyKey + "\n" + bodyHash)
applyID := "asap_" + applyHash[:24]
return RequestHash{
BodyHash: bodyHash,
ApplyHash: applyHash,
ApplyID: applyID,
}, nil
}
// BuildNormalizedChangesHash 计算转换后 changes 的稳定摘要。
//
// 职责边界:
// 1. 只用于审计和幂等辅助,不替代正式 DB 重校验;
// 2. 输入应是 NormalizeChanges 后的结果,避免相同语义因顺序不同得到不同摘要;
// 3. 序列化失败会返回 invalid_request调用方应拒绝本次 confirm。
func BuildNormalizedChangesHash(changes []ApplyChange) (string, error) {
return hashJSON(changes)
}
type confirmRequestBodyForHash struct {
CandidateID string `json:"candidate_id"`
Action ConfirmAction `json:"action"`
EditedChanges []ApplyChange `json:"edited_changes,omitempty"`
IdempotencyKey string `json:"idempotency_key"`
}
func hashJSON(value any) (string, error) {
raw, err := json.Marshal(value)
if err != nil {
return "", newApplyError(ErrorCodeInvalidRequest, "请求体无法生成稳定摘要", err)
}
return sha256Bytes(raw), nil
}
func sha256Text(text string) string {
return sha256Bytes([]byte(text))
}
func sha256Bytes(raw []byte) string {
sum := sha256.Sum256(raw)
return hex.EncodeToString(sum[:])
}
func normalizeConfirmAction(action ConfirmAction) ConfirmAction {
if action == "" {
return ConfirmActionConfirm
}
return action
}
func firstNonEmpty(values ...string) string {
for _, value := range values {
if strings.TrimSpace(value) != "" {
return value
}
}
return ""
}