## [ERR-20260427-016] Lazy-register 任务在新部署环境造成"看不到也点不了"鸡生蛋

**日期**: 2026-04-27
**类别**: 部署 / UX
**严重度**: 中（首次部署 UAT/PROD 不可用）

### 问题描述
ADP 同步模块部署到 UAT 后，admin 打开 `/sync-center/adp/pto` 看不到任何任务卡片。
凌晨 cron 还没到，前端也没有办法手动触发（按钮是绑在卡片上的）。

### 根因
原设计：`executeTask` 在每次执行时检查 `automationTask` 是否存在，没有就 upsert。
"lazy register"。问题是：
- 新部署环境 DB 里没有任务记录
- `getStatus()` 返回 `tasks: []`
- 前端 `(status?.tasks ?? []).map(...)` 渲染 0 张卡片
- "立即触发"按钮挂在卡片上 → 点不到 → 无法触发首次执行
- **必须等 cron 自动跑**才会自动注册

这是典型的"延迟初始化遇到 UI 强依赖列表"问题。

### 修复
`AdpSchedulerService` 实现 `OnModuleInit`，启动时 upsert 两个任务：
```ts
async onModuleInit() {
  for (const code of [LINKER, PTO_SYNC]) {
    await this.prisma.automationTask.upsert({
      where: { code },
      create: { code, name, type: 'ADP_SYNC', scheduleType: 'CRON', status: 'ACTIVE' },
      update: {},  // 已存在不动，避免覆盖运营手改字段
    });
  }
}
```

`update: {}` 是重要细节：保留运营可能手动改过的 `status: 'PAUSED'` 等字段，启动注册不能覆盖。

### 启示
- **任何"按需 lazy 创建"的资源**（任务记录、用户偏好、配置项），如果 UI 依赖它存在才能交互，就是潜在 bug
- 部署到新环境时应该 demo 走一遍"全新用户首次进页面"的体验 —— 这是 UX checklist 的必备
- `OnModuleInit` 是 NestJS 的标准初始化钩子，比 `@Cron('* * * * * *')` 之类的 hack 更干净
- `upsert` 默认参数 `update` 必填，写 `{}` 是「存在不动」的标准模式

---
