# 钉钉 SAP 同步集成开发复盘

> 日期: 2026-04-04 ~ 2026-04-05
> 范围: SAP 采购同步、同步中心导航重构、worktree 工作流优化

## 关键经验

### 1. Prisma schema 缺失模型导致运行时崩溃

**现象**: `DingtalkEmployee` 模型在代码中通过 `(this.prisma as any).dingtalkEmployee` 使用，但从未写入 Prisma schema 文件。`prisma db push` 后 Prisma Client 重新生成，原有的运行时绕过失效，导致 `findMany` 报 undefined 错误。

**教训**: 
- 不要用 `as any` 绕过 Prisma 类型检查——这只是把错误推迟到运行时
- 新增数据库表必须同时写入 Prisma schema，保持 schema 和代码同步
- `prisma db push` 会重新生成 Client，可能暴露之前被 `as any` 掩盖的问题

### 2. 合并 develop 后必须重新 `prisma db push`

**现象**: 后端 build 报 194 个 TS 错误，全部是 Prisma 模型不存在。

**根因**: develop 合并过来新的 schema 变更（performance、meeting-attendance 等），但本地数据库没同步，Prisma Client 和数据库结构不匹配。

**规则**: `git merge origin/develop` 后，如果 `prisma/schema/` 有变更，必须跑 `npx dotenv -e .env -- npx prisma db push --accept-data-loss`。

### 3. 宜搭 API 返回字段名可能是蛇形而非驼峰

**现象**: `getInstanceById` 返回的 `processInstanceId` 取不到值，审批链为空。

**修复**: 同时检查 `processInstanceId` 和 `process_instance_id`，并回退到 `formInstanceId`（宜搭中两者通常相同）。

**规则**: 宜搭 REST API 的字段命名不一致，有时驼峰有时蛇形，需要兼容两种。

### 4. SAP 内网服务需要 SSH 反向隧道 + TCP 代理

**现象**: SAP 服务在公司内网（`las-popci-01.faradayfuture.com:51000`），公网服务器无法直接访问。SSH `-R` 反向隧道直连 SAP 时 connection reset。

**解决**: 在内网机器上起 Python TCP 代理（转发到 SAP），再通过 SSH 反向隧道暴露代理端口。

**细节**:
- SAP 端口是 51000（HTTP），不是 443
- WSDL 中定义了两个端口：51000（HTTP）和 51001（HTTPS），但只有 51000 通
- 旧项目 `.env` 写了 `USE_HTTPS=true`，但实际走的是 HTTP
- 详细部署文档在 `docs/modules/dingtalk/sap-tunnel-setup.md`

### 5. SAP "already exists" 响应需要特殊处理

**现象**: 重复同步同一条采购单，SAP 返回 `Tracking number XXX already exists in PR Number NNNNN`，状态显示失败。

**修复**: 从错误消息中正则提取已有的 PR 号，标记为 `EXISTS` 状态并回写数据库，前端显示"已存在"而非"失败"。

### 6. 远程服务器 .env 可能是符号链接

**现象**: UAT 和正式服务器的 `backend/.env` 和 `frontend/.env` 都是软链，指向同一个文件（`.env.uat` 或 `.env.pro`）。

**规则**: 操作远程服务器配置前，先 `ls -la` 确认文件是否为符号链接，再用 `readlink -f` 获取实际路径。

### 7. 钉钉 API 有 IP 白名单

**现象**: UAT 服务器调钉钉 HR 接口返回 `errcode=88`（无权限）。

**根因**: 钉钉开放平台的应用配置有"服务器出口 IP"白名单，新服务器 IP 没加。

**规则**: 部署到新环境时，记得把服务器公网 IP 加到钉钉应用的出口 IP 白名单。

### 8. worktree 脚本必须先装依赖再初始化数据库

**现象**: `setup-worktree.sh` 创建完 Docker 容器后直接跑 `prisma db push`，报 `could not determine executable to run`。

**根因**: worktree 是新目录，没有 `node_modules`，prisma CLI 找不到。

**修复**: 在 `prisma db push` 之前加 `npm run install:all`。

### 9. 假期余额使用记录需要过滤 record_type

**现象**: 钉钉返回的假期变动记录包括发放记录和使用记录，前端把发放记录也显示成了"使用"。

**修复**: 过滤 `leave_record_type === 'leave'` 且排除 `refuse`/`abort` 状态。

### 10. 提交前必须 build 验证

**教训**: 这次有一次直接提交推送了没 build，后来发现前端缺 `leaflet` 依赖（develop 合并过来的其他模块引入的）。

**规则**: 提交前跑 `/simplify` + `nest build` + `next build`，尤其是合并 develop 之后。

## 流程改进建议

1. `setup-worktree.sh` 已增强：支持检出已有分支、自动装依赖、自动创建独立数据库
2. `start-feature` skill 已与脚本能力对齐
3. `formatTimeDisplay` 工具函数已提取，消除了 6 处重复代码
4. 建议后续在 `pre-push` hook 中加入 `nest build` 检查（目前只有契约校验）
