Files
silk/项目整改实施计划_V1.0.md
T
2026-08-13 23:54:04 +08:00

917 lines
35 KiB
Markdown
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.
# 蚕病智能防控平台整改实施计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 依据《蚕病智能防控平台规格说明书 V2.2》,先消除安全与业务正确性风险,再建立可迁移、可测试、可观测、可恢复的试点闭环。
**Architecture:** 保持现有 Go/Gin 主业务后端、Python/FastAPI AI 微服务、React Web、Taro 小程序、React Native APP 和 IoTDB/PostgreSQL/S3/WVP 基础。整改采用“小步迁移 + TDD + 功能开关 + 可回滚发布”,不在本计划中替换既有主技术栈。
**Tech Stack:** Go 1.23、Gin、GORM、PostgreSQL、IoTDB、Valkey/Redis、MQTT、FastAPI、ONNX Runtime、React、Taro 4、React Native 0.74、Vitest、pytest。
## Global Constraints
- 不得手改 `miniapp/dist/``web/dist/`、APK、Linux 二进制等生成产物。
- 不得覆盖无关的用户修改;每个任务只提交本任务文件。
- 新功能先写失败测试,再做最小实现,再运行模块和全量检查。
- 生产密钥只通过环境变量/密钥管理注入,不写入提交文件。
- 所有 schema 修改先备份、再迁移;生产不允许 GORM AutoMigrate 自动改表。
- 每项交付必须追加 `开发交接记录.md`,记录设计依据、验证证据和回滚点。
- 涉及发布时严格执行 `AGENTS.md` 的开发服务器部署前置、备份和回滚要求。
- 任务 0 的开放技术选型未确认前,不得擅自引入迁移工具、CI 平台或新外部服务。
---
## 1. 执行原则与分工
### 1.1 优先级
| 波次 | 目标 | 任务 | 预计串行工期 |
|---|---|---|---|
| Wave 0 | 决策与基线 | 01 | 23 人日 |
| Wave 1 | P0 安全与正确性 | 27 | 1015 人日 |
| Wave 2 | 工程可靠性 | 89 | 610 人日 |
| Wave 3 | 业务闭环 | 1012 | 2033 人日 |
| Wave 4 | 验收与发布 | 1314 | 711 人日 |
工期只用于排期,不是验收标准。多人并行时,任务 2 是涉及数据库实体任务的前置;任务 3~7 可在接口边界明确后分支并行,但合并前必须统一回归。
### 1.2 建议责任域
- 后端负责人:任务 210、13。
- AI 负责人:任务 6 中 AI 响应与模型元数据、任务 14 AI 验收。
- Web/小程序负责人:任务 1、11、12 的端侧部分。
- 运维负责人:任务 3、8、9、13 的配置、监控、备份和发布。
- 测试负责人:维护需求 ID—用例—结果矩阵,不以开发自测替代验收。
- 领域专家:确认 qPCR 规则、风险动作、病种清单和知识来源。
### 1.3 每个任务的完成定义
1. 需求 ID 与验收用例明确。
2. 失败测试已观察到预期失败原因。
3. 实现和迁移完成,模块测试通过。
4. 相关模块全量检查通过或准确记录环境阻断。
5. 文档、配置示例和交接记录更新。
6. 回滚步骤经过静态复核;涉及部署时完成实际冒烟。
---
### Task 0: 固化开放决策与整改基线
**Requirements:** V2.2 第 13 章
**Owner:** 项目负责人 + 后端/运维/领域负责人
**Estimated effort:** 0.51 人日
**Files:**
- Create: `docs/decisions/2026-08-13-remediation-decisions.md`
- Modify: `后续工作计划.md`
- Modify: `开发交接记录.md`
**Interfaces:**
- Consumes: `蚕病智能防控平台规格说明书_V2.2.md` 的开放决策。
- Produces: 迁移工具、CI 平台、保留期限、qPCR 规则负责人、APP 范围和模型门槛的书面决定。
- [ ] **Step 1: 召开 60 分钟决策会并逐项记录负责人、选项、理由和生效日期**
文档必须使用如下固定表头:
```markdown
| Decision ID | 结论 | 备选方案 | 选择理由 | 影响 | 负责人 | 复审条件 |
|---|---|---|---|---|---|---|
```
- [ ] **Step 2: 明确七项决定**
必须覆盖:数据库迁移工具、CI 承载平台、制品位置、数据保留期限、qPCR 方案确认流程、APP 产品范围、二分类转多病种指标门槛。不得写“以后再定”;暂不实施时写“保持现状 + 明确复审触发条件”。
- [ ] **Step 3: 将 Wave 14 写入 `后续工作计划.md`,使用七态能力状态而不是单一勾选**
- [ ] **Step 4: 评审并提交**
```powershell
rg -n "Decision ID|迁移|CI|保留|qPCR|APP|二分类" docs/decisions/2026-08-13-remediation-decisions.md
git diff --check
```
Expected: 七项主题均可检索,`git diff --check` exit 0。
**Rollback:** 删除决策文档并恢复计划/交接记录;尚未执行的决定不产生运行时影响。
---
### Task 1: 恢复可复现的多端质量基线
**Requirements:** V2.2 第 10 章
**Owner:** 各端负责人
**Estimated effort:** 12 人日
**Files:**
- Modify: `miniapp/src/pages/dashboard/index.tsx`
- Modify: `miniapp/package.json`
- Modify: `app/package.json`
- Modify: `ai-service/README.md`
- Create: `scripts/verify.ps1`
- Modify: CI 配置文件(路径由 Task 0 确认的平台决定)
- Modify: `开发交接记录.md`
**Interfaces:**
- Produces: 单命令验证入口 `powershell -ExecutionPolicy Bypass -File scripts/verify.ps1`
- [ ] **Step 1: 修复小程序 JSX 类型错误**
将裸文本箭头改为明确字符节点:
```tsx
<Text className={styles.sectionAction}>详情 {'>'}</Text>
<Text className={styles.sectionAction} onClick={handleAlarmTap}>全部 {'>'}</Text>
```
- [ ] **Step 2: 为小程序增加只读类型检查脚本,为 APP 确认本地 devDependencies 安装可用**
```json
{
"scripts": {
"typecheck": "tsc --noEmit"
}
}
```
- [ ] **Step 3: 创建验证脚本**
`scripts/verify.ps1` 必须按模块执行并在任一失败时返回非 0:
```powershell
$ErrorActionPreference = 'Stop'
Push-Location server-go; go test ./...; go vet ./...; go build ./...; Pop-Location
Push-Location web; npm test; npm run lint; npm run build; Pop-Location
Push-Location miniapp; npm run typecheck; npm run build:weapp; Pop-Location
Push-Location app; npm run tsc; npm run lint; Pop-Location
Push-Location ai-service; python -m pytest; Pop-Location
```
- [ ] **Step 4: 在干净依赖环境运行并保存结果**
Expected: 五个模块全部 exit 0Web lint warning 单独建单,不允许隐藏 exit code。
- [ ] **Step 5: 接入 Task 0 确认的 CI,禁止失败检查合并**
**Rollback:** 恢复两处 JSX 与脚本变更;CI 门禁可关闭但不得删除失败证据。
---
### Task 2: 引入版本化数据库迁移与 Schema 启动门禁
**Requirements:** V2.2 5.5、阶段 B
**Owner:** 后端负责人 + DBA/运维
**Estimated effort:** 23 人日
**Files:**
- Create: `server-go/migrations/000001_baseline.up.sql`
- Create: `server-go/migrations/000001_baseline.down.sql`
- Create: `server-go/internal/database/migrate.go`
- Create: `server-go/internal/database/migrate_test.go`
- Modify: `server-go/internal/database/db.go`
- Modify: `server-go/cmd/server/main.go`
- Modify: `README.md`
- Modify: `部署指南(物理机).md`
**Interfaces:**
- Produces: `database.CheckSchemaVersion(db *gorm.DB, expected string) error`
- Constraint: 迁移执行器使用 Task 0 批准的工具;SQL 迁移文件作为唯一 schema 事实来源。
- [ ] **Step 1: 写失败测试,证明 schema 版本缺失/落后时返回错误**
```go
func TestCheckSchemaVersionRejectsMismatch(t *testing.T) {
err := CheckSchemaVersion(fakeDBWithVersion("1"), "2")
if err == nil { t.Fatal("expected schema mismatch") }
}
```
- [ ] **Step 2: 生成当前 schema 基线 SQL,并人工核对所有表、索引和约束**
不得使用 `AutoMigrate` 输出作为未经审核的生产脚本;基线需在空库执行后与当前模型对照。
- [ ] **Step 3: 实现启动检查并移除生产自动迁移**
```go
func CheckSchemaVersion(db *gorm.DB, expected string) error
```
开发环境自动迁移只能由显式 `ALLOW_DEV_AUTOMIGRATE=true` 开启;生产配置忽略该开关并拒绝 AutoMigrate。
- [ ] **Step 4: 验证空库建库、旧库升级和 down 回滚**
```powershell
go test ./internal/database -run TestCheckSchemaVersion -v
go test ./...
go vet ./...
go build ./...
```
Expected: 版本匹配通过,不匹配时服务启动失败且日志不包含 DSN 密码。
**Rollback:** 恢复旧二进制和迁移前数据库备份;不得只回滚代码而保留不兼容 schema。
---
### Task 3: 移除默认密钥与默认管理员密码
> 2026-08-13 用户决策:Task 3 本轮跳过,留到最后处理。期间不删除默认密钥/默认管理员密码,不执行客户端密码预填清理。
**Requirements:** IAM-004、SEC-001、SEC-002
**Owner:** 后端 + 运维 + 各客户端负责人
**Estimated effort:** 12 人日
**Files:**
- Modify: `server-go/internal/config/config.go`
- Create: `server-go/internal/config/config_test.go`
- Modify: `server-go/internal/handler/auth.go`
- Modify: `miniapp/src/pages/login/index.tsx`
- Modify: `app/src/screens/LoginScreen.tsx`
- Create: `server-go/.env.example`
- Modify: `app/.env.example`
- Modify: `README.md`
**Interfaces:**
- Produces: `config.ValidateForEnvironment(cfg *Config, environment string) error`
- [ ] **Step 1: 写表驱动测试,验证 production 缺 JWT/S3/ZLM/Internal API/管理员初始化配置时失败**
```go
func TestValidateForEnvironmentRejectsProductionDefaults(t *testing.T) {
cfg := &Config{JWTSecret: "silk-secret-please-change-me"}
if err := ValidateForEnvironment(cfg, "production"); err == nil {
t.Fatal("expected insecure config rejection")
}
}
```
- [ ] **Step 2: 删除敏感字段的可用默认值,保留非敏感本地地址默认值**
生产环境必须显式设置 `APP_ENV=production`;关键字段为空、长度不足或等于历史默认值均拒绝启动。
- [ ] **Step 3: 删除小程序和 APP 密码预填**
```tsx
const [password, setPassword] = useState('');
```
- [ ] **Step 4: 生成轮换清单并由运维在目标环境执行**
轮换范围至少包括 PostgreSQL、Redis、MQTT、JWT、S3、WVP、ZLM、Internal API 和默认管理员。
- [ ] **Step 5: 验证**
```powershell
go test ./internal/config ./internal/handler -v
rg -n "silk@123|Silk-App-Secret|silk-internal-2026|su6Tied" server-go miniapp/src app/src
```
Expected: 测试通过;源码搜索不再出现可用历史密钥。文档可提及已废弃值,但不得作为配置示例。
**Rollback:** 代码可恢复,但已轮换密钥不得回退为历史值;使用新生成密钥更新旧版本运行环境。
---
### Task 4: 收口视频访问与摄像头密钥输出
**Requirements:** IAM-003、VIDEO-001、VIDEO-002、SEC-003、SEC-004
**Owner:** 视频后端负责人
**Estimated effort:** 23 人日
**Files:**
- Modify: `server-go/internal/model/models.go`
- Create: `server-go/internal/handler/video_dto.go`
- Create: `server-go/internal/handler/video_auth.go`
- Create: `server-go/internal/handler/video_auth_test.go`
- Modify: `server-go/internal/handler/video_camera.go`
- Modify: `server-go/internal/handler/video_clip.go`
- Modify: `server-go/internal/handler/video_stream.go`
- Modify: `server-go/internal/middleware/auth.go`
- Modify: `web/src/dal/video.ts`
**Interfaces:**
- Produces: `IssueVideoToken(userID, resourceType, resourceID string, ttl time.Duration) (string, error)`
- Produces: `ValidateVideoToken(token, resourceType, resourceID string) error`
- Token TTL: 5 分钟;绑定资源类型、ID、用户、用途和过期时间。
- [ ] **Step 1: 写安全回归测试**
```go
func TestCameraResponseNeverContainsSecrets(t *testing.T)
func TestVideoStreamRejectsMissingOrWrongResourceToken(t *testing.T)
func TestVideoTokenExpires(t *testing.T)
```
- [ ] **Step 2: 将模型密钥字段标记为不序列化,并使用公开 DTO**
```go
PasswordEnc *string `json:"-"`
GbAuthPassword *string `json:"-"`
```
`CameraPublic` 只包含播放和管理界面需要的非敏感字段;WVP 配置响应删除 `sipPassword`
- [ ] **Step 3: 播放地址改为短时令牌**
`/video/clips/:id/play``/video/cameras/:id/play` 先执行 JWT、权限和对象范围校验,再返回带短时 token 的 URL。stream/proxy 路由即使位于 auth 白名单,也必须验证该 token。
- [ ] **Step 4: 验证直接枚举 ID、跨用户 token、过期 token 和资源错配均为 401/403**
```powershell
go test ./internal/handler -run "Video|CameraResponse" -v
go test ./...
```
**Rollback:** 可临时回滚到“JWT header 直接保护 stream”模式;禁止回滚为公开流或返回密钥。
---
### Task 5: 修复 WebSocket 越权与 AI 流 SSRF
**Requirements:** IAM-003、SEC-005、SEC-007、VIDEO-004
**Owner:** 后端 + AI 负责人
**Estimated effort:** 23 人日
**Files:**
- Modify: `server-go/internal/ws/gateway.go`
- Create: `server-go/internal/ws/gateway_test.go`
- Modify: `server-go/cmd/server/main.go`
- Modify: `server-go/internal/config/config.go`
- Modify: `ai-service/app/main.py`
- Create: `ai-service/app/stream_tasks.py`
- Modify: `ai-service/tests/test_api.py`
**Interfaces:**
- WebSocket: `NewHub(jwtSecret string, authorizer DeviceAuthorizer, allowedOrigins []string)`
- AI 流接口:`POST /internal/stream-tasks` 接受 `taskId` 和已解析的内部流引用,不接受公网客户端任意 URL。
- [ ] **Step 1: 写失败测试,覆盖非法 Origin、refresh token、无权 deviceKey 和全局广播泄露**
- [ ] **Step 2: 增加设备授权接口并在 subscribe 时校验**
```go
type DeviceAuthorizer interface {
CanReadDevice(userID, deviceKey string) (bool, error)
}
```
删除普通连接的无范围 `telemetry.all` 广播;管理视图使用单独权限和组织范围订阅。
- [ ] **Step 3: 禁止长期 JWT 出现在 query,改用一次性 WebSocket ticket**
ticket 有效期 60 秒、仅使用一次;日志中不记录 ticket 值。
- [ ] **Step 4: 将 AI 流读取移到受限 worker**
只允许 Go 后端以内部认证创建任务;限制并发、帧数、分辨率、超时和允许的内部地址范围。FastAPI 路由不得在事件循环中直接执行长时间 `VideoCapture.read()`
- [ ] **Step 5: 验证**
```powershell
go test ./internal/ws -v
Push-Location ai-service; python -m pytest tests/test_api.py -v; Pop-Location
```
**Rollback:** 关闭流 AI 功能开关;WebSocket 保留来源和对象授权,不得回滚为 `CheckOrigin=true`
---
### Task 6: 修复 AI 风险语义并隔离 Mock 数据
**Requirements:** AI-INS-002004、RISK-001003
**Owner:** AI + Go 后端负责人
**Estimated effort:** 23 人日
**Files:**
- Modify: `ai-service/app/main.py`
- Modify: `ai-service/app/config.py`
- Modify: `ai-service/tests/test_api.py`
- Modify: `server-go/internal/service/ai_client.go`
- Modify: `server-go/internal/service/risk.go`
- Modify: `server-go/internal/service/risk_test.go`
- Modify: `server-go/internal/handler/inspection.go`
- Modify: `server-go/internal/handler/inspection_test.go`
- Create: `server-go/migrations/000007_inspection_idempotency.up.sql`
- Create: `server-go/migrations/000007_inspection_idempotency.down.sql`
- Modify: `server-go/internal/model/inspection.go`
- Create: `server-go/migrations/000002_risk_assessment.up.sql`
- Create: `server-go/migrations/000002_risk_assessment.down.sql`
**Interfaces:**
- AI response adds `modelVersion`, `isMock`, `abnormalProbability`
- Go produces `RiskAssessment` with score、level、confidence、components、missing、modelVersion、ruleVersion。
- [ ] **Step 1: 写关键失败测试**
```go
func TestHealthyHighConfidenceDoesNotIncreaseRisk(t *testing.T) {
got := AbnormalProbability([]AIDetection{{ClassName: "healthy", Confidence: .95}})
if got != 0 { t.Fatalf("got %v", got) }
}
func TestSickHighConfidenceIncreasesRisk(t *testing.T) {
got := AbnormalProbability([]AIDetection{{ClassName: "sick", Confidence: .9}})
if got != .9 { t.Fatalf("got %v", got) }
}
```
- [ ] **Step 2: 扩展 AI 响应并明确异常类别集合**
二分类 `sick` 和多病种中除 `healthy` 外的已配置异常类别贡献风险;空检测返回 unknown,不自动当健康。
- [ ] **Step 3: 用可空组件计算风险**
```go
type RiskInput struct {
AI, Env, Stage, Uniformity *float64
}
type RiskAssessment struct {
Score float64; Level, Confidence, RuleVersion string
Components map[string]*float64; Missing []string
}
```
缺失组件不填 0;V2 规则将可用权重归一化,并把 confidence 降为 low/medium。具体权重先沿用 0.5/0.2/0.15/0.15,标记为待试点校准规则而非科学事实。
- [ ] **Step 4: 持久化输入快照、模型/规则版本和 mock 标识**
Mock 记录可保存供联调,但查询统计默认排除;生产环境收到 `isMock=true` 时记录失败并告警。
- [ ] **Step 5: 评估历史数据**
生成只读 SQL 报告统计历史 `healthy` 高置信度记录及其风险等级;未经人工确认不得批量重算或覆盖。
- [ ] **Step 6: 验证**
```powershell
go test ./internal/service -run "Risk|Abnormal" -v
go test ./internal/handler -run Inspection -v
Push-Location ai-service; python -m pytest -v; Pop-Location
```
**Rollback:** 功能开关回退到只展示 AI 原始结果、不计算综合风险;不得恢复错误的最大置信度算法。
---
### Task 7: 修订 qPCR 判读与检测质控
**Requirements:** DET-003005
**Owner:** 后端负责人 + 领域专家
**Estimated effort:** 12 人日
**Files:**
- Modify: `server-go/internal/model/molecular.go`
- Modify: `server-go/internal/model/molecular_test.go`
- Modify: `server-go/internal/model/lamp.go`
- Modify: `server-go/internal/handler/lamp.go`
- Modify: `web/src/dal/lamp.ts`
- Modify: `web/src/pages/LampTests.tsx`
- Create: `server-go/migrations/000003_detection_qc.up.sql`
- Create: `server-go/migrations/000003_detection_qc.down.sql`
**Interfaces:**
- Produces: `JudgeQPCR(input QPCRJudgeInput) QPCRJudgeResult`
```go
type QPCRJudgeInput struct {
TargetCt *float64
InternalControlCt *float64
PositiveControlValid bool
NegativeControlValid bool
ProtocolVersion string
Threshold float64
}
```
- [ ] **Step 1: 由领域专家签字确认首个 protocolVersion 与阈值适用范围**
未确认前功能只能保存原始数据,不能自动输出阴性/阳性。
- [ ] **Step 2: 写表驱动测试**
覆盖:阳性、阴性、缺 Ct、内参失败、阳性对照失败、阴性对照污染、临界值和未知 protocol。
- [ ] **Step 3: 实现四态结果**
```text
positive / negative / invalid / indeterminate
```
无 Ct 仅在对照和内参有效且方案明确允许时判 negative;其他情况为 invalid/indeterminate。
- [ ] **Step 4: UI 强制填写质控字段并展示判读依据,不允许把 invalid 显示为阴性**
- [ ] **Step 5: 验证**
```powershell
go test ./internal/model -run QPCR -v
go test ./internal/handler -run QPCR -v
Push-Location web; npm test; npm run lint; npm run build; Pop-Location
```
**Rollback:** 回退为“只录入、不自动判读”,保留原始 Ct 和质控数据。
---
### Task 8: 建立可靠通知、吊销与跨实例状态
**Requirements:** IAM-001、MSG-001、MSG-002
**Owner:** 后端 + 运维
**Estimated effort:** 35 人日
**Files:**
- Create: `server-go/internal/model/notification.go`
- Create: `server-go/internal/service/outbox.go`
- Create: `server-go/internal/service/outbox_test.go`
- Modify: `server-go/internal/handler/notification.go`
- Modify: `server-go/internal/middleware/token_blacklist.go`
- Modify: `server-go/internal/middleware/ratelimit.go`
- Create: `server-go/migrations/000004_notifications_outbox.up.sql`
- Create: `server-go/migrations/000004_notifications_outbox.down.sql`
- Modify: `server-go/cmd/server/main.go`
**Interfaces:**
- Produces: `Outbox.PublishTx(tx *gorm.DB, event Event) error`
- Notification states: `pending/sending/sent/retry/failed/cancelled`
- [ ] **Step 1: 写测试证明服务重启后 pending 事件仍可处理,重复 eventId 不重复发送**
- [ ] **Step 2: 业务事务内写 outbox,worker 领取、发送、退避重试并记录最终失败**
- [ ] **Step 3: 将 token 吊销和登录限流迁移到 Redis**
Redis 不可用时:生产登录/刷新采取保守失败;已认证的只读请求按安全评审决定降级,不得静默切回单机内存。
- [ ] **Step 4: 微信发送保存模板、用户授权、响应码和重试结果**
- [ ] **Step 5: 验证并进行进程重启测试**
```powershell
go test ./internal/service ./internal/middleware ./internal/handler -run "Outbox|Notification|Revok|Rate" -v
go test ./...
```
**Rollback:** 暂停 worker 保留 outbox 数据;恢复旧读接口时不得删除未发送事件。
---
### Task 9: 建立统一检测任务、样本链与发病事件
**Requirements:** DET-001/002/006、TRACE-001
**Owner:** 后端 + Web + 小程序负责人
**Estimated effort:** 58 人日
**Files:**
- Create: `server-go/internal/model/detection_task.go`
- Create: `server-go/internal/model/sample.go`
- Create: `server-go/internal/model/disease_event.go`
- Create: `server-go/internal/handler/detection_task.go`
- Create: `server-go/internal/handler/detection_task_test.go`
- Modify: `server-go/internal/handler/inspection.go`
- Modify: `server-go/internal/handler/trace.go`
- Create: `server-go/migrations/000005_detection_disease_events.up.sql`
- Create: `server-go/migrations/000005_detection_disease_events.down.sql`
- Create: `web/src/dal/detectionTask.ts`
- Create: `web/src/pages/DetectionTasks.tsx`
- Modify: `web/src/router.tsx`
**Interfaces:**
- DetectionTask states: `draft/pending/assigned/sampling/testing/review/completed/cancelled`
- Sample states: `created/collected/handed_over/received/testing/consumed/disposed`
- DiseaseEvent states: `suspected/confirmed/controlled/closed/reopened`
- [ ] **Step 1: 写状态机和幂等失败测试**
```go
func TestDetectionTaskRejectsInvalidTransition(t *testing.T)
func TestOrangeInspectionCreatesOnlyOnePendingTask(t *testing.T)
func TestDiseaseEventRequiresEvidenceToConfirm(t *testing.T)
```
- [ ] **Step 2: 实现实体、外键、唯一业务键和状态转换服务**
- [ ] **Step 3: 橙色巡检通过 outbox 创建“待确认”任务,不能直接指派或视为已检测**
- [ ] **Step 4: 检测有效阳性或专家确认后创建 DiseaseEventTraceRecord 改为关联 DiseaseEvent**
- [ ] **Step 5: Web 提供任务列表、分派、采样交接、检测和复核视图**
- [ ] **Step 6: 验证完整 API 流程和重复请求**
```powershell
go test ./internal/model ./internal/service ./internal/handler -run "DetectionTask|Sample|DiseaseEvent" -v
Push-Location web; npm test; npm run lint; npm run build; Pop-Location
```
**Rollback:** 关闭自动建单开关,保留新表数据;旧 LAMP 记录继续只读,禁止删除已建立关联。
---
### Task 10: 补齐消毒、种源与二维码身份链
**Requirements:** FARM-002、FARM-004、FARM-005、TRACE-007
**Owner:** 后端 + Web + 小程序负责人
**Estimated effort:** 46 人日
**Files:**
- Create: `server-go/internal/model/biosecurity.go`
- Create: `server-go/internal/handler/biosecurity.go`
- Create: `server-go/internal/handler/biosecurity_test.go`
- Create: `server-go/migrations/000006_biosecurity.up.sql`
- Create: `server-go/migrations/000006_biosecurity.down.sql`
- Create: `web/src/dal/biosecurity.ts`
- Create: `web/src/pages/Biosecurity.tsx`
- Create: `miniapp/src/api/biosecurity.ts`
- Create: `miniapp/src/pages/biosecurity/index.tsx`
- Modify: `miniapp/src/app.config.ts`
**Interfaces:**
- QR payload only contains opaque `entityType``publicId`、校验版本,不包含密码、内部自增 ID 或个人信息。
- [ ] **Step 1: 写测试覆盖二维码篡改、跨组织访问、种源链循环和消毒记录必填项**
- [ ] **Step 2: 实现 SeedSource、DisinfectionRecord、二维码解析与对象授权**
- [ ] **Step 3: Web 提供来源/凭证/消毒计划与执行;小程序提供扫码和现场执行**
- [ ] **Step 4: 溯源初报消费种源和消毒数据,缺失时明确显示“证据不足”**
- [ ] **Step 5: 验证**
```powershell
go test ./internal/handler ./internal/service -run "Biosecurity|SeedSource|Disinfection|QRCode" -v
Push-Location web; npm run build; Pop-Location
Push-Location miniapp; npm run typecheck; npm run build:weapp; Pop-Location
```
**Rollback:** 隐藏页面入口并停止新增写入,保留已产生的种源/消毒/二维码映射。
---
### Task 11: 实现小程序离线巡检与可靠同步
**Requirements:** AI-INS-005、9.4 弱网要求
**Owner:** 小程序 + Go 后端负责人
**Estimated effort:** 46 人日
**Files:**
- Create: `miniapp/src/services/offlineQueue.ts`
- Create: `miniapp/src/services/offlineQueue.test.ts`
- Modify: `miniapp/src/pages/inspection/index.tsx`
- Modify: `miniapp/src/api/inspections.ts`
- Modify: `miniapp/src/store/useStore.ts`
- Modify: `server-go/internal/handler/inspection.go`
- Modify: `server-go/internal/handler/inspection_test.go`
**Interfaces:**
- Queue item states: `pending/uploading/synced/conflict/failed`
- Idempotency key: 客户端生成 UUID,创建后永不变化,重试复用。
- [ ] **Step 1: 写队列单测**
覆盖断网入队、重启恢复、重复点击、指数退避、成功删除本地图片引用、永久失败和冲突展示。
- [ ] **Step 2: 实现本地持久队列,限制容量并显示剩余空间**
- [ ] **Step 3: 网络恢复后串行上传,401 先刷新令牌,5xx/超时重试,4xx 进入人工处理**
- [ ] **Step 4: 服务端幂等键增加用户作用域,避免不同用户键碰撞**
唯一约束改为 `(user_id, idempotency_key)`,使用 `000007_inspection_idempotency` 迁移;先扫描并处理既有冲突,迁移不得静默删除记录。
- [ ] **Step 5: 在开发者工具模拟离线—重启—联网流程并保存截图/日志证据**
```powershell
Push-Location miniapp; npm run typecheck; npm run build:weapp; Pop-Location
go test ./internal/handler -run Inspection -v
```
**Rollback:** 关闭离线入口,只允许在线提交;服务端幂等改动保留,避免重复数据回归。
---
### Task 12: 完善环境规则、会诊治理、知识审核与效果评估
**Requirements:** ENV-003005、EXP-002005、KB-001/002、ANA-001/002
**Owner:** 后端 + Web + 领域专家 + 测试负责人
**Estimated effort:** 58 人日
**Files:**
- Create: `server-go/internal/model/governance.go`
- Create: `server-go/internal/service/rule_engine.go`
- Create: `server-go/internal/service/rule_engine_test.go`
- Modify: `server-go/internal/service/weather_risk.go`
- Modify: `server-go/internal/model/consultation.go`
- Modify: `server-go/internal/handler/consultation.go`
- Modify: `server-go/internal/handler/consultation_test.go`
- Modify: `server-go/internal/model/knowledge.go`
- Modify: `server-go/internal/handler/knowledge.go`
- Modify: `server-go/internal/service/health_profile.go`
- Modify: `server-go/internal/service/health_profile_test.go`
- Create: `server-go/migrations/000008_governance_effectiveness.up.sql`
- Create: `server-go/migrations/000008_governance_effectiveness.down.sql`
- Modify: `web/src/pages/Consultations.tsx`
- Modify: `web/src/pages/Knowledge.tsx`
- Modify: `web/src/pages/Traces.tsx`
**Interfaces:**
- Produces: `EvaluateRules(ctx RuleContext, rules []RuleVersion) []RuleResult`
- Consultation states: `unassigned/assigned/accepted/needs_info/resolved/archived`
- Knowledge states: `draft/review/published/withdrawn`
- Produces: `EvaluateControlEffect(eventID string, window EffectWindow) EffectReport`
- [ ] **Step 1: 写规则时窗与缺失数据测试**
```go
func TestRuleRequiresThreeContinuousDaysOfHumidity(t *testing.T)
func TestStaleTelemetryIsReportedMissing(t *testing.T)
func TestRuleResultKeepsInputAndVersion(t *testing.T)
```
规则输出必须保存版本、输入时间窗、命中项、缺失项和计算时间;单个最新值不得冒充连续时窗。
- [ ] **Step 2: 实现规则版本和数据新鲜度**
`RuleContext` 至少包含批次阶段、时间窗遥测、天气、密度、通风、种源和消毒证据。缺失输入降低结论可信度,不填 0。
- [ ] **Step 3: 写并实现会诊状态机、SLA 和意见版本测试**
```go
func TestConsultationRejectsArchiveBeforeResolution(t *testing.T)
func TestConsultationOpinionCreatesNewVersion(t *testing.T)
func TestConsultationSLAMarksOverdue(t *testing.T)
```
每次意见修改保留作者、时间、旧版本和原因;超时只产生升级事件,不自动伪造专家结论。
- [ ] **Step 4: 增加知识审核与来源字段**
未发布内容只对有审核权限用户可见;撤回后不在农户端出现,但历史病例引用保留版本快照。
- [ ] **Step 5: 实现防控效果评估**
`EffectReport` 对比处置前后指定窗口的有效巡检风险、有效检测阳性率、复发、死亡/损失和成本;所有缺失维度单独列出,不把缺失当改善。
- [ ] **Step 6: Web 完成会诊 SLA、知识审核和效果报告入口**
- [ ] **Step 7: 验证**
```powershell
go test ./internal/model ./internal/service ./internal/handler -run "Rule|Consultation|Knowledge|Health|Effect" -v
Push-Location web; npm test; npm run lint; npm run build; Pop-Location
```
**Rollback:** 关闭新版规则、SLA 和效果报告入口,保留版本与审核数据;不得覆盖或删除旧专家意见。
---
### Task 13: 建立可观测性、容量与恢复验证
**Requirements:** V2.2 9.19.3、10.2
**Owner:** 运维 + 后端 + 测试负责人
**Estimated effort:** 46 人日
**Files:**
- Modify: `server-go/internal/middleware/logger.go`
- Create: `server-go/internal/middleware/request_id.go`
- Modify: `server-go/internal/service/ai_client.go`
- Modify: `ai-service/app/main.py`
- Create: `docs/operations/slo-and-alerts.md`
- Create: `docs/operations/load-test-scenarios.md`
- Create: `docs/operations/backup-restore-drill.md`
- Modify: `部署指南(物理机).md`
**Interfaces:**
- 每个 HTTP 请求生成/透传 `X-Request-ID`;事件使用 `eventId`;日志字段命名统一。
- [ ] **Step 1: 写中间件测试,验证 requestId 响应头、透传和日志脱敏**
- [ ] **Step 2: 增加依赖指标**
至少覆盖 PostgreSQL、Redis、MQTT、IoTDB、S3、AI、WVP、微信、天气的请求数、失败数、p95 和积压/容量。
- [ ] **Step 3: 定义并执行三类负载场景**
```text
A. 500 在线用户:80% 查询、10% 遥测趋势、5% 上传、5% 管理操作
B. 1000 WebSocket 连接:按授权设备订阅,持续 30 分钟
C. AI 峰值:指定 T4、模型版本、输入尺寸和并发,记录 p50/p95/失败率
```
- [ ] **Step 4: 执行备份恢复演练**
恢复到隔离数据库/对象存储命名空间;记录备份时间、恢复时间、数据点核对、RPO/RTO 和失败项。
- [ ] **Step 5: 建立告警阈值和负责人表,进行一次模拟故障验证通知链**
**Rollback:** 指标与 requestId 可保持;高开销采样可降低频率,不得删除备份恢复记录。
---
### Task 14: 建立规格追踪、端到端验收与发布门禁
**Requirements:** V2.2 第 1012 章
**Owner:** 测试负责人 + 项目负责人
**Estimated effort:** 35 人日
**Files:**
- Create: `docs/acceptance/requirements-traceability.md`
- Create: `docs/acceptance/core-e2e-cases.md`
- Create: `docs/acceptance/release-checklist.md`
- Modify: `蚕病智能防控平台规格说明书_V2.2.md`
- Modify: `后续工作计划.md`
- Modify: `开发交接记录.md`
**Interfaces:**
- Traceability columns: Requirement ID、实现、自动测试、手工用例、环境、证据、状态、限制、负责人。
- [ ] **Step 1: 为 Wave 13 的每个需求 ID 建立追踪行,不允许只写模块名**
- [ ] **Step 2: 编写核心端到端用例**
至少覆盖:
```text
1. healthy 图片 → 低风险/不触发检测
2. sick 图片 → 橙色 → 仅一个待确认任务
3. AI 失败 → 可重试且无虚假风险
4. qPCR 对照失败 → invalid → 不确诊
5. 有效阳性 → DiseaseEvent → 自动溯源初报
6. 无权用户访问视频/设备/WebSocket → 403
7. 离线巡检 → 重启 → 联网 → 仅同步一次
8. 通知发送失败 → 重试 → 最终状态可查
9. 服务重启 → 任务/通知/吊销状态保持
10. 备份恢复 → 核心记录与对象引用核对通过
```
- [ ] **Step 3: 在目标环境执行全量质量门禁和端到端用例**
```powershell
powershell -ExecutionPolicy Bypass -File scripts/verify.ps1
git diff --check
```
- [ ] **Step 4: 按 AGENTS.md 完成开发服务器备份、部署、健康检查、冒烟和回滚点记录**
- [ ] **Step 5: 更新能力状态**
只有附有测试/环境/日期证据的功能才能从“部分可用”改为“可验收/已验证”。
**Rollback:** 使用发布前 tag/commit、数据库 dump、旧二进制和旧 dist;回滚后重新执行健康检查并记录原因。
---
## 2. 不纳入本轮整改的事项
以下能力保留在 V2.2 阶段 D/E,不得为了展示进度插入 Wave 1~3:
- 7 类病种真实模型训练与发布。
- 固定摄像头群体巡检和个体追踪。
- LAMP 图像自动判读。
- SERS/高光谱自动分析和方法学结论。
- 区域传播预测模型和数字孪生自动控制。
- 未经确认的 APP 全量蚕病功能同步。
## 3. 合并与发布顺序
```text
Task 0 决策
Task 1 质量基线 ── Task 2 迁移基线
↓ ↓
Task 3 配置安全 Task 6/7 数据语义
Task 4 视频安全 Task 8 可靠事件
Task 5 WS/AI 安全 ↓
└──────────────→ Task 9 检测/发病主链
Task 10 生物安全链
Task 11 离线同步
Task 12 规则/治理/效果
Task 13 运维验证
Task 14 总体验收
```
Task 3~5 可以分别评审,但必须在 Wave 1 集成分支统一执行安全回归。Task 6~12 每个 schema 变更必须基于 Task 2 的迁移机制顺序编号,禁止多人自行创建冲突编号。
## 4. 计划自检清单
- [ ] V2.2 的 P0 问题均映射到 Task 27。
- [ ] 业务可靠性和治理问题映射到 Task 8~13。
- [ ] 每个任务有负责人、文件、接口、测试、验证和回滚。
- [ ] 未擅自确定迁移工具、CI 平台、云服务和 qPCR 医学/检疫规则。
- [ ] 不包含手改生成产物或在服务器直接修源码的步骤。
- [ ] 每项部署均要求备份、健康检查、冒烟和回滚记录。
- [ ] 研究能力与近期整改明确分离。
## 5. 交付节奏建议
- 每个 Task 一个独立分支/PR;分支前缀使用 `codex/` 或团队既有规范。
- Task 3~7 每完成一项立即做针对性安全/业务回归,不等待大版本集中验证。
- 每周更新追踪矩阵和风险清单;只汇报有证据的状态变化。
- Wave 1 完成后再开始 Wave 3 业务扩展;P0 未关闭时不部署新增研究功能。
- 每次评审先看测试失败/通过证据,再看代码;每次发布先看回滚点,再执行覆盖操作。