16 KiB
| title | type | status | date | origin |
|---|---|---|---|---|
| chore: GEO Tech Debt Cleanup Sprint | chore | active | 2026-06-01 | docs/brainstorms/2026-06-01-geo-tech-debt-cleanup-requirements.md |
Summary
分三批清理 GEO 平台技术债:Batch 1 修复 28 个模型文件中 68 个缺失 DateTime(timezone=True) 的 datetime 列(monitoring_record.py 是废弃文件不修);Batch 2 统一前端 2 个页面组件的 API 客户端调用并扩展 fetchWithAuth 支持非 JSON 响应;Batch 3 完成前端端到端验证和部署安全加固。
Problem Frame
Plan 004 端到端验证暴露了 asyncpg 严格时区检查的系统性问题。当前仅修复了变现闭环涉及的 4 个核心表,其余 28 个模型文件的 68 个 datetime 列仍是确定的运行时炸弹——任何写入 datetime.now(UTC) 到未标记 timezone 的列都会直接报错。同时前端 2 个页面绕过统一 API 客户端,认证 token 不被附加导致 401。Docker 部署从未验证成功,Redis/PostgreSQL 安全配置缺失。这些技术债不清理,系统无法真正上线。
Requirements
时区修复(Batch 1)
R1. 28 个模型文件的 68 个 datetime 列添加 DateTime(timezone=True),PostgreSQL 对应列类型改为 TIMESTAMPTZ
R2. 时区修复按 API 调用路径分三批执行:核心变现路径 → Agent 框架路径 → 辅助路径
R3. 每批修复后验证对应 API 路径可正常写入和读取 datetime 数据
R4. 生成对应的 Alembic 迁移脚本
R5. 删除废弃的 monitoring_record.py 文件(无任何代码引用,与 monitoring.py 定义同名表但字段不同)
前端 API 客户端统一(Batch 2)
R6. reports/page.tsx 的 CSV 导出改用统一 API 客户端
R7. lifecycle/new/page.tsx 的项目创建改用统一 API 客户端
R8. 统一 API 客户端支持非 JSON 响应(blob/PDF 导出)
前端端到端验证(Batch 3)
R9. 浏览器中可完成完整变现闭环:注册→登录→创建品牌→诊断→查看健康分→付费墙→支付→解锁
R10. 公开健康分页面无需登录即可访问
R11. Onboarding 流程在浏览器中可正常走通
部署安全加固(Batch 3)
R12. Redis 配置密码保护
R13. PostgreSQL 默认弱密码更换为强密码
R14. 创建 .env.production 模板
R15. Docker Compose 部署验证通过
Key Technical Decisions
KTD1. 时区修复按 API 调用路径分批,而非一次性全量迁移。 68 列同时 ALTER TABLE 风险高,按实际调用路径修复风险可控。核心变现路径(brand、query、content 等)最优先,因为这是用户最可能触发的路径。
KTD2. monitoring_record.py 是废弃文件,删除而非修复。 代码引用分析确认所有 import 都指向 monitoring.py,monitoring_record.py 无任何引用。两个文件定义同名表但字段结构不同,保留 monitoring.py(更完整,有 relationship 和 user_id/query_id 外键)。
KTD3. 扩展 fetchWithAuth 支持非 JSON 响应,而非新增独立函数。 reports.ts 的 PDF blob 导出被迫绕过统一客户端,因为 fetchWithAuth 只返回 JSON。扩展一个 responseType 参数比新增 fetchWithAuthBlob 更符合 DRY 原则,且对现有调用方无侵入。
KTD4. Alembic 迁移按 Batch 生成,而非一个大迁移。 每个 Batch 生成一个迁移文件,便于回滚和增量部署。Batch 1 因模型数量多可能需要 2-3 个迁移文件。
Implementation Units
U1. Batch 1a: 核心变现路径时区修复
- Goal: 修复核心变现路径涉及的模型文件 datetime 列,确保品牌创建、查询、内容管理、GEO 计划、建议生成等 API 不再触发 asyncpg 时区错误
- Requirements: R1, R2, R3, R4
- Dependencies: none
- Files:
backend/app/models/brand.py— 4 列(last_queried_at, next_query_at, created_at, updated_at)backend/app/models/query.py— 4 列(last_queried_at, next_query_at, created_at, updated_at)backend/app/models/citation_record.py— 1 列(queried_at)backend/app/models/attribution_record.py— 4 列(published_at, window_end_at, created_at, updated_at)backend/app/models/content.py— 4 列(Content: created_at, updated_at; ContentVersion: created_at; ContentReview: created_at)backend/app/models/geo_plan.py— 5 列(GeoPlan: created_at, updated_at; GeoPlanAction: completed_at, created_at, updated_at)backend/app/models/suggestion.py— 2 列(generated_at, updated_at)backend/app/models/competitor.py— 1 列(created_at)backend/app/models/competitor_insight.py— 2 列(created_at, updated_at)backend/app/models/distribution.py— 2 列(created_at, updated_at)backend/app/models/brand_knowledge.py— 3 列(BrandKnowledge: created_at, updated_at; Keyword: created_at)
- Approach: 每个文件添加
DateTimeimport(如缺失),将所有Mapped[datetime]列的mapped_column()添加DateTime(timezone=True)参数。对于已有DateTime但无timezone=True的列(如 brand.py 的 last_queried_at),改为DateTime(timezone=True)。修复后启动后端服务,通过 curl 验证品牌创建和查询 API 的 datetime 读写正常。 - Patterns to follow: 已修复的 4 个核心表(diagnosis_record.py, payment_order.py, subscription.py, user.py)的修改模式
- Test scenarios:
- 品牌创建 API 返回的 created_at 包含时区信息
- 查询品牌列表 API 返回的 datetime 字段包含时区信息
- 创建 GEO 计划后 completed_at 可写入 timezone-aware datetime
- attribution_record 的 published_at 和 window_end_at 可写入 timezone-aware datetime
- Verification: 启动后端服务,通过 curl 调用品牌创建、查询、内容管理 API,确认 datetime 读写无 asyncpg 时区错误
U2. Batch 1b: Agent 框架路径时区修复
- Goal: 修复 Agent 框架和监控相关模型文件的 datetime 列
- Requirements: R1, R2, R3, R4
- Dependencies: U1
- Files:
backend/app/models/agent.py— 9 列(AgentRegistry: last_heartbeat, created_at, updated_at; AgentConfig: updated_at; AgentTask: scheduled_at, started_at, completed_at, created_at; AgentTaskLog: created_at)backend/app/models/detection_task.py— 4 列(last_run_at, next_run_at, created_at, updated_at)backend/app/models/monitoring.py— 5 列(MonitoringRecord: last_checked_at, next_check_at, created_at, updated_at; ContentBaseline: recorded_at)backend/app/models/trend_insight.py— 4 列(period_start, period_end, created_at, updated_at)backend/app/models/query_task.py— 3 列(scheduled_at, started_at, completed_at)backend/app/models/usage_record.py— 2 列(timestamp, created_at)backend/app/models/api_key.py— 3 列(last_verified_at, created_at, updated_at)
- Approach: 同 U1 模式。注意
detection_task.py的next_run_at有default=lambda: datetime.now(timezone.utc),这已经是 timezone-aware 的,但列类型仍是DateTime(无 timezone),需要改为DateTime(timezone=True)。 - Patterns to follow: U1 的修改模式
- Test scenarios:
- Agent 注册时 last_heartbeat 可写入 timezone-aware datetime
- AgentTask 的 scheduled_at、started_at、completed_at 可写入 timezone-aware datetime
- detection_task 的 next_run_at 默认值写入不触发时区错误
- monitoring_record 的 last_checked_at、next_check_at 可写入 timezone-aware datetime
- Verification: 启动后端服务,通过 curl 调用 Agent 相关 API,确认 datetime 读写无错误
U3. Batch 1c: 辅助路径时区修复 + 废弃文件清理
- Goal: 修复剩余辅助路径模型文件的 datetime 列,删除废弃的 monitoring_record.py
- Requirements: R1, R2, R3, R4, R5
- Dependencies: U2
- Files:
backend/app/models/knowledge.py— 6 列(KnowledgeBase: created_at, updated_at; KnowledgeDocument: created_at, updated_at; KnowledgeChunk: created_at; KnowledgeSearchLog: created_at)backend/app/models/knowledge_graph.py— 3 列(KnowledgeEntity: created_at, updated_at; KnowledgeRelation: created_at)backend/app/models/organization.py— 3 列(Organization: created_at, updated_at; OrgMember: joined_at)backend/app/models/lifecycle.py— 4 列(LifecycleProject: created_at, updated_at; ProjectStage: started_at, completed_at)backend/app/models/alert.py— 1 列(created_at)backend/app/models/alert_setting.py— 2 列(created_at, updated_at)backend/app/models/platform_rule.py— 1 列(updated_at)backend/app/models/platform_rule_version.py— 1 列(created_at)backend/app/models/schema_suggestion.py— 2 列(created_at, updated_at)backend/app/models/monitoring_record.py— 删除整个文件backend/app/models/__init__.py— 移除 monitoring_record 的 import(如有)
- Approach: 同 U1 模式。删除
monitoring_record.py前确认__init__.py无引用(已验证无任何代码 import 此文件)。删除后检查__init__.py是否有相关 import 需清理。 - Patterns to follow: U1 的修改模式
- Test scenarios:
- knowledge 相关 API 的 created_at/updated_at 可写入 timezone-aware datetime
- lifecycle project 的 started_at/completed_at 可写入 timezone-aware datetime
- 删除 monitoring_record.py 后后端服务正常启动,无 import 错误
- Verification: 启动后端服务,确认无 import 错误;调用 knowledge 和 lifecycle API 验证 datetime 读写
U4. Alembic 迁移生成与执行
- Goal: 为 U1-U3 的所有模型变更生成 Alembic 迁移脚本,并在本地数据库执行
- Requirements: R4
- Dependencies: U1, U2, U3
- Files:
backend/alembic/versions/— 新增迁移文件
- Approach: 运行
alembic revision --autogenerate生成迁移。检查生成的迁移脚本确认所有 ALTER COLUMN 操作正确(TIMESTAMP → TIMESTAMPTZ)。执行迁移后验证数据库列类型已更新。如果项目未配置 Alembic,则通过init_schema.py或手动 SQL 完成数据库更新。 - Patterns to follow: 已有的 Alembic 迁移文件(如存在)
- Test scenarios:
- 迁移脚本可成功执行,无错误
- 迁移后数据库列类型为 TIMESTAMPTZ
- 迁移可回滚(downgrade)
- Verification: 执行迁移,检查数据库列类型
U5. 前端 API 客户端统一
- Goal: 统一前端 2 个页面组件的 API 调用,扩展 fetchWithAuth 支持非 JSON 响应
- Requirements: R6, R7, R8
- Dependencies: none(可与 U1-U4 并行)
- Files:
frontend/lib/api/client.ts— 扩展 fetchWithAuth 支持 responseType 参数frontend/app/(dashboard)/dashboard/reports/page.tsx— CSV 导出改用 fetchWithAuthfrontend/app/(dashboard)/dashboard/lifecycle/new/page.tsx— 项目创建改用 fetchWithAuthfrontend/lib/api/reports.ts— PDF blob 导出改用 fetchWithAuth(responseType: 'blob')
- Approach: 在
fetchWithAuth中添加可选responseType参数,默认'json',当为'blob'时返回Response对象而非解析 JSON。修改 reports/page.tsx 和 lifecycle/new/page.tsx 使用fetchWithAuth替代手动fetch。修改 reports.ts 使用fetchWithAuth的 blob 模式。 - Patterns to follow:
frontend/lib/api/client.ts现有的 fetchWithAuth 实现 - Test scenarios:
- fetchWithAuth 默认行为不变(返回 JSON)
- fetchWithAuth responseType='blob' 返回 Response 对象
- reports 页面 CSV 导出认证 token 正确传递
- lifecycle/new 页面项目创建认证 token 正确传递
- PDF 导出通过 fetchWithAuth blob 模式正常工作
- Verification: 前端构建通过,浏览器中访问 reports 和 lifecycle/new 页面无 401 错误
U6. 前端端到端验证
- Goal: 在浏览器中验证完整变现闭环和关键用户流程
- Requirements: R9, R10, R11
- Dependencies: U4, U5
- Files:
- 无代码修改,纯验证
- Approach: 启动前后端服务,在浏览器中手动走通完整变现闭环。重点验证:注册→登录→Onboarding→创建品牌→诊断→健康分→付费墙→支付→解锁。同时验证公开健康分页面无需登录可访问。
- Test scenarios:
- 完整变现闭环:注册→登录→创建品牌→诊断→健康分→付费墙→支付→解锁
- 公开健康分页面无需登录可访问并生成报告
- Onboarding 流程可正常走通
- reports 页面 CSV 导出正常
- lifecycle/new 页面项目创建正常
- Verification: 所有流程在浏览器中走通,无 401、无页面报错
U7. 部署安全加固
- Goal: Redis 密码保护、PostgreSQL 强密码、.env.production 模板、Docker Compose 部署验证
- Requirements: R12, R13, R14, R15
- Dependencies: U4
- Files:
docker-compose.yml— Redis 添加密码配置、PostgreSQL 密码更新backend/.env— Redis 密码和 PostgreSQL 密码同步更新backend/.env.production— 新建生产环境配置模板docker-compose.prod.yml— 更新生产环境配置(如存在)
- Approach: 在 docker-compose.yml 中为 Redis 添加
--requirepass命令和环境变量。PostgreSQL 密码从弱密码geo123改为强密码。创建.env.production模板包含所有必需配置项。后端代码中 Redis 连接需同步添加密码参数。执行docker compose up验证 4 个服务正常启动和通信。 - Patterns to follow: docker-compose.yml 现有配置结构
- Test scenarios:
- Redis 无密码连接被拒绝
- Redis 有密码连接成功
- PostgreSQL 弱密码连接被拒绝
- PostgreSQL 强密码连接成功
- Docker Compose 4 个服务正常启动
- 后端服务可连接 Redis 和 PostgreSQL
- Verification:
docker compose up成功,4 个服务健康检查通过,后端 API 可正常响应
Scope Boundaries
In scope:
- 28 个模型文件的 DateTime(timezone=True) 修复
- 废弃文件 monitoring_record.py 删除
- Alembic 迁移生成和执行
- 前端 2 个页面组件 API 客户端统一
- 统一 API 客户端非 JSON 响应支持
- 浏览器端到端验证
- Docker Compose 部署验证
- Redis/PostgreSQL 安全配置
- .env.production 模板
Deferred for later:
- 真实微信/支付宝 SDK 接入
- CI/CD 流水线
- 性能优化和压力测试
- 生产环境域名和 HTTPS 配置
- 完整测试覆盖
- pgvector 镜像优化
- JWT_SECRET 强密钥生成
- UI 打磨和视觉优化
Outside this sprint:
- 新功能开发
- 代码重构(除时区修复和废弃文件删除外)
Risks & Dependencies
- Alembic 未配置或配置不完整。 如果项目未正确配置 Alembic,自动迁移生成可能失败。回退方案:通过
init_schema.py或手动 SQL 完成 ALTER COLUMN。 - Docker Hub 网络问题。 Plan 004 中 Docker 部署因网络问题失败,U7 可能遇到同样问题。回退方案:配置 Docker 镜像源或使用本地构建。
- 前端构建可能暴露其他问题。 前端代码从未在浏览器中完整验证,E2E 验证可能发现新的 bug,这些 bug 需要额外修复。
- Redis 密码变更影响后端连接。 后端代码中 Redis 连接配置需同步更新,否则服务启动失败。
Sources / Research
- Plan 004 端到端验证记录:时区 bug 在 diagnosis_records 和 payment_orders 上的具体表现
backend/app/models/— 28 个待修复模型文件,68 个 datetime 列backend/app/models/monitoring.py— 实际使用的监控模型(被 4 处代码引用)backend/app/models/monitoring_record.py— 废弃文件(零引用)frontend/lib/api/client.ts— 统一 API 客户端frontend/app/(dashboard)/dashboard/reports/page.tsx— 绕过统一客户端frontend/app/(dashboard)/dashboard/lifecycle/new/page.tsx— 绕过统一客户端frontend/lib/api/reports.ts— PDF blob 导出被迫绕过docker-compose.yml— Redis 无密码、PostgreSQL 弱密码