## [ERR-20260501-005] dev 部署 `prisma db push` 撞 `type "citext" does not exist`

**日期**: 2026-05-01
**类别**: 部署 / Prisma / PostgreSQL 扩展
**严重度**: 高（阻断 develop 合并后的 dev 自动部署，影响所有后续 PR 的 dev 验证）

### 问题描述

合并 PR #208 触发 `deploy-dev.yml` 后，`(cd backend && npm install && npm run db:push)` 报：

```
Error: ERROR: type "citext" does not exist
  step=CreateTable { table_id: TableId(132) }
```

### 根本原因

PR #207（M365 休眠账号）在 `backend/prisma/schema/platform_ops_center.prisma` 引入 `userPrincipalName String @db.Citext`，并在配套 migration.sql 顶部加了：

```sql
CREATE EXTENSION IF NOT EXISTS citext;
```

但 **dev 环境部署使用的是 `prisma db push` 而不是 `prisma migrate deploy`**，见 `.gitea/workflows/deploy-dev.yml`：

```bash
echo "=== 同步 schema (db push, dev 标准做法) ==="
(cd backend && npm install && npm run db:push)

CI=true bash scripts/deploy/deploy.sh dev deploy --skip-migrate
```

**`db push` 不读 `migrations/*/migration.sql`**，它只根据 `schema.prisma` 直接 diff 当前 DB 然后下 DDL。所以 `CREATE EXTENSION citext` 那行从来不会在 dev DB 里执行。

### 为什么 #207 自己合的时候没炸

- #207 合并部署时 dev DB 里**还没有** `m365_users` 表，`db push` 第一次尝试创建带 citext 列的表 → 应该也炸过一次（建议查 1467/1463 这批 run 日志确认）。
- 也可能 #207 部署时 prisma 客户端未 regenerate / 表已通过别的途径建好（比如手工跑过 migrate deploy），扩展状态飘了。
- #208 的 schema 改动让 prisma 重新尝试 CreateTable，撞了无 citext 的雷。

### 绕行方案（已验证）

```bash
ssh ubuntu@43.153.69.73 "docker exec ffws-dev-postgres psql -U ffws_dev -d ffws_dev -c 'CREATE EXTENSION IF NOT EXISTS citext;'"
```

然后通过 `workflow_dispatch` 重跑 deploy-dev：

```bash
curl -s -X POST -H "Authorization: token $GITEA_API_TOKEN" \
  -H "Content-Type: application/json" \
  "http://43.130.59.228/api/v1/repos/FFAIWorkspace/workspace/actions/workflows/deploy-dev.yml/dispatches" \
  -d '{"ref":"develop"}'
```

注意 Gitea Actions 不支持 `runs/{id}/rerun` 或 `jobs/{id}/rerun` 端点（返回 404）。重试只能靠 `workflow_dispatch` 或新 push。

### 长期建议（待团队决策）

1. **dev workflow 改用 `migrate deploy`**：和 UAT/prod 一致，让 migration.sql 的 `CREATE EXTENSION` 真的生效。代价：dev 不再容忍未签入迁移的 schema 实验。
2. **保留 `db push` 但前置预装扩展**：在 deploy-dev 的 SSH 脚本里加：
   ```bash
   docker exec ffws-dev-postgres psql -U ffws_dev -d ffws_dev \
     -c 'CREATE EXTENSION IF NOT EXISTS citext;
         CREATE EXTENSION IF NOT EXISTS pg_trgm;
         CREATE EXTENSION IF NOT EXISTS "uuid-ossp";'
   ```
   每加一个新扩展都要手动维护这一行。
3. **db:seed 阶段 ensure 扩展**：在 seed 流程里 idempotent 装常用扩展，把"扩展归属"从 migration 移到 seed。

倾向 1（最干净），代价是 dev 必须先有 migration 才能 push 新 schema，跟其他环境一致。

### 同类雷区

任何用到 PostgreSQL 扩展的 `@db.Xxx` 类型都会撞同样的雷：

- `@db.Citext` → citext
- `pg_trgm` 索引（GIN/GIST）→ pg_trgm
- PostGIS（`geography` / `geometry`）→ postgis
- `uuid_generate_v4()` 默认值 → uuid-ossp

只要 dev 用 `db push`，新增任何这种扩展的第一次部署都会失败。
