917 lines
35 KiB
Markdown
917 lines
35 KiB
Markdown
# 蚕病智能防控平台整改实施计划
|
||
|
||
> **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 | 决策与基线 | 0~1 | 2~3 人日 |
|
||
| Wave 1 | P0 安全与正确性 | 2~7 | 10~15 人日 |
|
||
| Wave 2 | 工程可靠性 | 8~9 | 6~10 人日 |
|
||
| Wave 3 | 业务闭环 | 10~12 | 20~33 人日 |
|
||
| Wave 4 | 验收与发布 | 13~14 | 7~11 人日 |
|
||
|
||
工期只用于排期,不是验收标准。多人并行时,任务 2 是涉及数据库实体任务的前置;任务 3~7 可在接口边界明确后分支并行,但合并前必须统一回归。
|
||
|
||
### 1.2 建议责任域
|
||
|
||
- 后端负责人:任务 2~10、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.5~1 人日
|
||
|
||
**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 1~4 写入 `后续工作计划.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:** 1~2 人日
|
||
|
||
**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 0;Web 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:** 2~3 人日
|
||
|
||
**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:** 1~2 人日
|
||
|
||
**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:** 2~3 人日
|
||
|
||
**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:** 2~3 人日
|
||
|
||
**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-002~004、RISK-001~003
|
||
**Owner:** AI + Go 后端负责人
|
||
**Estimated effort:** 2~3 人日
|
||
|
||
**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-003~005
|
||
**Owner:** 后端负责人 + 领域专家
|
||
**Estimated effort:** 1~2 人日
|
||
|
||
**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:** 3~5 人日
|
||
|
||
**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:** 5~8 人日
|
||
|
||
**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: 检测有效阳性或专家确认后创建 DiseaseEvent,TraceRecord 改为关联 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:** 4~6 人日
|
||
|
||
**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:** 4~6 人日
|
||
|
||
**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-003~005、EXP-002~005、KB-001/002、ANA-001/002
|
||
**Owner:** 后端 + Web + 领域专家 + 测试负责人
|
||
**Estimated effort:** 5~8 人日
|
||
|
||
**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.1~9.3、10.2
|
||
**Owner:** 运维 + 后端 + 测试负责人
|
||
**Estimated effort:** 4~6 人日
|
||
|
||
**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 第 10~12 章
|
||
**Owner:** 测试负责人 + 项目负责人
|
||
**Estimated effort:** 3~5 人日
|
||
|
||
**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 1~3 的每个需求 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 2~7。
|
||
- [ ] 业务可靠性和治理问题映射到 Task 8~13。
|
||
- [ ] 每个任务有负责人、文件、接口、测试、验证和回滚。
|
||
- [ ] 未擅自确定迁移工具、CI 平台、云服务和 qPCR 医学/检疫规则。
|
||
- [ ] 不包含手改生成产物或在服务器直接修源码的步骤。
|
||
- [ ] 每项部署均要求备份、健康检查、冒烟和回滚记录。
|
||
- [ ] 研究能力与近期整改明确分离。
|
||
|
||
## 5. 交付节奏建议
|
||
|
||
- 每个 Task 一个独立分支/PR;分支前缀使用 `codex/` 或团队既有规范。
|
||
- Task 3~7 每完成一项立即做针对性安全/业务回归,不等待大版本集中验证。
|
||
- 每周更新追踪矩阵和风险清单;只汇报有证据的状态变化。
|
||
- Wave 1 完成后再开始 Wave 3 业务扩展;P0 未关闭时不部署新增研究功能。
|
||
- 每次评审先看测试失败/通过证据,再看代码;每次发布先看回滚点,再执行覆盖操作。
|