chore: Plan 004 - launch readiness sprint (timezone fixes, health check, JWT secret)

This commit is contained in:
chiguyong 2026-06-01 20:35:56 +08:00
parent 3bd848ee36
commit 4f86f2bd62
9 changed files with 424 additions and 10 deletions

View File

@ -2,5 +2,6 @@ DATABASE_URL=sqlite+aiosqlite:///./test.db
REDIS_URL=redis://localhost:6379 REDIS_URL=redis://localhost:6379
ENVIRONMENT=testing ENVIRONMENT=testing
LOG_LEVEL=info LOG_LEVEL=info
JWT_SECRET=test-jwt-secret-for-testing-at-least-32-characters-long
SECRET_KEY=test-secret-key-for-testing-only SECRET_KEY=test-secret-key-for-testing-only
CORS_ORIGINS=http://localhost:3000 CORS_ORIGINS=http://localhost:3000

View File

@ -39,7 +39,7 @@ EXPOSE 8000
# 健康检查 # 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -f http://localhost:8000/api/health || exit 1 CMD curl -f http://localhost:8000/health || exit 1
CMD ["gunicorn", "app.main:app", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", \ CMD ["gunicorn", "app.main:app", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", \
"--bind", "0.0.0.0:8000", "--timeout", "120", "--access-logfile", "-"] "--bind", "0.0.0.0:8000", "--timeout", "120", "--access-logfile", "-"]

View File

@ -1,7 +1,7 @@
import uuid import uuid
from datetime import datetime from datetime import datetime
from sqlalchemy import String, Uuid, JSON, Float, Text, ForeignKey, Index, func from sqlalchemy import String, Uuid, JSON, Float, Text, DateTime, ForeignKey, Index, func
from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.orm import Mapped, mapped_column
from app.database import Base from app.database import Base
@ -28,9 +28,9 @@ class DiagnosisRecord(Base):
error_message: Mapped[str | None] = mapped_column(Text, nullable=True) error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
collection_metadata: Mapped[dict | None] = mapped_column(JSON, nullable=True) collection_metadata: Mapped[dict | None] = mapped_column(JSON, nullable=True)
created_at: Mapped[datetime] = mapped_column( created_at: Mapped[datetime] = mapped_column(
server_default=func.now(), nullable=False DateTime(timezone=True), server_default=func.now(), nullable=False
) )
completed_at: Mapped[datetime | None] = mapped_column(nullable=True) completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
__table_args__ = ( __table_args__ = (
Index("idx_diagnosis_records_brand_id", "brand_id"), Index("idx_diagnosis_records_brand_id", "brand_id"),

View File

@ -29,12 +29,14 @@ class PaymentOrder(Base):
pay_url: Mapped[str | None] = mapped_column(String(1024), nullable=True) pay_url: Mapped[str | None] = mapped_column(String(1024), nullable=True)
callback_data: Mapped[dict | None] = mapped_column(JSONType, nullable=True) callback_data: Mapped[dict | None] = mapped_column(JSONType, nullable=True)
created_at: Mapped[datetime] = mapped_column( created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(), server_default=func.now(),
nullable=False, nullable=False,
) )
updated_at: Mapped[datetime] = mapped_column( updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(), server_default=func.now(),
onupdate=func.now(), onupdate=func.now(),
nullable=False, nullable=False,
) )
paid_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) paid_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

View File

@ -1,7 +1,7 @@
import uuid import uuid
from datetime import datetime, date from datetime import datetime, date
from sqlalchemy import String, ForeignKey, Numeric, func from sqlalchemy import String, ForeignKey, Numeric, DateTime, func
from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.orm import Mapped, mapped_column, relationship
@ -29,6 +29,7 @@ class Subscription(Base):
payment_method: Mapped[str | None] = mapped_column(String(50), nullable=True) payment_method: Mapped[str | None] = mapped_column(String(50), nullable=True)
payment_id: Mapped[str | None] = mapped_column(String(255), nullable=True) payment_id: Mapped[str | None] = mapped_column(String(255), nullable=True)
created_at: Mapped[datetime] = mapped_column( created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(), server_default=func.now(),
nullable=False, nullable=False,
) )

View File

@ -22,13 +22,13 @@ class User(Base):
isActive: Mapped[bool] = mapped_column(Boolean, default=True) isActive: Mapped[bool] = mapped_column(Boolean, default=True)
emailVerified: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) emailVerified: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
phoneVerified: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) phoneVerified: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
lastLoginAt: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) lastLoginAt: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
createdAt: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) createdAt: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime, default=func.now(), onupdate=func.now(), nullable=False) updatedAt: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=func.now(), onupdate=func.now(), nullable=False)
mfaSecret: Mapped[str | None] = mapped_column(Text, nullable=True) mfaSecret: Mapped[str | None] = mapped_column(Text, nullable=True)
mfaEnabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) mfaEnabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
loginAttempts: Mapped[int] = mapped_column(Integer, default=0, nullable=False) loginAttempts: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
lockedUntil: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) lockedUntil: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
organization_id: Mapped[uuid.UUID | None] = mapped_column(Uuid(as_uuid=True), ForeignKey("organizations.id", ondelete="SET NULL"), nullable=True) organization_id: Mapped[uuid.UUID | None] = mapped_column(Uuid(as_uuid=True), ForeignKey("organizations.id", ondelete="SET NULL"), nullable=True)
role: Mapped[str] = mapped_column(String(20), server_default="owner", nullable=False) role: Mapped[str] = mapped_column(String(20), server_default="owner", nullable=False)
plan: Mapped[str] = mapped_column(String(20), server_default="free", nullable=False) plan: Mapped[str] = mapped_column(String(20), server_default="free", nullable=False)

View File

@ -56,6 +56,9 @@ services:
- "8000:8000" - "8000:8000"
env_file: env_file:
- .env - .env
environment:
DATABASE_URL: postgresql+asyncpg://postgres:postgres123@db:5432/geo_platform
REDIS_URL: redis://redis:6379/0
volumes: volumes:
- ./backend:/app - ./backend:/app
depends_on: depends_on:

View File

@ -0,0 +1,109 @@
---
date: "2026-06-01"
topic: geo-launch-readiness
---
## Summary
GEO 平台变现闭环代码已全部落地Plan 003但从未端到端运行过。本需求定义一个"部署+验证"冲刺:先将系统部署到类生产环境,再在部署环境中验证完整变现闭环(注册→诊断→健康分→付费墙→支付→解锁),直到新用户可完成全链路操作。
## Problem Frame
Plan 003 完成了 9 个实施单元的代码编写——诊断数据采集、免费健康分页面、Onboarding 重设计、支付集成、AI 内容生成、效果归因、邮件集成、契约测试、E2E 烟雾测试。但这些代码从未作为完整系统运行过。后端服务能否启动、前端能否构建、数据库迁移能否执行、前后端联调是否通畅——全部未知。在代码从未跑过的状态下,任何单点修复都是盲目的;只有让系统先跑起来,才能发现真正的阻塞问题。
## Key Decisions
**部署优先于功能验证。** 本地跑通不等于可上线。部署问题数据库迁移、环境变量、CORS、静态资源往往是上线前最大的意外。先解决部署再在部署环境中验证流程一步到位。
**支付用 mock 模式验证。** 真实微信/支付宝 SDK 接入涉及商户审核、证书配置,是独立的长期工作。本次验证用 mock 支付确认付费墙逻辑正确即可,真实支付接入作为后续计划。
**阻塞驱动,不追求完美。** 遇到问题修到能继续往下走即可,不做全面重构或优化。目标是跑通闭环,不是打磨每个细节。
## Requirements
**部署就绪**
R1. 后端服务可在 Docker Compose 环境中启动,所有 API 端点可访问
R2. 前端构建产物可被部署并提供页面访问
R3. 数据库迁移可从空库执行到最新版本,所有表和索引正确创建
R4. 环境变量配置完整,第三方服务密钥缺失时优雅降级为 mock 模式
**核心流程可跑通**
R5. 新用户可完成注册并登录,获得有效 JWT
R6. 登录用户可创建品牌,触发诊断,获得非零健康分
R7. 免费用户访问付费功能时触发付费墙,显示升级提示
R8. 用户可发起支付mock 模式),支付完成后配额刷新、付费功能解锁
R9. 公开健康分页面无需注册即可访问,输入品牌名可生成报告
**端到端验证**
R10. 完整变现闭环可在部署环境中走通:注册→诊断→健康分→付费墙→支付→解锁
**代码质量2026-06-01 复盘新增)**
R11. Dockerfile 健康检查端点与实际 API 端点一致(当前 `/api/health` 不存在,应为 `/health`
R12. 测试环境配置完整,`.env.test` 包含必需的 JWT_SECRET
R13. 前端页面统一使用 API 客户端,认证 token 正确传递reports、lifecycle/new 页面绕过了统一客户端)
## Key Flows
- F1. 完整变现闭环验证
- **Trigger:** 新用户访问平台
- **Steps:** 注册账号 → 登录 → 创建品牌 → 触发诊断 → 查看非零健康分 → 尝试付费功能 → 触发付费墙 → 发起 mock 支付 → 支付完成 → 付费功能解锁
- **Outcome:** 用户完成从获客到付费的完整链路
- **Covers:** R5, R6, R7, R8, R10
- F2. 公开健康分获客路径
- **Trigger:** 未注册用户访问公开健康分页面
- **Steps:** 输入品牌名 → 系统生成 GEO 健康分报告 → 显示关键指标和问题 → 引导注册查看完整报告
- **Outcome:** 用户被引导进入注册流程
- **Covers:** R9, R10
## Scope Boundaries
**In scope:**
- Docker Compose 部署配置
- 数据库迁移执行
- 核心流程端到端验证
- 阻塞问题修复
**Deferred for later:**
- 真实微信/支付宝 SDK 接入
- CI/CD 流水线
- 性能优化和压力测试
- 生产环境域名和 HTTPS
- 完整测试覆盖
**Outside this sprint:**
- UI 打磨和视觉优化
- 新功能开发
- 代码重构
## Dependencies / Assumptions
- PostgreSQL 15 + pgvector 扩展可用Docker 容器已配置)
- Redis 7 可用(会话和缓存)
- 第三方 API 密钥DeepSeek、OpenAI 等)可能未配置,系统需支持 mock 降级
- LLM API 调用成本可控(诊断和内容生成会消耗 token
## Outstanding Questions
**Resolved:**
- Docker Compose 配置已完整,包含 db (PostgreSQL 15) + redis (Redis 7) + backend (FastAPI) + frontend (Next.js) 四个服务,开发和生产两套配置均就绪
- 数据库迁移策略:使用 `create_all` + `stamp head`KTD2
- 前端部署方式:独立容器,开发模式用 `npm run dev`,生产模式用 standalone `node server.js`
- pgvector 扩展安装:通过 init-db.sh 从源码编译安装KTD3
- 验证方式本地裸跑优先Docker 部署后续KTD6
**Open:**
- 前端 reports 和 lifecycle/new 页面绕过统一 API 客户端,是否在本次修复?(当前标记为 deferred

View File

@ -0,0 +1,298 @@
---
title: "chore: GEO Platform Launch Readiness Sprint"
type: chore
status: active
date: "2026-06-01"
origin: docs/brainstorms/2026-06-01-geo-launch-readiness-requirements.md
---
## Summary
修复部署阻塞问题,启动服务,端到端验证完整变现闭环(注册→诊断→健康分→付费墙→支付→解锁),使 GEO 平台达到可上线状态。
## Problem Frame
Plan 003 完成了 9 个实施单元的代码编写,但这些代码从未作为完整系统运行过。研究发现 4 个 P0 阻塞问题导致服务无法启动JWT_SECRET 长度不足、DATABASE_URL 驱动不匹配、根目录 .env 缺失、pgvector 扩展未安装。在代码从未跑过的状态下,任何单点修复都是盲目的;只有让系统先跑起来,才能发现真正的集成问题。
## Requirements
**部署就绪**
R1. 后端服务可在 Docker Compose 环境中启动,所有 API 端点可访问
R2. 前端构建产物可被部署并提供页面访问
R3. 数据库迁移可从空库执行到最新版本,所有表和索引正确创建
R4. 环境变量配置完整,第三方服务密钥缺失时优雅降级为 mock 模式
**核心流程可跑通**
R5. 新用户可完成注册并登录,获得有效 JWT
R6. 登录用户可创建品牌,触发诊断,获得非零健康分
R7. 免费用户访问付费功能时触发付费墙,显示升级提示
R8. 用户可发起支付mock 模式),支付完成后配额刷新、付费功能解锁
R9. 公开健康分页面无需注册即可访问,输入品牌名可生成报告
**端到端验证**
R10. 完整变现闭环可在部署环境中走通:注册→诊断→健康分→付费墙→支付→解锁
**代码质量**
R11. Dockerfile 健康检查端点与实际 API 端点一致
R12. 测试环境配置完整,测试可正常启动
R13. 前端页面统一使用 API 客户端,认证 token 正确传递
## Key Technical Decisions
KTD1. **DATABASE_URL 使用 asyncpg 驱动。** `database.py` 使用 `create_async_engine`,必须用 `postgresql+asyncpg://` 而非 `postgresql+psycopg://`。当前 `backend/.env` 中的值使用了错误的同步驱动。
KTD2. **数据库初始化使用 create_all + stamp head。** Alembic 迁移链存在顺序问题alerts 表引用 brands 但 brands 在更晚的迁移中创建),直接 `alembic upgrade head` 会失败。先用 `Base.metadata.create_all()` 创建所有表,再用 `alembic stamp head` 标记版本。这与前序会话中验证过的策略一致。
KTD3. **pgvector 通过 PostgreSQL 初始化脚本安装。** 在 Docker Compose 中为 db 服务添加初始化脚本,从源码编译安装 pgvector 扩展。避免构建自定义 PostgreSQL 镜像的复杂性。
KTD4. **支付/分发/邮件保持 mock 模式。** 本次验证目标是确认付费墙逻辑正确,真实 SDK 接入是后续工作。`PAYMENT_MODE=mock`、`DISTRIBUTION_MODE=mock`、`EMAIL_MODE=mock`。
KTD5. **开发环境优先于生产环境。** 先用 `docker-compose.yml`(开发模式)验证,确认跑通后再配置 `docker-compose.prod.yml`。开发模式挂载源码目录便于调试。
KTD6. **本地裸跑优先于 Docker 部署。** Docker Hub 网络问题导致镜像构建失败,先用本地直接运行后端/前端验证核心流程Docker 部署作为后续工作。
KTD7. **验证驱动修复。** 手动走完注册→诊断→支付全链路,每步发现问题就修,不做预防性重构。
## Implementation Units
### U1. Fix environment configuration blockers ✅
**Goal:** 修复所有 P0 阻塞问题,使服务可以启动
**Requirements:** R4
**Status:** Completed (previous session)
**What was done:**
- `backend/.env` DATABASE_URL 改为 `postgresql+asyncpg://`
- `JWT_SECRET` 改为 43 字符
- 从 `.env.example` 创建根目录 `.env`
- `.gitignore` 确认排除 `.env`
---
### U2. Database setup with pgvector and migrations ✅
**Goal:** PostgreSQL 容器安装 pgvector 扩展,数据库表结构完整创建
**Requirements:** R3
**Status:** Completed (previous session)
**What was done:**
- 创建 `backend/init-db.sh` pgvector 初始化脚本
- `docker-compose.yml` 挂载初始化脚本
- 创建 `backend/init_schema.py` 使用 create_all + stamp head
- 数据库表和 pgvector 扩展已验证存在
---
### U3. Service startup and health verification 🔄
**Goal:** 后端和前端服务均可启动并通过健康检查
**Requirements:** R1, R2
**Status:** Partially completed (previous session)
**What was done:**
- 后端本地启动成功:`uvicorn app.main:app --host 0.0.0.0 --port 8000`
- 后端健康检查通过:`/health` → healthy, `/ready` → database:ok, redis:ok
- 前端本地启动成功:`npm run dev` on port 3001
**Remaining:**
- 验证前端页面可访问(`curl http://localhost:3001`
- 验证前端可调用后端 API无 CORS 错误)
---
### U3.5. Fix audit-discovered P0 issues
**Goal:** 修复复盘发现的 P0 阻塞问题
**Requirements:** R11, R12
**Dependencies:** none (可并行)
**Files:**
- `backend/Dockerfile` — 修复健康检查端点 `/api/health``/health`
- `backend/.env.test` — 添加 JWT_SECRET
**Approach:**
1. 修改 Dockerfile HEALTHCHECK 端点从 `/api/health` 改为 `/health`
2. 在 `.env.test` 中添加 `JWT_SECRET=test-jwt-secret-for-testing-at-least-32-characters-long`
**Test scenarios:**
- Dockerfile 健康检查端点与 FastAPI 注册的 `/health` 一致
- `pytest` 可正常启动(不因 JWT_SECRET 缺失而 sys.exit
**Verification:** Dockerfile HEALTHCHECK CMD 正确,测试可运行
---
### U4. Authentication flow verification
**Goal:** 新用户可完成注册、登录、获取 JWT
**Requirements:** R5
**Dependencies:** U3
**Files:**
- `backend/app/api/auth.py` — 认证 API
- `backend/app/services/auth.py` — 认证服务
- `backend/app/models/user.py` — User 模型
- `frontend/app/(auth)/` — 前端认证页面
**Known issues:**
- 前端部分页面绕过统一 API 客户端reports、lifecycle/new认证 token 可能不被附加
**Approach:**
1. 启动后端和前端服务
2. 通过 API 注册新用户:`POST /api/v1/auth/register`
3. 验证用户数据正确写入数据库plan=free, max_queries=5
4. 登录获取 JWT token`POST /api/v1/auth/login`
5. 用 JWT token 调用受保护 API`GET /api/v1/brands`
6. 修复认证流程中的任何错误
**Test scenarios:**
- `POST /api/v1/auth/register` 创建用户成功,返回 201
- `POST /api/v1/auth/login` 返回 JWT token
- `GET /api/v1/brands` 携带 JWT 返回 200非 401
- 新用户 plan 字段为 "free"max_queries 为 5
**Verification:** 新用户可完成注册→登录→访问受保护资源
---
### U5. Diagnosis and health score verification
**Goal:** 用户可创建品牌、触发诊断、获得非零健康分;公开健康分页面可访问
**Requirements:** R6, R9
**Dependencies:** U4
**Files:**
- `backend/app/api/diagnosis.py` — 诊断 API
- `backend/app/api/health_score.py` — 公开健康分 API
- `backend/app/services/diagnosis/` — 诊断服务
- `frontend/app/(public)/health-score/` — 公开健康分页面
- `frontend/app/(dashboard)/onboarding/` — Onboarding 页面
**Known risks:**
- 诊断依赖 DeepSeek API如果 API Key 无效或额度耗尽,诊断会返回空结果
- 诊断是异步流程,可能需要轮询等待结果
**Approach:**
1. 登录用户创建品牌
2. 触发诊断验证数据采集流程AI 平台查询 + CitationRecord 分析)
3. 查看诊断结果,确认健康分为非零值
4. 访问公开健康分页面,输入品牌名生成报告
5. 修复诊断流程中的任何错误LLM API 调用失败、数据采集空结果等)
**Test scenarios:**
- `POST /api/v1/brands` 创建品牌成功
- `POST /api/v1/diagnosis` 触发诊断,返回诊断任务 ID
- `GET /api/v1/diagnosis/{id}` 返回非零健康分
- `GET /api/v1/public/health-score?brand={name}` 返回公开健康分报告
- 公开健康分页面无需登录即可访问
**Verification:** 用户可获得非零 GEO 健康分,公开页面可生成报告
---
### U6. Monetization closed loop verification
**Goal:** 完整变现闭环可走通——免费用户触发付费墙、mock 支付、功能解锁
**Requirements:** R7, R8, R10
**Dependencies:** U5
**Files:**
- `backend/app/middleware/subscription_enforcement.py` — 订阅限制中间件
- `backend/app/api/payments.py` — 支付 API
- `backend/app/services/payment/` — 支付服务
- `frontend/components/subscription/` — 订阅 UI 组件
- `frontend/app/(dashboard)/dashboard/` — Dashboard 页面
**Approach:**
1. 免费用户尝试访问付费功能(如 AI 内容生成、高级诊断),验证付费墙触发
2. 验证升级提示正确显示
3. 发起 mock 支付,确认支付流程完成
4. 验证支付后用户 plan 升级、配额刷新
5. 验证付费功能解锁
6. 修复变现闭环中的任何错误
**Test scenarios:**
- 免费用户访问付费 API 返回 403 + 升级提示
- `POST /api/v1/payments/create` 创建支付订单
- Mock 支付回调后用户 plan 从 "free" 变为 "pro"
- 付费功能解锁,用户可正常使用
**Verification:** 完整变现闭环走通——注册→诊断→付费墙→支付→解锁
---
## Scope Boundaries
**In scope:**
- 修复部署阻塞问题
- 数据库初始化和迁移
- 服务启动验证
- 核心流程端到端验证
- 验证过程中发现的阻塞 bug 修复
- 审计发现的 P0 问题修复
**Deferred for later:**
- 真实微信/支付宝 SDK 接入
- CI/CD 流水线
- 性能优化和压力测试
- 生产环境域名和 HTTPS 配置
- `.env.production` 文件创建
- Redis 密码保护
- PostgreSQL 弱密码更换
- pgvector 镜像优化(改用 pgvector/pgvector:pg15
- 前端统一 API 客户端修复reports、lifecycle/new 页面)
- JWT_SECRET 强密钥生成
**Outside this sprint:**
- UI 打磨和视觉优化
- 新功能开发
- 代码重构
- 完整测试覆盖
## Risks & Dependencies
- **LLM API 可用性**:诊断和内容生成依赖 DeepSeek API如果 API Key 无效或额度耗尽,诊断会返回空结果。需确认 API Key 有效。
- **pgvector 编译时间**:从源码编译 pgvector 需要安装 build-essential 和 git首次启动数据库容器可能需要 2-3 分钟。
- **迁移链顺序问题**Alembic 迁移链可能存在未发现的顺序依赖create_all + stamp head 策略可绕过此问题。
- **前端构建问题**ESLint 警告已降级为 warn但可能存在运行时错误仅在浏览器中暴露。
- **前端 API 客户端不一致**:部分页面绕过统一 API 客户端,认证 token 可能不被附加,导致 401 错误。
- **Docker Hub 网络问题**:国内环境拉取 Docker 镜像可能超时,影响 Docker Compose 部署。
## Sources & Research
- `backend/.env` — 当前环境变量配置DATABASE_URL 驱动和 JWT_SECRET 已修复
- `backend/app/config.py` — JWT_SECRET >= 32 字符校验逻辑
- `backend/app/database.py` — async engine 配置,确认需要 asyncpg 驱动
- `backend/app/main.py` — FastAPI 入口28+ API 路由注册6 层中间件栈
- `backend/Dockerfile` — 健康检查端点错误(/api/health → /health
- `docker-compose.yml` — 开发环境 4 服务配置backend environment 覆盖
- `docker-compose.prod.yml` — 生产环境配置,依赖 `.env.production`
- 前序会话 pgvector 安装经验:从源码编译 v0.5.1
- 2026-06-01 全面复盘审计:发现 Dockerfile 健康检查、.env.test、前端 API 客户端等问题