docs: add BuildFlow API optimization design
This commit is contained in:
parent
51eea796e2
commit
e549d3e3fb
@ -0,0 +1,416 @@
|
|||||||
|
# BuildFlow API 全面优化设计
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
本设计用于系统性修复当前 BuildFlow API 已确认的安全、可靠性、性能和架构规范问题,并建立可重复执行的深度测试与闭环 SOP。
|
||||||
|
|
||||||
|
完成标准不是“代码能够编译”,而是同时满足以下条件:
|
||||||
|
|
||||||
|
- 已确认的高风险安全问题全部关闭。
|
||||||
|
- 工作流 Timer 和异步导入在多实例、进程重启和异常重试场景下保持可靠、幂等。
|
||||||
|
- 已确认的 N+1、无界上下文、整表内存加载和串行远程调用被消除或受到明确上限约束。
|
||||||
|
- HTTP 层、模块边界、异常错误码和文件组织符合 `rag-coding-api`。
|
||||||
|
- EF Migration 可在空库和存量库上执行,并验证升级前后数据不丢失。
|
||||||
|
- 单元测试、集成测试、并发测试、性能门禁和 HTTP/SSE 冒烟测试全部通过。
|
||||||
|
- 最终提供一个可重复运行、失败可定位、结果可归档的后端优化 SOP。
|
||||||
|
|
||||||
|
## 2. 范围与兼容性
|
||||||
|
|
||||||
|
### 2.1 范围
|
||||||
|
|
||||||
|
本次覆盖:
|
||||||
|
|
||||||
|
- `BuildFlow.Host`
|
||||||
|
- `modules/platform`
|
||||||
|
- `modules/rag`
|
||||||
|
- `modules/file`
|
||||||
|
- `modules/monitoring`
|
||||||
|
- `modules/workflow`
|
||||||
|
- `shared/BuildFlow.Shared`
|
||||||
|
- 对应测试项目、EF Migration 和 SOP 脚本
|
||||||
|
|
||||||
|
### 2.2 兼容性约束
|
||||||
|
|
||||||
|
- 保持现有 HTTP 路由不变。
|
||||||
|
- 保持现有成功响应信封和 SSE 事件协议兼容。
|
||||||
|
- 权限收紧属于预期行为变化:越权、匿名 Obsidian 同步和缺少部署密钥的管理员初始化将被拒绝。
|
||||||
|
- 数据库允许新增表、字段和索引,但不得通过删除重建现有表实现迁移。
|
||||||
|
- 保留当前工作树中的用户改动;每批修改只触碰该批明确列出的文件。
|
||||||
|
- 不引入跨模块 service/interface bridge;跨模块协作只能使用目标模块 Application 拥有的 MediatR Command/Query。
|
||||||
|
|
||||||
|
## 3. 实施策略
|
||||||
|
|
||||||
|
采用“风险优先的纵向批次”,而不是按模块逐个重构或一次性大改。
|
||||||
|
|
||||||
|
本文是总设计和顺序约束,不直接展开为一个超大实施计划。五个批次分别生成独立的 TDD 实施计划;每个批次完成并通过验收后,才为下一批编写计划。这样可以让每批保持可审查、可验证和可回滚。
|
||||||
|
|
||||||
|
每个批次必须形成独立闭环:
|
||||||
|
|
||||||
|
1. 写失败测试并确认失败原因正确。
|
||||||
|
2. 实现最小修复。
|
||||||
|
3. 运行该批测试和受影响模块测试。
|
||||||
|
4. 运行架构测试和解决方案构建。
|
||||||
|
5. 记录契约、Migration、性能和未验证项。
|
||||||
|
6. 通过门禁后再进入下一批。
|
||||||
|
|
||||||
|
批次顺序固定为:
|
||||||
|
|
||||||
|
1. 安全与对象权限。
|
||||||
|
2. 持久化任务和多实例可靠性。
|
||||||
|
3. 数据库查询与批处理性能。
|
||||||
|
4. 聊天、文件和 Excel I/O 性能。
|
||||||
|
5. 架构规范收口与完整 SOP。
|
||||||
|
|
||||||
|
### 3.1 基线修复门禁
|
||||||
|
|
||||||
|
当前工作树构建通过,但 RAG 测试存在两个已知失败:测试容器注册 `IOptions<AiOptions>`,而生产解析器已经依赖 `IOptionsMonitor<AiOptions>`。开始第一批生产代码修改前,先修复这两个测试的依赖注册并运行现有全解决方案测试,建立可信的绿色基线。
|
||||||
|
|
||||||
|
基线修复只允许调整测试装配,不改变生产行为;若全量测试发现其他失败,先记录并判断是否由当前工作树已有改动造成,不能把红色基线带入后续批次。
|
||||||
|
|
||||||
|
## 4. 第一批:安全与对象权限
|
||||||
|
|
||||||
|
### 4.1 管理员初始化
|
||||||
|
|
||||||
|
现状会在空库中创建固定密码 `admin123` 的最高权限管理员。
|
||||||
|
|
||||||
|
目标设计:
|
||||||
|
|
||||||
|
- 新增 `Platform:BootstrapAdmin` 配置节,包含 `Enabled`、`Username`、`Email` 和 `Password`。
|
||||||
|
- 默认 `Enabled=false`,生产环境不会隐式创建管理员。
|
||||||
|
- 启用初始化时,密码必须来自 IConfiguration,即环境变量、Secrets 或 Consul;仓库配置文件不得提供真实默认密码。
|
||||||
|
- 密码必须通过与注册接口相同的强度规则。
|
||||||
|
- 数据库已有管理员时不重置密码。
|
||||||
|
- 日志只能记录“已创建/已跳过”,不得记录账号密码。
|
||||||
|
|
||||||
|
验收证据:
|
||||||
|
|
||||||
|
- 未启用时空库不会创建固定管理员。
|
||||||
|
- 启用但密码缺失或弱密码时启动种子步骤明确失败并记录原因。
|
||||||
|
- 合法部署密钥只在首次初始化创建管理员,后续启动不修改密码。
|
||||||
|
- 生产源码和配置扫描不存在固定管理员默认口令;测试样例和安全设计文档不计入该扫描。
|
||||||
|
|
||||||
|
### 4.2 Obsidian 同步边界
|
||||||
|
|
||||||
|
目标设计:
|
||||||
|
|
||||||
|
- Endpoint 从 `AllowAnonymous()` 改为现有知识库写权限。
|
||||||
|
- Handler 注入 `ICurrentUserContext`,验证知识库属于当前用户;知识库以 `CreatedBy` 作为现有所有权依据。
|
||||||
|
- 新增 `Rag:Obsidian` 配置:`AllowedRoots`、`MaxFiles`、`MaxFileBytes`、`MaxTotalBytes`。
|
||||||
|
- 请求路径规范化后必须位于某个允许根目录中;符号链接解析后不得逃逸根目录。
|
||||||
|
- 枚举过程流式执行,不先对整个目录 `ToList()`。
|
||||||
|
- 在读取前检查文件数、单文件大小和累计大小,超限抛 `ErrorCode.TooLarge`。
|
||||||
|
- 单文件读取使用流式哈希,不为计算 SHA256 分配完整 `byte[]`。
|
||||||
|
- 目录权限、文件消失和取消操作必须产生可诊断日志,不泄露文件内容。
|
||||||
|
|
||||||
|
验收证据:
|
||||||
|
|
||||||
|
- 未认证、无权限、非所有者、根目录逃逸、符号链接逃逸、数量超限和大小超限均被拒绝。
|
||||||
|
- 合法目录可增量同步,未变文件不会重复写入。
|
||||||
|
- 大目录测试证明内存不随文件总大小线性增长。
|
||||||
|
|
||||||
|
### 4.3 RAG 会话所有权
|
||||||
|
|
||||||
|
以下操作统一执行对象级所有权校验:详情、更新、删除、普通发送、流式发送、编辑后重新生成。
|
||||||
|
|
||||||
|
目标设计:
|
||||||
|
|
||||||
|
- Application handler 注入 `ICurrentUserContext`。
|
||||||
|
- 查询条件直接包含 `Conversation.Id` 和 `Conversation.UserId`,避免先查再判断产生信息泄露。
|
||||||
|
- 非所有者统一抛 `NotFoundException(ErrorCode.NotFound, "会话不存在")`,不暴露资源是否存在。
|
||||||
|
- Endpoint 不承载所有权逻辑。
|
||||||
|
- 相关缓存 key 仍以会话 ID 标识,但只有通过所有权校验后才能访问。
|
||||||
|
|
||||||
|
验收证据:
|
||||||
|
|
||||||
|
- 用户 A 无法读取、更新、删除或向用户 B 的会话发送消息。
|
||||||
|
- 用户 A 的所有现有会话操作保持兼容。
|
||||||
|
- 流式和非流式路径使用同一所有权规则。
|
||||||
|
|
||||||
|
### 4.4 配置日志
|
||||||
|
|
||||||
|
- Consul Provider 不再输出任何配置值或其截断片段。
|
||||||
|
- 日志只包含 key 数量、版本索引和刷新结果。
|
||||||
|
- 增加静态测试,禁止配置 Provider 出现值输出模板。
|
||||||
|
|
||||||
|
## 5. 第二批:持久化任务和多实例可靠性
|
||||||
|
|
||||||
|
### 5.1 工作流 Timer 数据模型
|
||||||
|
|
||||||
|
新增 `WorkflowTimerJob`,映射到 `b_workflow_timer_jobs`。核心字段:
|
||||||
|
|
||||||
|
- `Id`
|
||||||
|
- `InstanceId`
|
||||||
|
- `TokenId`
|
||||||
|
- `NodeId`
|
||||||
|
- `DueAt`
|
||||||
|
- `Status`
|
||||||
|
- `LeaseOwner`
|
||||||
|
- `LeaseExpiresAt`
|
||||||
|
- `AttemptCount`
|
||||||
|
- `LastError`
|
||||||
|
- `CompletedAt`
|
||||||
|
- 标准审计字段
|
||||||
|
|
||||||
|
约束与索引:
|
||||||
|
|
||||||
|
- `TokenId` 唯一,确保一个 token 最多一个 Timer Job。
|
||||||
|
- `(Status, DueAt)` 组合索引支撑到期扫描。
|
||||||
|
- `(Status, LeaseExpiresAt)` 支撑过期租约恢复。
|
||||||
|
|
||||||
|
状态限定为 `Pending`、`Processing`、`Completed`、`Failed`、`Cancelled`。
|
||||||
|
|
||||||
|
### 5.2 Timer 执行链
|
||||||
|
|
||||||
|
- `ProcessEngine` 遇到未来 Timer 时只持久化 Timer Job,不再创建 `Task.Run`。
|
||||||
|
- 新增 Host `WorkflowTimerSchedulerService`,只负责周期触发 Application processor。
|
||||||
|
- Application processor 使用 PostgreSQL `FOR UPDATE SKIP LOCKED` 或等价原子语句批量抢占到期任务。
|
||||||
|
- 抢占时写入实例唯一 `LeaseOwner` 和 `LeaseExpiresAt`。
|
||||||
|
- 处理前重新验证实例、token 和 Timer Job 状态。
|
||||||
|
- 消费 token、创建下游 token、更新 Timer Job 必须处于同一数据库事务。
|
||||||
|
- 进程中断后,租约过期的 `Processing` 任务重新进入可领取状态。
|
||||||
|
- 达到最大重试次数后标记 `Failed` 并保留 `LastError`,不得无限热重试。
|
||||||
|
- 实例终止或 token 非 Active 时标记 `Cancelled`,不得继续投递。
|
||||||
|
|
||||||
|
### 5.3 导入任务原子抢占
|
||||||
|
|
||||||
|
在现有导入任务增加:
|
||||||
|
|
||||||
|
- `LeaseOwner`
|
||||||
|
- `LeaseExpiresAt`
|
||||||
|
- `AttemptCount`
|
||||||
|
- `LastHeartbeatAt`
|
||||||
|
|
||||||
|
并增加 `(Status, CreatedAt)`、`(Status, LeaseExpiresAt)` 索引。
|
||||||
|
|
||||||
|
后台服务不再先查询 ID 再普通更新,而是调用 Application 层原子领取命令。领取规则:
|
||||||
|
|
||||||
|
- 只领取 `Pending` 或租约过期的 `Running` 任务。
|
||||||
|
- 一次最多领取配置数量。
|
||||||
|
- 多实例不得领取同一任务。
|
||||||
|
- 处理过程中按批次更新心跳。
|
||||||
|
- 完成、失败和取消都清理租约字段。
|
||||||
|
|
||||||
|
验收证据:
|
||||||
|
|
||||||
|
- 两个并发 worker 只能有一个获得同一 Timer 或导入任务。
|
||||||
|
- worker 在领取后崩溃,租约过期后另一 worker 能恢复任务。
|
||||||
|
- 相同 token 不会创建两个下游 token。
|
||||||
|
- 导入恢复不会重复插入已经确认完成的批次。
|
||||||
|
|
||||||
|
## 6. 第三批:数据库查询与批处理性能
|
||||||
|
|
||||||
|
### 6.1 表单统计
|
||||||
|
|
||||||
|
把当前“目标行 × 统计字段”的查询模式改为一次构建计算计划:
|
||||||
|
|
||||||
|
- Schema 只解析一次。
|
||||||
|
- 按来源表单编码分组统计字段。
|
||||||
|
- 一次批量加载来源表单定义。
|
||||||
|
- 一次批量加载当前实例需要的来源 FormData。
|
||||||
|
- 在内存中按来源表单和关系值建立索引。
|
||||||
|
- 计算全部目标行后统一更新 `WorkflowInstance.Variables` 一次。
|
||||||
|
- 单次刷新只执行一次 `SaveChangesAsync`。
|
||||||
|
|
||||||
|
查询次数门禁不依赖运行时长,而使用 EF CommandInterceptor 计数。固定输入规模增大时,SQL 次数必须保持常数级,不得随目标行数和统计字段数线性增长。
|
||||||
|
|
||||||
|
### 6.2 导入批处理
|
||||||
|
|
||||||
|
- Excel 解析结果以流或可迭代行提供,不一次性保存全部行对象。
|
||||||
|
- 每批默认 200 行,可配置但有上限。
|
||||||
|
- 每行继续执行派生和 Schema 校验,但表单定义与 Schema 解析结果在任务内复用。
|
||||||
|
- 每批执行一次数据库提交和一次任务进度/心跳更新。
|
||||||
|
- 聚合字段在批次写入后统一刷新,不为每行单独刷新两次。
|
||||||
|
- 移除手动 `new SubmitFormDataCommandHandler(...)`,由模块内明确的批量用例承载导入逻辑。
|
||||||
|
- 行 InstanceId 使用完整 `jobId + rowIndex` 确定性哈希,不再只覆盖 Guid 的两个字节。
|
||||||
|
- 每个导入任务记录已提交行游标,使租约恢复从下一未完成批次继续。
|
||||||
|
|
||||||
|
### 6.3 Monitoring UPSERT
|
||||||
|
|
||||||
|
- `SyncedRecord` 以现有 `(ExternalSystemId, ExternalId, RecordType)` 唯一键进行 PostgreSQL `ON CONFLICT DO UPDATE`。
|
||||||
|
- 每批最多 500 条,参数化执行,不拼接外部输入。
|
||||||
|
- 同步任务、记录写入和系统最近同步信息保持清晰事务边界。
|
||||||
|
- JSON payload 解析失败仍保留 `_raw`,但记录结构化告警。
|
||||||
|
|
||||||
|
验收证据:
|
||||||
|
|
||||||
|
- 同步 N 条记录不再产生 N 次查询。
|
||||||
|
- 并发同步相同外部记录不产生重复行或唯一键异常。
|
||||||
|
- 更新记录保留最新 `SyncJobId`、`OccurredAt` 和 payload。
|
||||||
|
|
||||||
|
## 7. 第四批:聊天、文件和 Excel I/O 性能
|
||||||
|
|
||||||
|
### 7.1 聊天上下文窗口
|
||||||
|
|
||||||
|
新增 `Rag:Chat` 配置:
|
||||||
|
|
||||||
|
- `MaxContextMessages`
|
||||||
|
- `MaxContextTokens`
|
||||||
|
- `CacheTtlMinutes`
|
||||||
|
|
||||||
|
生成路径按 `ConversationId + CreatedAt` 倒序只读取有限消息,再恢复时间顺序。`ChatContextWindowBuilder` 从最新消息向前累加估算 token,达到上限后停止;完整历史仍保留在数据库。
|
||||||
|
|
||||||
|
新增 `(ConversationId, CreatedAt)` 组合索引。普通发送、流式发送和编辑后重新生成必须复用同一个上下文构建器,避免三套行为漂移。
|
||||||
|
|
||||||
|
### 7.2 Redis 消息缓存
|
||||||
|
|
||||||
|
- key 固定为 `chat:messages:{conversationId}`。
|
||||||
|
- 存储结构从单个 JSON 字符串改为 Redis List。
|
||||||
|
- 追加使用原子 `RPUSH`,并通过事务或 Lua 同时刷新 TTL。
|
||||||
|
- 读取详情使用 `LRANGE`,缓存 miss 才回源数据库并重建 List。
|
||||||
|
- 删除会话继续删除整个 key。
|
||||||
|
- 不再使用非原子“读完整列表、追加、写完整列表”。
|
||||||
|
|
||||||
|
验收证据:
|
||||||
|
|
||||||
|
- 多个并发追加不会丢消息。
|
||||||
|
- 单次追加传输量不随历史长度增长。
|
||||||
|
- 缓存失效后可从数据库完整重建。
|
||||||
|
|
||||||
|
### 7.3 文件大小回填
|
||||||
|
|
||||||
|
- 新增 `SizeVerified` 字段;新上传文件在落库时直接标记已验证。
|
||||||
|
- 历史记录迁移规则:`Size > 0` 标记已验证,`Size == 0` 保持待验证。
|
||||||
|
- 文件夹树和文件列表不再调用 S3 HeadObject,也不在 GET 请求中写数据库。
|
||||||
|
- 新增后台修复 processor,每轮领取有限数量未验证文件,并以配置的有限并发查询 S3。
|
||||||
|
- 对象大小为 0 仍标记已验证;对象不存在记录警告并标记已验证,避免永久重试。
|
||||||
|
- 临时网络失败保持未验证,下一轮重试。
|
||||||
|
|
||||||
|
### 7.4 Excel 边界
|
||||||
|
|
||||||
|
- 上传入口在读取前限制文件字节数。
|
||||||
|
- 导入限制最大数据行数和最大单元格文本长度。
|
||||||
|
- 导入使用流式 OpenXML reader,避免 `ReadAllBytesAsync` 和整表对象常驻。
|
||||||
|
- 导出使用 OpenXmlWriter 顺序写行,并从数据库按稳定游标分页读取。
|
||||||
|
- HTTP 下载保持现有文件响应契约;生成过程使用临时文件或可回收流,避免同时保留完整实体集合和最终字节数组。
|
||||||
|
- 临时文件在成功、失败和取消路径都必须清理。
|
||||||
|
|
||||||
|
验收证据:
|
||||||
|
|
||||||
|
- 超限文件在进入深度解析前被拒绝并返回 `ErrorCode.TooLarge`。
|
||||||
|
- 大数据导入/导出测试记录峰值内存,数据规模扩大时不再近似多份完整文件线性叠加。
|
||||||
|
- 取消导出后无残留临时文件。
|
||||||
|
|
||||||
|
## 8. 第五批:架构规范收口
|
||||||
|
|
||||||
|
### 8.1 Endpoint CQRS
|
||||||
|
|
||||||
|
以下 Endpoint 迁移为纯 HTTP 适配器:
|
||||||
|
|
||||||
|
- `ChangePasswordEndpoint`
|
||||||
|
- `RAGStreamEndpoint`
|
||||||
|
- `StreamMessageEndpoint`
|
||||||
|
- `EditMessageStreamEndpoint`
|
||||||
|
|
||||||
|
非流式操作使用普通 MediatR Command/Query。流式操作使用 MediatR `IStreamRequest<TEvent>`;Application handler 负责业务、持久化和产生领域流事件,Endpoint 只负责把事件编码为现有 SSE 协议。
|
||||||
|
|
||||||
|
### 8.2 移除跨模块 Bridge
|
||||||
|
|
||||||
|
移除以下 Host 注册和实现:
|
||||||
|
|
||||||
|
- `InProcessUserInfoService`
|
||||||
|
- `InProcessFileStorageClient`
|
||||||
|
- `InProcessAvatarUrlResolver`
|
||||||
|
|
||||||
|
替代方式:
|
||||||
|
|
||||||
|
- IM 需要用户信息时直接发送 Platform Application 的批量用户 Query。
|
||||||
|
- RAG 需要上传/下载文件时直接发送 File Application 的 Command/Query。
|
||||||
|
- Platform 需要头像访问地址时发送 File Application 的预览路径 Query,或只返回 object key 由已有文件 Endpoint 解析;不得再注入 Host bridge。
|
||||||
|
|
||||||
|
目标模块拥有 Command、Query 和 DTO,调用模块只引用这些契约。
|
||||||
|
|
||||||
|
### 8.3 异常和文件组织
|
||||||
|
|
||||||
|
- 全量清理共享 `BusinessException`、`NotFoundException`、`UnauthorizedException` 的旧单参数调用。
|
||||||
|
- 按语义选择 `ErrorCode`,不做无脑统一替换。
|
||||||
|
- 拆分所有一个文件包含多个顶级 class、record 或 enum 的源文件。
|
||||||
|
- Command、Query、Handler、Endpoint、Request 和 DTO 继续保持一文件一类型。
|
||||||
|
|
||||||
|
### 8.4 架构护栏
|
||||||
|
|
||||||
|
扩展 `BuildFlow.Architecture.Tests`,至少验证:
|
||||||
|
|
||||||
|
- Endpoint 构造函数不允许依赖任何 DbContext。
|
||||||
|
- Host 不允许存在 `InProcess*` 跨模块桥接注册。
|
||||||
|
- 业务异常调用必须显式携带 `ErrorCode`。
|
||||||
|
- 一个源文件只允许一个顶级 class、record 或 enum,明确的嵌套配置类型除外。
|
||||||
|
- 模块不能出现独立 Program、launchSettings 或 Api 项目。
|
||||||
|
|
||||||
|
架构测试必须扫描当前源码而不是只验证少量已知文件,确保新增代码也受约束。
|
||||||
|
|
||||||
|
## 9. Migration 设计
|
||||||
|
|
||||||
|
预计涉及:
|
||||||
|
|
||||||
|
- Workflow:新增 Timer Job 表、导入租约/进度字段及索引。
|
||||||
|
- RAG:新增聊天消息组合索引。
|
||||||
|
- File:新增 `SizeVerified` 字段及待验证查询索引。
|
||||||
|
|
||||||
|
Migration 验证分两条路径:
|
||||||
|
|
||||||
|
1. 空库从零执行全部 Migration,验证最终模型与 Snapshot 一致。
|
||||||
|
2. 从本次变更前的数据库结构升级,验证原有数据、主键、外键和索引保留。
|
||||||
|
|
||||||
|
每个 Migration 必须检查生成 SQL,不接受意外 `DropTable`、`DropColumn` 或数据丢失式重建。涉及数据回填时使用明确、幂等的 SQL。
|
||||||
|
|
||||||
|
## 10. 深度测试策略
|
||||||
|
|
||||||
|
### 10.1 测试层级
|
||||||
|
|
||||||
|
- 单元测试:所有权、路径边界、上下文窗口、状态转换、批量计算和错误分类。
|
||||||
|
- EF 集成测试:使用真实 PostgreSQL 验证 Migration、锁、`SKIP LOCKED`、UPSERT 和索引支持的查询。
|
||||||
|
- Redis 集成测试:验证原子追加、并发一致性、TTL 和缓存重建。
|
||||||
|
- S3 集成测试:验证空对象、缺失对象、网络失败和后台回填。
|
||||||
|
- Host 集成测试:验证权限、响应信封、HTTP 状态、SSE 事件顺序和取消传播。
|
||||||
|
- 并发测试:至少两个 processor 实例同时竞争 Timer、Import 和 Monitoring 数据。
|
||||||
|
- 大数据测试:表单统计、导入、导出、长会话和大量待回填文件。
|
||||||
|
|
||||||
|
### 10.2 稳定性能门禁
|
||||||
|
|
||||||
|
性能验收优先使用确定性指标,避免只依赖容易抖动的墙钟时间:
|
||||||
|
|
||||||
|
- SQL 命令数量。
|
||||||
|
- 远程 S3 调用数量和最大并发数。
|
||||||
|
- Redis 每次追加的命令数量与传输对象大小。
|
||||||
|
- LLM 上下文消息数和估算 token 上限。
|
||||||
|
- 导入每批事务数量。
|
||||||
|
- 峰值托管内存和临时文件清理结果。
|
||||||
|
|
||||||
|
墙钟耗时只作为辅助报告,不作为唯一门禁。
|
||||||
|
|
||||||
|
## 11. 闭环 SOP
|
||||||
|
|
||||||
|
新增后端优化 SOP,按以下顺序执行:
|
||||||
|
|
||||||
|
1. 环境检查:.NET 10、Docker、PostgreSQL、Redis、S3 兼容服务和必要配置。
|
||||||
|
2. 静态扫描:固定密码、裸异常、Endpoint DbContext、InProcess bridge、单文件多类型、`DateTime.Now`。
|
||||||
|
3. 格式与构建:`dotnet build BuildFlow.slnx --no-restore`,要求 0 warning / 0 error。
|
||||||
|
4. 单元与架构测试:运行全部非外部依赖测试,禁止 skipped。
|
||||||
|
5. 基础设施启动:启动隔离的 PostgreSQL、Redis 和 S3 测试容器。
|
||||||
|
6. Migration 测试:空库迁移和存量结构升级。
|
||||||
|
7. 集成测试:数据库锁、UPSERT、Redis、S3、Host HTTP/SSE。
|
||||||
|
8. 并发与恢复:双 worker 竞争、worker 崩溃、租约过期、Host 重启。
|
||||||
|
9. 大数据场景:长会话、大表单、大 Excel、大目录。
|
||||||
|
10. 结果归档:命令、通过数、失败数、skipped 数、耗时、查询计数、内存结果和失败日志路径。
|
||||||
|
|
||||||
|
SOP 脚本任何一步失败必须立即返回非零退出码,不得继续输出“完成”。脚本不得修改开发者现有数据库或覆盖本地 Secrets。
|
||||||
|
|
||||||
|
## 12. 回滚与故障处理
|
||||||
|
|
||||||
|
- 每批独立提交;下一批开始前上一批必须全绿。
|
||||||
|
- 行为改造保留配置开关时,开关只用于短期回滚,不长期维持两套实现。
|
||||||
|
- Timer 和 Import Migration 的 Down 只删除本批新增结构;执行 Down 前必须明确会丢失尚未完成的任务状态。
|
||||||
|
- 后台 processor 出现持续失败时停止领取新任务,但 Host 其他 API 保持可用。
|
||||||
|
- 配置缺失导致安全边界无法建立时采用 fail closed,不回退匿名或固定密码行为。
|
||||||
|
|
||||||
|
## 13. 完成审计
|
||||||
|
|
||||||
|
最终完成前逐项核对:
|
||||||
|
|
||||||
|
- 第一至第五批所有验收证据均有对应测试或命令输出。
|
||||||
|
- 全解决方案测试通过,0 skipped;若第三方环境导致无法执行,目标不得标记完成。
|
||||||
|
- 空库和升级 Migration 均通过。
|
||||||
|
- 所有静态规范扫描为零违规。
|
||||||
|
- 现有 API 路由和 SSE 协议回归通过。
|
||||||
|
- 没有遗留 `Task.Run` Timer、非原子 Redis 读改写、导入任务先查后抢、逐记录 Monitoring 查询或 GET 路径 S3 回填。
|
||||||
|
- SOP 从干净测试环境完整运行一次并生成归档报告。
|
||||||
Loading…
x
Reference in New Issue
Block a user