From 5fecd05d46a1bc552fd145524dc7448bb6d9cc5e Mon Sep 17 00:00:00 2001 From: xuans Date: Wed, 30 Sep 2026 02:40:41 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E9=BD=90=E8=AF=81=E4=B9=A6=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=88=9B=E5=BB=BA=E4=BA=BA=E5=BD=92=E5=B1=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 传递已认证操作者上下文,关联证书任务审计,并同步四份整改清单进度。 --- backend/internal/app/certificate_tasks.go | 9 +++-- .../internal/app/certificate_tasks_test.go | 34 +++++++++++++++++++ backend/internal/httpapi/task_api.go | 3 +- backend/internal/repository/task_context.go | 17 +++++++++- doc/Scert开发任务清单.md | 11 ++++++ doc/Scert整改任务执行清单.md | 11 ++++++ doc/Scert整改合并优化清单.md | 10 ++++++ doc/基础整改任务规格.md | 10 ++++++ 8 files changed, 100 insertions(+), 5 deletions(-) diff --git a/backend/internal/app/certificate_tasks.go b/backend/internal/app/certificate_tasks.go index eca5e69..e779497 100644 --- a/backend/internal/app/certificate_tasks.go +++ b/backend/internal/app/certificate_tasks.go @@ -65,7 +65,8 @@ func (w *certificateTaskWorkflow) run(ctx context.Context, id, taskType, stage s task := repository.Task{ ID: domain.NewID(), DomainID: id, TaskType: taskType, Status: "running", Stage: stage, Message: "正在执行证书工作流", - StartedAt: &started, CreatedAt: started, + OperatorID: repository.TaskOperatorFromContext(ctx), + StartedAt: &started, CreatedAt: started, } if err := w.tasks.CreateTask(ctx, task); err != nil { return result, err @@ -84,7 +85,8 @@ func (w *certificateTaskWorkflow) run(ctx context.Context, id, taskType, stage s auditErr := w.audit.CreateAuditLog(finishCtx, repository.AuditLog{ TaskID: task.ID, Action: taskType, ResourceType: "domain", ResourceID: id, - Status: status, Message: message, + OperatorID: task.OperatorID, + Status: status, Message: message, }) if auditErr != nil { status, message = certificateTaskOutcome(result, auditErr) @@ -99,7 +101,8 @@ func (w *certificateTaskWorkflow) run(ctx context.Context, id, taskType, stage s if err := w.audit.CreateAuditLog(taskCtx, repository.AuditLog{ TaskID: task.ID, Action: taskType, ResourceType: "domain", ResourceID: id, - Status: "running", Message: "开始执行证书工作流", + OperatorID: task.OperatorID, + Status: "running", Message: "开始执行证书工作流", }); err != nil { return result, err } diff --git a/backend/internal/app/certificate_tasks_test.go b/backend/internal/app/certificate_tasks_test.go index a46bcf3..8aa66bf 100644 --- a/backend/internal/app/certificate_tasks_test.go +++ b/backend/internal/app/certificate_tasks_test.go @@ -92,6 +92,40 @@ func TestCertificateTasksRecordSuccessFailureAndCancellation(t *testing.T) { } } +func TestCertificateTasksRecordOperatorFromWorkflowContext(t *testing.T) { + w, a := newCertificateTaskFixture(t) + ctx := repository.WithTaskOperator(t.Context(), "operator-1") + _, err := w.run(ctx, "task-domain", "certificate_validate", "certificate_validate", 1, 0, func(context.Context, string) (CertificateWorkflowResult, error) { + return CertificateWorkflowResult{}, nil + }) + if err != nil { + t.Fatalf("带操作者的证书任务执行失败:%v", err) + } + items, err := a.Tasks.ListTasks(t.Context(), "task-domain", 10) + if err != nil || len(items) != 1 || items[0].OperatorID != "operator-1" { + t.Fatalf("任务创建人记录错误:%+v %v", items, err) + } + var operators []string + rows, err := a.Database.DB.QueryContext(t.Context(), "SELECT operator_id FROM audit_logs WHERE task_id = ? ORDER BY created_at, id", items[0].ID) + if err != nil { + t.Fatalf("读取任务审计失败:%v", err) + } + defer rows.Close() + for rows.Next() { + var operator string + if err := rows.Scan(&operator); err != nil { + t.Fatalf("读取任务审计操作者失败:%v", err) + } + operators = append(operators, operator) + } + if err := rows.Err(); err != nil { + t.Fatalf("遍历任务审计失败:%v", err) + } + if len(operators) != 2 || operators[0] != "operator-1" || operators[1] != "operator-1" { + t.Fatalf("任务审计创建人记录错误:%v", operators) + } +} + func TestCertificateTasksKeepRetriesInOneTask(t *testing.T) { w, a := newCertificateTaskFixture(t) calls := 0 diff --git a/backend/internal/httpapi/task_api.go b/backend/internal/httpapi/task_api.go index 8d89bde..9f07632 100644 --- a/backend/internal/httpapi/task_api.go +++ b/backend/internal/httpapi/task_api.go @@ -242,10 +242,11 @@ func (s *Server) unregisterTaskCancel(taskID string) { } func (s *Server) taskWorkflowContext(c *gin.Context) context.Context { - return repository.WithTaskCancellationHooks(c.Request.Context(), repository.TaskCancellationHooks{ + ctx := repository.WithTaskCancellationHooks(c.Request.Context(), repository.TaskCancellationHooks{ Register: s.registerTaskCancel, Unregister: s.unregisterTaskCancel, }) + return repository.WithTaskOperator(ctx, s.currentOperatorID(c)) } func taskRepositoryUnavailable(tasks repository.TaskRepository) bool { diff --git a/backend/internal/repository/task_context.go b/backend/internal/repository/task_context.go index b824146..66615bf 100644 --- a/backend/internal/repository/task_context.go +++ b/backend/internal/repository/task_context.go @@ -1,6 +1,9 @@ package repository -import "context" +import ( + "context" + "strings" +) type TaskCancellationHooks struct { Register func(string, context.CancelFunc) @@ -8,6 +11,7 @@ type TaskCancellationHooks struct { } type taskCancellationHooksContextKey struct{} +type taskOperatorContextKey struct{} func WithTaskCancellationHooks(ctx context.Context, hooks TaskCancellationHooks) context.Context { return context.WithValue(ctx, taskCancellationHooksContextKey{}, hooks) @@ -20,3 +24,14 @@ func TaskCancellationHooksFromContext(ctx context.Context) (TaskCancellationHook } return hooks, true } + +// WithTaskOperator 将当前已认证操作者绑定到任务工作流上下文。 +func WithTaskOperator(ctx context.Context, operatorID string) context.Context { + return context.WithValue(ctx, taskOperatorContextKey{}, strings.TrimSpace(operatorID)) +} + +// TaskOperatorFromContext 读取任务工作流的创建人;后台调度任务没有操作者时返回空字符串。 +func TaskOperatorFromContext(ctx context.Context) string { + operatorID, _ := ctx.Value(taskOperatorContextKey{}).(string) + return strings.TrimSpace(operatorID) +} diff --git a/doc/Scert开发任务清单.md b/doc/Scert开发任务清单.md index 9df0d7f..bcfe16a 100644 --- a/doc/Scert开发任务清单.md +++ b/doc/Scert开发任务清单.md @@ -2,6 +2,17 @@ ## 最新执行摘要(2026-09-29 继续开发) +总体验收位置继续保持 **P0-03 完成,待复审**;本轮继续先开发、后统一测试,不进入远端生产验收。 + +- [x] 已为证书任务工作流增加操作者上下文;HTTP 发起的证书部署、导入、恢复、校验任务会记录当前创建人。 +- [x] 已将证书任务开始与完成审计关联到同一创建人;后台续期调度没有登录操作者时保持为空,避免伪造身份。 +- [x] 已新增创建人及审计归属回归测试;`go test ./internal/app ./internal/httpapi ./internal/repository` 和 `git diff --check` 已通过。 +- [~] P0-10 其余任务的崩溃恢复、DNS 验证任务、批量部署结果及跨入口归属仍需继续审查。 +- [ ] 三数据库矩阵、浏览器清缓存、密码 SSH、远端部署和生产验收仍未执行。 +- [!] 不升级总体验收位置;当前仍为 **P0-03 完成,待复审**,本轮没有新增 `[✓]` 复审通过项。 + +## 最新执行摘要(2026-09-29 继续开发) + 本轮继续保持总体验收位置为 **P0-03 完成,待复审**,没有新增复审通过项;当前仍按“先开发、后统一测试”执行。 - [x] 已提交证书导入、恢复、校验任务入口接入代码:`b899751`;远程推送因仓库认证失败未成功,未阻塞本地继续开发。 diff --git a/doc/Scert整改任务执行清单.md b/doc/Scert整改任务执行清单.md index 5375b4a..976fede 100644 --- a/doc/Scert整改任务执行清单.md +++ b/doc/Scert整改任务执行清单.md @@ -2,6 +2,17 @@ ## 最新任务批次(2026-09-29 继续开发) +总体验收仍保持 **P0-03 完成,待复审**;本批只补齐 P0-10 任务创建人和审计归属,不代表生产部署验收通过。 + +- [x] HTTP 证书工作流上下文已传递当前已认证操作者;证书部署、导入、恢复、校验任务不再默认丢失创建人。 +- [x] 任务开始、完成审计已写入任务创建人;后台续期无认证操作者时保持空值,不使用请求身份以外的信息填充。 +- [x] 创建人归属回归测试已通过;专项测试为 `go test ./internal/app ./internal/httpapi ./internal/repository`,并通过 `git diff --check`。 +- [~] P0-10 仍需继续审查 DNS 验证任务、批量部署 `partial_success`、崩溃恢复、重试和其他同步长耗时入口。 +- [ ] P0-12、三数据库实库矩阵、浏览器、密码 SSH、远端部署和生产验收尚未执行。 +- [!] 总体验收位置不变:**P0-03 完成,待复审**。 + +## 最新任务批次(2026-09-29 继续开发) + 总体验收仍保持 **P0-03 完成,待复审**;本批代码已完成,统一测试已完成本地自动化部分。 - [x] 证书导入、恢复、校验入口已接入应用任务工作流,提交 `b899751`。 diff --git a/doc/Scert整改合并优化清单.md b/doc/Scert整改合并优化清单.md index 066b22a..befc581 100644 --- a/doc/Scert整改合并优化清单.md +++ b/doc/Scert整改合并优化清单.md @@ -2,6 +2,16 @@ ## 最新进度摘要(2026-09-29 继续开发) +**总体验收位置仍为 P0-03 完成,待复审。** 本轮只推进 P0-10 任务归属链,不把局部代码或专项测试结果视为整项复审通过。 + +- [x] 任务工作流从认证请求上下文读取创建人,并写入证书任务的 `operator_id`。 +- [x] 任务开始和完成审计使用任务自身的创建人;后台调度任务继续保留空操作者边界。 +- [x] 创建人和审计归属专项测试通过;尚未执行三数据库、浏览器、密码 SSH、远端部署和生产门禁。 +- [~] P0-10 其余任务链、DNS 验证、批量部署、崩溃恢复、重试幂等和 P0-12 仍未完成。 +- [!] 不新增 `[✓]` 复审通过项,总体验收继续停在 **P0-03 完成,待复审**。 + +## 最新进度摘要(2026-09-29 继续开发) + **总体验收位置仍为 P0-03 完成,待复审。** 本轮只推进 P0-10 任务工作流,不把局部代码完成或专项测试通过视为生产验收通过。 - [x] 证书导入、恢复、校验任务入口接入应用层,提交 `b899751`。 diff --git a/doc/基础整改任务规格.md b/doc/基础整改任务规格.md index fbcb907..41bf378 100644 --- a/doc/基础整改任务规格.md +++ b/doc/基础整改任务规格.md @@ -2,6 +2,16 @@ ## 最新执行与补充约束(2026-09-29 继续开发) +总体验收位置继续保持 **P0-03 完成,待复审**。本轮补充 P0-10 任务归属不变量,不改变原始 44 项范围,也不提前进入生产验收。 + +- [x] 证书工作流通过上下文记录已认证请求的任务创建人,后台调度任务不得伪造登录操作者。 +- [x] 证书任务开始、完成审计必须与任务的 `operator_id` 一致;任务资源范围仍由域名项目归属服务端确定。 +- [x] 创建人归属专项测试通过;完整后端、三数据库实库、浏览器、密码 SSH、远端部署和生产门禁仍未完成。 +- [~] DNS 验证任务、批量部署结果、崩溃恢复、重试和其他长耗时入口仍需继续补齐。 +- [!] 本轮不得将总体验收升级为复审通过,当前仍为 **P0-03 完成,待复审**。 + +## 最新执行与补充约束(2026-09-29 继续开发) + 总体验收位置继续保持 **P0-03 完成,待复审**。本轮开发不改变原始 44 项范围,也不提前进入生产验收。 - [x] 证书导入、恢复、校验已接入统一任务工作流,提交 `b899751`。