---
name: backend-main
description: 当用户要求开发后端功能、实现 API、编写业务逻辑、创建服务或控制器时使用，遵循分层架构、API 契约安全与文档驱动流程。
---

# 后端功能技能

## 项目概览入口
- 项目定位、技术栈与目录入口请阅读：`../references/project-overview.md`

## 目的与触发
- 目的：按文档驱动交付后端功能，保持 API/数据契约安全、分层清晰、可验证。
- 触发：用户要求实现/修改后端功能、API、业务逻辑、服务/控制器。

## 快速守则
- 文档为事实来源；未经指令不得改动外部契约（路径/字段/语义/错误码）。
- 发生需求改动时，先按影响面更新 `docs/modules/{module}/` 对应文档（01-10，按需），再进行代码实现。
- 分层：Controller 仅 HTTP；Service 仅业务；Repository 仅持久化。
- 写接口必须鉴权/授权 + 输入校验；错误返回需稳定 code，可选 i18n key+参数。
- 默认最小改动；所有变更需可验证（测试或最小复现步骤）。
- `07-api.md` 必须包含接口数量汇总、按域接口清单（含序号/方法/路径/权限/域内数量）、编号化的统一格式+简短示例。
- 统一响应由拦截器处理：成功响应由 `TransformInterceptor` 统一封包，错误响应由 `AllExceptionsFilter` 统一格式化；前端由 `api-client` 响应拦截器统一解包。
- **外部数据同步必须接入 `platform_automation` 同步中心**（复用 `AutomationTask` + `AutomationExecution`），不得自建 SyncRun/SyncLog 类表。同步源数据表放在 `platform_automation` schema 内，参考 `organization/sync/`（Entra）和 `organization/dingtalk/sync/`。详见 `docs/standards/02-backend-architecture.md` "外部数据同步" 一节。
- **业务规则改动开工前必须"三选一"确认事实源**（司龄、年假、加班、考勤、薪酬、定价、税率、合规阈值等）：(1) `docs/modules/{module}/` 文档明写规则 + 来源 + 确认日期；(2) 用户在本次会话明确确认；(3) 二者皆无 → **停下问用户**，不要从代码现状 / 上游系统行为 / 注释推断。锁公式的集成测试只有规则被确认后才有意义——否则锁的是错口径，下次改方向时同样要回退。反例与背景详见 [`.learnings/2026-05-08-dingtalk-tenure-confirm-source-with-hr.md`](../../../.learnings/2026-05-08-dingtalk-tenure-confirm-source-with-hr.md)。

## 输入与结构速览
- 输入文档：`docs/modules/{module}/01-prd.md`，`05-ui-interaction-spec.md`，`04-state-machine.md`（如有），`06-data-model.md`，`07-api.md`。
- 架构摘要：Controller → Service → Repository → Database；目录 `backend/src/{core,engines,modules,common,config}`；Prisma schema 在 `backend/prisma/schema/`；入口 `backend/src/main.ts`。

## 产物
- 文档：`06-data-model.md`，`07-api.md`，`08-error-codes.md`（推荐）。
- 代码：分层实现（Controller/Service/Repository），必要迁移，L1 集成测试（不写单元测试，E2E 由 test-frontend 通过 MCP 执行）。

## 工作流（顺序）
0) **若功能涉及外部系统数据同步**（cron 拉取、API 同步、定期任务）：先扫 `platform_automation` 同步中心（`grep "^model.*Sync\|automation" backend/prisma/schema/platform_automation.prisma`），按 `02-backend-architecture.md` 的"外部数据同步"标准接入，**不要**自建 SyncRun/SyncLog 表或起独立 cron。
1) 从 UI/状态机抽取字段、操作、权限 → 得到 API 需求清单。
2) 设计数据模型（约束/索引）→ `06-data-model.md`。
3) 设计 API 契约（schema/分页/错误码引用）→ `07-api.md`（含数量汇总、按域清单、编号详情与简短示例）；定义错误码映射 → `08-error-codes.md`。
4) 实现分层代码，补校验/鉴权/错误映射/幂等（如需）。
5) **更新角色权限种子**：在 `prisma/seeds/roles.seed.ts` 中为 Employee/Leader/DepartmentManager 添加新模块的查看权限，权限码必须与 `constants/permissions.ts` 一致，然后运行 `npx ts-node prisma/seeds/iam-seed.ts`。
6) 数据库变更走 Prisma 迁移（database-main 技能）；更新相关类型。
7) **Schema Review（必做）**：迁移生成后，执行 `database-main` 技能的 Schema Review 流程，对变更的模型做七维结构审查（数据类型/约束/索引/关系/隔离/迁移安全/范式）。发现阻断级问题必须修复后再进入下一步。
8) 编写 L1 集成测试（`test-backend` 技能）：L1a 契约测试 + L1b 业务规则测试。不写单元测试，E2E 由 `test-frontend` 通过 MCP 执行。记录验证命令。
9) **开发后自检（Self-Review）**：代码实现完成后、提交 PR 前，执行下方自检流程。

---

## 开发后自检（Self-Review）

代码写完后、提交 PR 前，**必须**执行以下自检。目标是在 reviewer 之前自己发现 80% 的问题。

### 自检流程

#### Step 1: 分层合规检查

扫描本次变更的所有文件，逐项验证：

| 检查项 | 说明 | 自动验证方式 |
|--------|------|-------------|
| Controller 瘦身 | Controller 只做 HTTP 入参/出参，不含业务逻辑 | grep 变更的 controller 文件，检查是否有 `prisma`/`repository` 直接调用 |
| Service 职责 | Service 不直接处理 HTTP 请求/响应对象（如 `@Req()`、`@Res()`） | grep service 文件中的 Request/Response 引用 |
| DTO 校验 | 所有入参 DTO 使用 `class-validator` 装饰器 | grep DTO 文件，检查是否缺少 `@IsString`/`@IsNumber`/`@IsOptional` 等 |
| Guard 完整 | 写操作接口有 `@UseGuards(JwtAuthGuard)` + `@RequirePermissions()` | grep controller 中的 POST/PUT/PATCH/DELETE 方法是否有 Guard |
| 审计接入 | 写操作（POST/PUT/PATCH/DELETE）方法上有 `@Auditable()`；敏感操作叠加 `@Sensitive()`，财务操作叠加 `@Financial()` | `bash scripts/ops/check-controller-auditable.sh --mode block --all`（pre-commit 已自动 warn 模式扫 staged） |
| 错误处理 | Service 层抛出的异常使用 NestJS 内置异常（`NotFoundException`、`BadRequestException` 等） | grep throw 语句 |

#### Step 2: 契约一致性验证

| 检查项 | 说明 |
|--------|------|
| DTO ↔ 文档 | 请求 DTO 字段与 `07-api.md` 中定义的请求体一致 |
| 返回值 ↔ 文档 | Controller 返回结构与 `07-api.md` 中定义的响应体一致 |
| 返回值 ↔ 前端 | 如已有前端 interface/type，检查返回字段是否对齐 |
| 枚举完整 | 新增的 enum 值在 DTO、Service、前端 type、i18n 中全部同步 |
| 错误码 ↔ 文档 | 使用的错误码在 `08-error-codes.md` 中有登记 |

#### Step 3: 安全与隔离（IAM 四层模型，规则源 `docs/standards/09-iam-security.md`）

| 检查项 | 说明 |
|--------|------|
| L1 鉴权 | Controller 用 `@RequirePermissions('resource:action')`；**禁用** `@RequireOrganizationPermissions` / `@RequireGlobalPermissions` / `@Roles`（已废弃，导出已删除） |
| L3 权限码 | 新接口的权限码已加到 `backend/prisma/seeds/permissions.seed.ts` 且 resource 在 `backend/src/common/constants/data-scope-resources.ts`（snake_case 单数；历史命名 `parts/org/site-attendance/robot-manager` 走 LEGACY 集合） |
| L3 角色绑定 | 在 `backend/prisma/seeds/data-scopes.seed.ts` 给目标角色绑该权限码 |
| L4 写路径 IDOR | service 按 ID 取资源前调用 `assertAccess(user, resource, id)`；如确实不需要再校验，必须 `@SkipAssertAccess('显式理由')`，否则 `testing/scripts/assert-access-check.ts` pre-commit 会拒 |
| L4 数据权限 | 新业务表用 `@DataScope({ resource: 'foo' })` 装饰 service 方法；新表必须含标准字段（`organizationId / departmentId / regionId / createdById`，规则 §5.3.1.3） |
| 组织隔离 | DataScopeInterceptor 已全局生效；**不要**在 service 里再手写 `where: { organizationId }`（双重过滤导致空结果）|
| 治理审计 | **凡 POST/PUT/PATCH/DELETE 必标 `@Auditable()`**（敏感操作叠加 `@Sensitive()`，财务操作叠加 `@Financial()`）；或注入 `IamAuditService.record({ actor, action, resource, before, after })`。fire-and-forget 不阻塞主请求。`scripts/ops/check-controller-auditable.sh` 在 pre-commit 扫 staged controller，漏标会告警（过渡期 warn-only，后续改 block）。详见 `docs/modules/audit-system/README.md`。 |
| 字段级 | 含敏感字段（手机号 / 身份证 / 工资）用 `FieldPermissionService.maskFields(user, entity, resource)` 在返回前脱敏（PR #138 引入） |
| 敏感字段 | 返回中不暴露密码哈希、token、内部 ID 等 |
| SQL 注入 | 使用 Prisma 参数化查询，无 `$queryRaw` 拼接用户输入 |
| ID 暴露 | 路径参数走 `ParseUUIDPipe`；不接受用户输入的 organization_id 等跨权限字段 |

> 详细的 17 决策 / 22 禁止事项见 `docs/standards/09-iam-security.md`。
> 如新模块需"系统身份"（cron / queue / webhook 触发的写操作），用 `SystemPrincipalService.getSystemActor()` 而不是 hard-code 一个 user UUID。
> 委托 / Access Review / 紧急豁免是平台级能力，业务模块通常**不直接调**，由 IAM 治理服务统一处理。

#### Step 4: 性能嗅探

| 检查项 | 说明 |
|--------|------|
| N+1 查询 | 循环中是否有数据库调用？应使用 `include`/`select` 或批量查询 |
| 缺失分页 | 列表接口是否有分页？无限制的 `findMany()` 是风险 |
| 大事务 | 事务内是否包含外部调用（HTTP/消息队列）？应缩小事务范围 |
| 索引覆盖 | 高频查询的 WHERE 字段是否有索引？ |

#### Step 5: 输出自检报告

```
## 后端自检报告

### 分层合规: ✅/⚠️ (N 项通过 / M 项需修复)
### 契约一致: ✅/⚠️
### 安全隔离: ✅/⚠️
### 性能嗅探: ✅/⚠️
### Schema Review: ✅/N/A

### 需修复项:
- [文件:行号] 问题 → 修复方式

### 结论: ready-to-PR / needs-fix
```

发现 `needs-fix` 项时，**就地修复**后重新自检，直到全部通过。

---

## 文档变更后代码对齐检查

当模块文档（01-10）发生大规模变更后，用以下清单检查后端代码是否需要适配。

### Step 1: 数据模型对齐（06-data-model vs Prisma schema）

| 检查项 | 方法 |
|--------|------|
| 新增/删除实体 | 对比 06 实体清单 vs Prisma schema 中的 model 列表 |
| 新增/删除字段 | 逐模型对比字段数量，重点关注必填/可空变化 |
| 枚举值增删 | 对比 06 枚举定义 vs Prisma enum，特别是已废弃值 |
| 索引/约束变化 | 对比 06 索引表 vs Prisma @@index/@@unique |
| 关系变化 | 对比 06 关系表 vs Prisma @relation |

发现差异时：先改 Prisma schema → 生成迁移 → 更新 Prisma Client → 再改 Service/DTO。

### Step 2: API 契约对齐（07-api vs Controller/DTO）

| 检查项 | 方法 |
|--------|------|
| 已删除接口 | grep 07 中已删除的路由路径，确认 Controller 中对应方法已删除 |
| 请求字段增删 | 对比 07 请求体定义 vs DTO class，重点关注字段名和必填性 |
| 响应字段增删 | 对比 07 响应体定义 vs Service 返回值 |
| 接口路径变化 | 对比 07 路径 vs Controller @Get/@Post 装饰器 |

### Step 3: 错误码对齐（08-error-codes vs error-codes.ts）

| 检查项 | 方法 |
|--------|------|
| 错误码增删 | 对比 08 错误码清单 vs constants/error-codes.ts |
| HTTP 状态码 | 对比 08 的 HTTP 列 vs error-codes.ts 的 httpStatus 值 |
| 硬编码检查 | grep service 文件中的硬编码错误码字符串，应改为常量引用 |

### Step 4: 状态机对齐（04-state-machine vs Service 逻辑）

| 检查项 | 方法 |
|--------|------|
| 合法流转 | 对比 04 流转表 vs Service 中的 validateStatusTransition 逻辑 |
| 守卫条件 | 对比 04 守卫条件 vs Service 中的前置检查代码 |
| 已删除状态值 | grep 代码中是否还引用了 04 中已标废弃的枚举值 |

---

## 检查清单（合并前）
- API 契约与 UI 需求一致（不多不少），路径/字段/错误码未擅改。
- `07-api.md` 结构完整（数量汇总/按域清单/编号详情/简短示例）。
- 输入校验、鉴权/授权、错误映射完整；幂等性（如适用）。
- 数据库迁移规范、可回滚；错误码集中登记可复用。
- **Schema Review 已通过**：涉及数据库变更时，七维结构审查无阻断级问题。
- **Self-Review 已通过**：分层/契约/安全/性能四项自检无 needs-fix 项。
- 有测试或最小验证步骤，便于复现。

## 通用能力抽取
- 认证/授权、输入校验、错误映射封装为公共层。  
- 查询（分页/筛选/排序/搜索）封装通用逻辑。  
- 事务与幂等统一封装；审计日志/操作记录/异常告警统一格式。  
- 领域服务边界清晰，避免重复业务实现。

## 何时调用其他技能
- Prisma schema/迁移：`database-main`
- 后端集成测试：`test-backend`；前端 E2E：`test-frontend`

## 参考资料
- `references/api-standards.md`
- `references/service-design.md`
- `references/error-handling.md`
- `references/security-standards.md`
- `references/api-contract-checklist.md`（前后端契约字段对齐检查项）
- `references/nestjs-decorators.md`（NestJS 装饰器速查）
