# 审批引擎 API

> API 接口文档 - 审批引擎的所有 REST API 接口定义

---

## 📋 概述

| 属性 | 值 |
|------|-----|
| Base URL | `/api/v1/approval` |
| 认证方式 | JWT Bearer Token |
| 接口数量 | **54 个**（以代码 `backend/src/engines/approval/*.controller.ts` 中 `@Get/@Post/@Put/@Patch/@Delete` 实测为准）|
| 版本 | v3.0 |

### 命名说明

- API DTO 中的 `ProcessDefinition/ProcessVersion/ProcessInstance` 对应数据库模型 `ApprovalDefinition/ApprovalVersion/ApprovalInstance`

### 基础路径

```
/api/v1/approval
```

### 认证与权限

所有接口需要 JWT 认证，通过 `Authorization: Bearer <token>` 传递。

| 权限 | 说明 | 适用接口 |
|------|------|----------|
| `approval:start` | 发起审批流程 | 启动流程 |
| `approval:approve` | 处理审批任务 | 通过、驳回、退回、转发等 |
| `approval:execute` | 执行任务（填写字段） | 执行人操作 |
| `approval:admin` | 流程管理（管理员） | 管理员接口、代审批、强制结束 |
| `form:use` | 使用表单 | 查看表单详情 |

### 幂等性

启动流程接口可传入 `X-Idempotency-Key` 作为幂等键（当前仅透传到 DTO，未做统一去重处理）：

```http
X-Idempotency-Key: uuid-v4-string
```

其他写操作可携带 `X-Request-Id` 用于链路追踪与审计记录：

```http
X-Request-Id: uuid-v4-string
```

### 管理员操作审计

所有管理员级别的高危操作支持 `X-Admin-Reason` 请求头（可选），用于日志系统统一收集：

```http
X-Admin-Reason: 业务需求变更，流程作废
```

**适用接口**:

| 接口 | 操作 | 风险等级 |
|------|------|----------|
| `POST /admin/:instanceId/terminate` | 强制结束流程 | 🔴 高 |
| `POST /admin/:instanceId/approve` | 代审批（通过） | 🟠 中 |
| `POST /admin/:instanceId/reject` | 代审批（驳回） | 🟠 中 |
| `POST /admin/:instanceId/reassign` | 重新分配任务 | 🟡 低 |
| `POST /admin/:instanceId/resume-with-approvers` | 恢复挂起流程（指派审批人） | 🟠 中 |

**说明**:
- `X-Admin-Reason` 为可选 header，不影响接口功能
- **所有管理员高危操作必须提供 `reason` 字段，否则返回 `VALIDATION_ERROR`**
- `X-Admin-Reason` 主要用于日志系统侧的统一收集和审计报表
- 建议在日志系统中对带有此 header 的请求进行特殊标记和告警
- **审计日志会统一保存 `X-Request-Id` 和 `X-Admin-Reason`（如有），用于跨系统追踪**

**日志记录示例**:

```json
{
  "timestamp": "2024-12-07T10:30:00Z",
  "level": "WARN",
  "action": "ADMIN_TERMINATE",
  "operator": "admin-001",
  "instanceId": "pi-001",
  "reason": "业务需求变更，流程作废",
  "headers": {
    "X-Admin-Reason": "业务需求变更，流程作废",
    "X-Request-Id": "req-001"
  },
  "tags": ["admin-action", "high-risk"]
}
```

### 通用响应格式

> **命名提示**：以下示例中 `Process*` 为 API DTO 命名，对应数据库模型 `Approval*`。

**成功响应**:

```typescript
{
  success: true,
  data: T,
  message?: string,
  timestamp: string  // ISO 8601 格式
}
```

**错误响应**:

```typescript
{
  success: false,
  error: {
    code: string,           // 错误码（机器可读）
    message: string,        // 错误信息（人类可读）
    details?: any,          // 详细错误信息
    field?: string,         // 字段级错误
    errors?: FieldError[]   // 多字段验证错误
  },
  timestamp: string,
  path: string,
  method: string,
  statusCode: number
}

interface FieldError {
  field: string;
  message: string;
  value?: any;
  constraint?: string;
}
```

### 分页响应格式

```typescript
{
  success: true,
  data: {
    items: T[],
    total: number,
    page: number,
    limit: number,
    totalPages: number,
    hasNext: boolean,
    hasPrev: boolean
  },
  timestamp: string,
  path?: string
}
```

---

## 🏷️ 路径参数命名规范

本 API 遵循统一的路径参数命名规范：

| 参数 | 说明 | 示例 |
|------|------|------|
| `:instanceId` | 流程实例 ID（UUID） | `pi-550e8400-e29b-41d4-a716-446655440000` |
| `:taskId` | 审批任务 ID（UUID） | `task-550e8400-e29b-41d4-a716-446655440000` |
| `:key` | 流程定义 KEY（不可变标识符） | `OVERTIME_APPROVAL` |
| `:version` | 版本号（数字，从 1 开始） | `1`, `2`, `3` |
| `:businessType` | 业务类型 | `FORM_INSTANCE`, `OVERTIME` |
| `:businessId` | 业务单据 ID | `form-instance-001` |

---

## 📑 接口目录

| 分类 | 接口数量 | 说明 |
|------|----------|------|
| [流程启动](#-流程启动) | 1 | 启动审批流程 |
| [审批操作](#-审批操作) | 10 | 通过、驳回、退回、转发、撤回等 |
| [任务查询](#-任务查询) | 7 | 待办、已办、发起的等 |
| [流程查询](#-流程查询) | 5 | 状态、历史、详情等 |
| [流程图数据](#-流程图数据) | 2 | 流程图渲染数据 |
| [催办与提醒](#-催办与提醒) | 3 | 催办、提醒记录 |
| [流程定义管理](#-流程定义管理) | 6 | 流程定义 CRUD |
| [管理员操作](#-管理员操作) | 13 | 同步配置、代审批、强制结束、统计导出 |
| **总计** | **47** | |

---

## 🚀 流程启动

### POST /start - 启动审批流程

启动一个新的审批流程实例。

**权限**: `approval:start`

**HTTP 状态码**: `201 Created` | `400 Bad Request` | `404 Not Found` | `409 Conflict`

#### ⚠️ 业务唯一性说明

> **重要**: 业务方调用前必须保证 `businessId` 的唯一性。

| 场景 | 行为 |
|------|------|
| 同一 `businessType` + `businessId` 重复调用 | 返回 `409 PROCESS_ALREADY_EXISTS`，附带已有流程 `instanceId` |
| 不同 `businessType` 但相同 `businessId` | 允许创建（不同业务类型视为独立） |
| 已终止/已完成的流程重新发起 | 需使用新的 `businessId`，或调用专门的「重新发起」接口 |

**默认策略**: 同一业务单据（`businessType` + `businessId`）只能有一个运行中的流程实例。

**错误响应示例**:

```json
{
  "success": false,
  "error": {
    "code": "PROCESS_ALREADY_EXISTS",
    "message": "该业务单据已存在运行中的审批流程",
    "details": {
      "existingInstanceId": "pi-550e8400-e29b-41d4-a716-446655440000",
      "existingStatus": "RUNNING",
      "createdAt": "2024-12-07T10:00:00Z"
    }
  },
  "statusCode": 409
}
```

**请求体**:

```typescript
interface StartApprovalRequest {
  // 必填字段
  processDefinitionKey: string;     // 流程定义 KEY，如 "OVERTIME_APPROVAL"
  businessType: string;             // 业务类型，如 "FORM_INSTANCE", "OVERTIME"
  businessId: string;               // 业务单据 ID
  
  // 可选字段
  businessKey?: string;             // 业务单号（可读标识）
  title?: string;                   // 流程标题
  variables?: Record<string, any>;  // 流程变量（表单数据等）
  priority?: number;                // 优先级 0-3，默认 1
  dueDate?: string;                 // 截止时间（ISO 8601）
  version?: number;                 // 指定流程版本，默认使用默认版本
}
```

**响应**:

```typescript
interface StartApprovalResponse {
  instanceId: string;       // 流程实例 ID
  workflowId: string;       // Temporal 工作流 ID
  workflowRunId: string;    // Temporal 运行 ID
  status: InstanceStatus;   // 流程状态
  currentNodeId?: string;   // 当前节点 ID
  startTime: string;        // 启动时间
}
```

**示例**:

```bash
curl -X POST http://localhost:3001/api/v1/approval/start \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Request-Id: $(uuidgen)" \
  -d '{
    "processDefinitionKey": "OVERTIME_APPROVAL",
    "businessType": "FORM_INSTANCE",
    "businessId": "form-instance-001",
    "businessKey": "OT-2024-001",
    "title": "张三的加班申请 - 2024年12月7日",
    "variables": {
      "formKey": "overtime-request",
      "formData": {
        "startTime": "2024-12-07T18:00:00",
        "endTime": "2024-12-07T22:00:00",
        "hours": 4,
        "reason": "项目紧急上线"
      }
    },
    "priority": 2
  }'
```

**响应示例**:

```json
{
  "success": true,
  "data": {
    "instanceId": "pi-550e8400-e29b-41d4-a716-446655440000",
    "workflowId": "approval-pi-550e8400-e29b-41d4-a716-446655440000",
    "workflowRunId": "run-123456",
    "status": "RUNNING",
    "currentNodeId": "manager_approval",
    "startTime": "2024-12-07T10:30:00.000Z"
  },
  "timestamp": "2024-12-07T10:30:00.123Z"
}
```

---

## ✅ 审批操作

### 🛡️ 前端防误操作建议

审批系统涉及多种高危操作，建议前端在以下操作前统一弹窗确认：

| 操作 | 风险等级 | 建议交互 |
|------|----------|----------|
| 驳回（reject） | 🟠 中 | 弹窗确认 + 必填驳回原因 |
| 发起人撤回（withdraw） | 🟡 低 | 弹窗确认 |
| 审批人撤回（approver-withdraw） | 🟠 中 | 弹窗确认 + 必填撤回原因 |
| 管理员强制结束（terminate） | 🔴 高 | 二次确认弹窗 + 必填原因 + 输入确认文字 |
| 管理员代审批（approve/reject） | 🟠 中 | 弹窗确认 + 必填原因 |
| 管理员重分配（reassign） | 🟡 低 | 弹窗确认 + 选择新审批人 |

**建议实现**:

```typescript
// 高危操作确认弹窗示例
const confirmDangerousAction = async (action: string, instanceId: string) => {
  const confirmed = await showConfirmDialog({
    title: '确认操作',
    message: `确定要${action}吗？此操作不可撤销。`,
    confirmText: '确认',
    cancelText: '取消',
    type: 'warning',
    // 高风险操作要求输入确认文字
    requireInput: action === '强制结束',
    inputPlaceholder: '请输入"确认终止"以继续',
    inputValidator: (value) => value === '确认终止',
  });
  return confirmed;
};
```

---

### POST /:instanceId/approve - 审批通过

通过当前审批节点。

**权限**: `approval:approve`

**HTTP 状态码**: `200 OK` | `400 Bad Request` | `404 Not Found` | `409 Conflict`

**路径参数**:
- `instanceId`: 流程实例 ID

**请求体**:

```typescript
interface ApproveRequest {
  taskId: string;                    // 任务 ID（必填）
  comment?: string;                  // 审批意见
  formData?: Record<string, any>;    // 审批人修改的表单数据
  attachments?: Attachment[];        // 附件
}

interface Attachment {
  id: string;
  name: string;
  url: string;
  size: number;
  mimeType: string;
}
```

**响应**:

```typescript
interface ApprovalActionResponse {
  success: boolean;
  taskId: string;
  action: string;
  nextNodeId?: string;     // 下一节点 ID（如有）
  isProcessCompleted: boolean;
}
```

**示例**:

```bash
curl -X POST http://localhost:3001/api/v1/approval/pi-001/approve \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Request-Id: $(uuidgen)" \
  -d '{
    "taskId": "task-001",
    "comment": "同意，加班时间合理"
  }'
```

---

### POST /:instanceId/reject - 审批驳回

驳回审批申请，流程终止。

**权限**: `approval:approve`

**HTTP 状态码**: `200 OK` | `400 Bad Request` | `404 Not Found` | `409 Conflict`

**请求体**:

```typescript
interface RejectRequest {
  taskId: string;          // 任务 ID（必填）
  comment: string;         // 驳回原因（必填）
  rejectCode?: string;     // 驳回代码（用于统计）
}
```

**响应**: 同 `ApprovalActionResponse`

---

### POST /:instanceId/return - 退回

将任务退回到指定节点。

**权限**: `approval:approve`

**HTTP 状态码**: `200 OK` | `400 Bad Request` | `404 Not Found`

**请求体**:

```typescript
interface ReturnRequest {
  taskId: string;          // 任务 ID（必填）
  targetNodeId: string;    // 目标节点 ID（必填）
  comment: string;         // 退回原因（必填）
}
```

**响应**: 同 `ApprovalActionResponse`

**注意**: 
- 只能退回到已执行过的节点
- 使用 `GET /:instanceId/returnable-nodes` 获取可退回节点列表

---

### POST /:instanceId/forward - 转发

将任务转发给其他人处理。

**权限**: `approval:approve`

**HTTP 状态码**: `200 OK` | `400 Bad Request` | `404 Not Found`

**请求体**:

```typescript
interface ForwardRequest {
  taskId: string;          // 任务 ID（必填）
  targetUserId: string;    // 目标用户 ID（必填）
  comment?: string;        // 转发说明
}
```

**响应**: 同 `ApprovalActionResponse`

---

### POST /:instanceId/withdraw - 发起人撤回

发起人撤回申请。

**权限**: 仅流程发起人可操作

**HTTP 状态码**: `200 OK` | `400 Bad Request` | `403 Forbidden`

**请求体**:

```typescript
interface WithdrawRequest {
  reason?: string;         // 撤回原因（根据配置可能必填）
}
```

**响应**:

```typescript
interface WithdrawResponse {
  success: boolean;
  instanceId: string;
  status: 'TERMINATED';
  endReason: 'WITHDRAWN';
}
```

**错误码**:
- `WITHDRAW_NOT_ALLOWED`: 当前节点不允许撤回
- `NOT_INITIATOR`: 非发起人无法撤回

---

### POST /:instanceId/approver-withdraw - 审批人撤回

审批人撤回已通过的审批（下一级未处理时）。

**权限**: `approval:approve`

**HTTP 状态码**: `200 OK` | `400 Bad Request` | `403 Forbidden`

**请求体**:

```typescript
interface ApproverWithdrawRequest {
  taskId: string;          // 原任务 ID（必填）
  reason?: string;         // 撤回原因（根据配置可能必填）
}
```

**响应**: 同 `ApprovalActionResponse`

**错误码**:
- `NEXT_NODE_ALREADY_PROCESSED`: 下一级已处理，无法撤回
- `WITHDRAW_TIME_EXCEEDED`: 超过撤回时限
- `APPROVER_WITHDRAW_DISABLED`: 审批人撤回功能未启用

---

### POST /:instanceId/add-sign - 加签

动态添加审批人。

**权限**: `approval:approve`

**HTTP 状态码**: `200 OK` | `400 Bad Request` | `404 Not Found`

**请求体**:

```typescript
interface AddSignRequest {
  taskId: string;                              // 任务 ID（必填）
  userIds: string[];                           // 添加的用户 ID 列表（必填）
  signType: 'BEFORE' | 'AFTER' | 'PARALLEL';   // 加签类型（必填）
  comment?: string;                            // 说明
}
```

**加签类型说明**:
- `BEFORE`: 前加签，新增审批人先审批
- `AFTER`: 后加签，当前审批人审批后再由新增审批人审批
- `PARALLEL`: 并行加签，与当前审批人同时审批

**响应**: 同 `ApprovalActionResponse`

---

### POST /:instanceId/claim - 认领任务

从候选人中认领任务。

**权限**: `approval:approve`

**HTTP 状态码**: `200 OK` | `400 Bad Request` | `409 Conflict`

**请求体**:

```typescript
interface ClaimRequest {
  taskId: string;          // 任务 ID（必填）
}
```

**响应**:

```typescript
interface ClaimResponse {
  success: boolean;
  taskId: string;
  assignee: string;        // 认领人 ID
  claimTime: string;
}
```

**错误码**:
- `TASK_ALREADY_CLAIMED`: 任务已被他人认领
- `NOT_CANDIDATE`: 不在候选人列表中

---

### POST /:instanceId/unclaim - 取消认领

取消已认领的任务。

**权限**: `approval:approve`

**请求体**:

```typescript
interface UnclaimRequest {
  taskId: string;          // 任务 ID（必填）
}
```

---

### POST /tasks/:taskId/execute - 执行任务

执行人填写指定字段（非审批操作）。

**权限**: `approval:execute`

**请求体**:

```typescript
interface ExecuteTaskRequest {
  formData: Record<string, any>;    // 填写的表单数据（必填）
  comment?: string;                 // 备注
}
```

**响应**: 同 `ApprovalActionResponse`

---

## 📋 任务查询

### GET /my/pending - 我的待办

查询当前用户待处理的审批任务。

**权限**: 已认证用户

**HTTP 状态码**: `200 OK`

**查询参数**:

```typescript
interface PendingTasksQuery {
  page?: number;              // 页码，默认 1
  limit?: number;             // 每页数量，默认 20，最大 100
  businessType?: string;      // 业务类型筛选
  processKey?: string;        // 流程 KEY 筛选
  priority?: number;          // 优先级筛选
  keyword?: string;           // 关键词搜索（标题、单号）
  isDelegated?: boolean;      // 是否只看委托给我的任务
  sortBy?: 
    | 'createTime'            // 创建时间（默认）
    | 'dueDate'               // 截止时间
    | 'priority'              // 优先级
    | 'lastReminderTime'      // 最后催办时间
    | 'remainingHours';       // 剩余处理时间（升序=最紧急在前）
  sortOrder?: 'asc' | 'desc'; // 排序方向，默认 desc
}
```

**响应**:

```typescript
interface PendingTasksResponse {
  items: ApprovalTaskItem[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
  hasNext: boolean;
  hasPrev: boolean;
}

interface ApprovalTaskItem {
  id: string;
  name: string;
  description?: string;
  type: TaskType;
  status: TaskStatus;
  
  // 处理人信息
  assignee?: UserInfo;
  candidateUsers: UserInfo[];
  
  // 委托信息
  isDelegated: boolean;           // 是否为委托任务
  delegatedFrom?: UserInfo;       // 委托来源（原审批人）
  delegatedAt?: string;           // 委托时间
  delegationReason?: string;      // 委托原因
  
  // 时间信息
  createTime: string;
  dueDate?: string;
  lastReminderTime?: string;      // 最后催办时间
  
  // 关联信息
  instanceId: string;
  businessType: string;
  businessId: string;
  businessKey?: string;
  title?: string;
  
  // 发起人
  initiator: UserInfo;
  
  // 优先级
  priority: number;
  
  // 超时信息
  isOverdue: boolean;
  remainingHours?: number;        // 剩余处理时间（小时），负数表示已超时
  reminderCount?: number;         // 已催办次数
}

interface UserInfo {
  id: string;
  displayName: string;
  email?: string;
  avatar?: string;
  department?: string;
}
```

**示例**:

```bash
curl "http://localhost:3001/api/v1/approval/my/pending?page=1&limit=20&priority=2" \
  -H "Authorization: Bearer $TOKEN"
```

---

### GET /my/initiated - 我发起的

查询当前用户发起的审批流程。

**权限**: 已认证用户

**HTTP 状态码**: `200 OK`

**查询参数**:

```typescript
interface InitiatedQuery {
  page?: number;
  limit?: number;
  status?: InstanceStatus;    // 流程状态筛选
  businessType?: string;
  processKey?: string;
  startTimeFrom?: string;     // 开始时间范围
  startTimeTo?: string;
  keyword?: string;
}
```

**响应**:

```typescript
interface InitiatedResponse {
  items: ProcessInstanceItem[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
  hasNext: boolean;
  hasPrev: boolean;
}

interface ProcessInstanceItem {
  id: string;
  businessType: string;
  businessId: string;
  businessKey?: string;
  title?: string;
  
  // 流程信息
  processDefinitionKey: string;
  processDefinitionName: string;
  
  // 状态
  status: InstanceStatus;
  currentNodeId?: string;
  currentNodeName?: string;
  
  // 当前处理人
  currentAssignees: UserInfo[];
  
  // 时间
  startTime: string;
  endTime?: string;
  
  // 结果
  endReason?: string;
  
  // 优先级
  priority: number;
}
```

---

### GET /my/processed - 我已处理

查询当前用户已处理的审批任务。

**权限**: 已认证用户

**HTTP 状态码**: `200 OK`

**查询参数**:

```typescript
interface ProcessedQuery {
  page?: number;
  limit?: number;
  businessType?: string;
  processKey?: string;
  action?: ApprovalAction;    // 操作类型筛选
  actionTimeFrom?: string;    // 操作时间范围
  actionTimeTo?: string;
  keyword?: string;
}
```

**响应**:

```typescript
interface ProcessedResponse {
  items: ProcessedTaskItem[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
  hasNext: boolean;
  hasPrev: boolean;
}

interface ProcessedTaskItem {
  id: string;
  name: string;
  
  // 操作信息
  action: ApprovalAction;
  comment?: string;
  actionTime: string;
  
  // 关联信息
  instanceId: string;
  businessType: string;
  businessId: string;
  businessKey?: string;
  title?: string;
  
  // 发起人
  initiator: UserInfo;
  
  // 流程状态
  instanceStatus: InstanceStatus;
}
```

---

### GET /my/cc - 抄送我的

查询抄送给当前用户的流程。

**权限**: 已认证用户

**HTTP 状态码**: `200 OK`

**查询参数**:

```typescript
interface CcQuery {
  page?: number;
  limit?: number;
  businessType?: string;
  isRead?: boolean;           // 是否已读
  ccTimeFrom?: string;
  ccTimeTo?: string;
}
```

---

### GET /my/stats - 审批统计

获取当前用户的审批统计数据。

**权限**: 已认证用户

**响应**:

```typescript
interface ApprovalStatsResponse {
  pendingCount: number;       // 待我处理
  urgentCount: number;        // 紧急待办（即将超时）
  overdueCount: number;       // 已超时
  initiatedCount: number;     // 我发起的（进行中）
  processedToday: number;     // 今日已处理
  processedThisWeek: number;  // 本周已处理
  ccUnreadCount: number;      // 抄送未读
}
```

---

### GET /tasks/:taskId - 任务详情

获取单个任务的详细信息。

**权限**: 任务相关人员

**HTTP 状态码**: `200 OK` | `403 Forbidden` | `404 Not Found`

**响应**:

```typescript
interface TaskDetailResponse {
  id: string;
  name: string;
  description?: string;
  type: TaskType;
  status: TaskStatus;
  
  // 处理人
  assignee?: UserInfo;
  candidateUsers: UserInfo[];
  candidateGroups: string[];
  owner?: UserInfo;           // 原始负责人（转发前）
  
  // 委托信息
  isDelegated: boolean;           // 是否为委托任务
  delegatedFrom?: UserInfo;       // 委托来源（原审批人）
  delegatedAt?: string;           // 委托时间
  delegationReason?: string;      // 委托原因（如：请假、夜间自动委托）
  delegationType?: 'MANUAL' | 'AUTO_ON_LEAVE' | 'AUTO_TIME_RANGE' | 'AUTO_TIMEOUT';
  
  // 时间
  createTime: string;
  claimTime?: string;
  dueDate?: string;
  endTime?: string;
  lastReminderTime?: string;      // 最后催办时间
  
  // 超时信息
  isOverdue: boolean;
  remainingHours?: number;        // 剩余处理时间（小时）
  reminderCount?: number;         // 已催办次数
  
  // 版本号（用于乐观锁）
  version: number;
  
  // 表单配置
  editableFields?: string[];  // 可编辑字段
  requiredFields?: string[];  // 必填字段
  
  // 操作权限
  allowedActions: ApprovalAction[];
  
  // 可退回节点
  returnTargets?: ReturnableNode[];
  
  // 关联信息
  instance: ProcessInstanceDetail;
  nodeInstance: NodeInstanceDetail;
  
  // 操作日志
  actionLogs: ActionLogItem[];
}

interface ReturnableNode {
  id: string;
  name: string;
  type: NodeType;
}

interface ActionLogItem {
  id: string;
  action: ApprovalAction;
  operator: UserInfo;
  comment?: string;
  actionTime: string;
  formDataChanges?: Record<string, { old: any; new: any }>;
}
```

---

### GET /:instanceId/returnable-nodes - 可退回节点

获取可以退回的节点列表。

**权限**: 当前任务处理人

**响应**:

```typescript
interface ReturnableNodesResponse {
  nodes: ReturnableNode[];
}
```

---

## 📊 流程查询

### GET /:instanceId - 流程详情

获取流程实例的完整信息。

**权限**: 流程相关人员

**响应**:

```typescript
interface ProcessInstanceDetailResponse {
  id: string;
  
  // 业务关联
  businessType: string;
  businessId: string;
  businessKey?: string;
  title?: string;
  
  // 流程定义
  processDefinition: {
    id: string;
    key: string;
    name: string;
    category: string;
  };
  processVersion: {
    id: string;
    version: number;
    name: string;
  };
  
  // Temporal 信息
  workflowId: string;
  workflowRunId: string;
  
  // 发起人
  initiator: UserInfo;
  
  // 状态
  status: InstanceStatus;
  currentNodeId?: string;
  currentNodeName?: string;
  
  // 流程变量
  variables: Record<string, any>;
  
  // 执行统计
  totalNodeExecutions: number;
  
  // 时间
  startTime: string;
  endTime?: string;
  
  // 结束信息
  endReason?: string;
  endComment?: string;
  
  // 优先级
  priority: number;
  dueDate?: string;
  
  // 节点实例列表
  nodeInstances: NodeInstanceDetail[];
  
  // 当前待处理任务
  pendingTasks: ApprovalTaskItem[];
}

interface NodeInstanceDetail {
  id: string;
  nodeId: string;
  nodeName: string;
  nodeType: NodeType;
  status: NodeStatus;
  
  // 审批配置
  assignees: UserInfo[];
  approvalMode?: ApprovalMode;
  
  // 执行信息
  executionCount: number;
  result?: NodeResult;
  
  // 时间
  startTime: string;
  endTime?: string;
  
  // 关联任务
  tasks: ApprovalTaskItem[];
}
```

---

### GET /:instanceId/status - 流程状态

获取流程实例的当前状态（轻量级）。

**权限**: 流程相关人员

**响应**:

```typescript
interface ProcessStatusResponse {
  id: string;
  status: InstanceStatus;
  currentNodeId?: string;
  currentNodeName?: string;
  currentAssignees: UserInfo[];
  startTime: string;
  endTime?: string;
  endReason?: string;
  progress: number;           // 进度百分比 0-100
}
```

---

### GET /:instanceId/history - 审批历史

获取流程的完整审批历史。

**权限**: 流程相关人员

**响应**:

```typescript
interface ApprovalHistoryResponse {
  items: HistoryItem[];
}

interface HistoryItem {
  nodeId: string;
  nodeName: string;
  nodeType: NodeType;
  
  // 节点状态
  status: NodeStatus;
  startTime: string;
  endTime?: string;
  
  // 任务列表
  tasks: TaskHistoryItem[];
}

interface TaskHistoryItem {
  id: string;
  assignee?: UserInfo;
  status: TaskStatus;
  
  // 是否自动通过
  autoApproved: boolean;
  autoApproveReason?: string;
  
  // 操作日志
  actions: ActionLogItem[];
}
```

---

### GET /by-business/:businessType/:businessId - 通过业务查询

通过业务类型和业务 ID 查询流程。

**权限**: 业务相关人员

**响应**:

```typescript
interface BusinessInstanceResponse {
  instance?: ProcessInstanceDetailResponse;
  exists: boolean;
}
```

---

### GET /search - 流程搜索

搜索流程实例（管理员或有权限用户）。

**权限**: `approval:admin` 或流程相关人员

**查询参数**:

```typescript
interface ProcessSearchQuery {
  page?: number;
  limit?: number;
  
  // 筛选条件
  processKey?: string;
  businessType?: string;
  status?: InstanceStatus;
  initiatorId?: string;
  
  // 时间范围
  startTimeFrom?: string;
  startTimeTo?: string;
  endTimeFrom?: string;
  endTimeTo?: string;
  
  // 搜索
  keyword?: string;           // 搜索标题、单号
  
  // 排序
  sortBy?: 'startTime' | 'endTime' | 'priority';
  sortOrder?: 'asc' | 'desc';
}
```

---

## 📈 流程图数据

### GET /:instanceId/diagram - 获取流程图数据

获取用于渲染流程图的数据（基于实例）。

**权限**: 流程相关人员

**响应**:

```typescript
interface ProcessDiagramResponse {
  // React Flow 格式的节点
  nodes: DiagramNode[];
  
  // React Flow 格式的边
  edges: DiagramEdge[];
  
  // 流程实例信息
  instance: {
    id: string;
    status: InstanceStatus;
    currentNodeId?: string;
    startTime: string;
    endTime?: string;
  };
  
  // 发起人
  initiator: UserInfo;
}

interface DiagramNode {
  id: string;
  type: 'startNode' | 'endNode' | 'approvalNode' | 'gatewayNode' | 'serviceNode';
  position: { x: number; y: number };
  data: {
    label: string;
    nodeType: NodeType;
    status: NodeStatus;
    isCurrent: boolean;
    
    // 审批节点特有
    assignees?: UserInfo[];
    approvalMode?: ApprovalMode;
    
    // 执行历史
    history?: {
      action: ApprovalAction;
      operator: UserInfo;
      comment?: string;
      actionTime: string;
    }[];
    
    // 超时信息
    isOverdue?: boolean;
    dueDate?: string;
  };
  style?: Record<string, any>;
}

interface DiagramEdge {
  id: string;
  source: string;
  target: string;
  type: 'smoothstep';
  label?: string;
  animated: boolean;          // 当前路径动画
  style?: Record<string, any>;
  data?: {
    condition?: string;
    isActive: boolean;        // 是否是已走过的路径
  };
}
```

---

### GET /diagram/:businessType/:businessId - 通过业务获取流程图

通过业务类型和业务 ID 获取流程图数据。

**权限**: 业务相关人员

**响应**: 同 `ProcessDiagramResponse`

---

## 🔔 催办与提醒

### POST /:instanceId/remind - 手动催办

发起人或管理员对待办任务发送催办。

**权限**: 发起人或 `approval:admin`

**请求体**:

```typescript
interface RemindRequest {
  taskId?: string;            // 指定任务 ID（可选，不指定则催办所有待办）
  message?: string;           // 催办消息
  channels?: ('EMAIL' | 'DINGTALK' | 'FEISHU' | 'INTERNAL')[];  // 通知渠道
}
```

**响应**:

```typescript
interface RemindResponse {
  success: boolean;
  remindedTasks: {
    taskId: string;
    assignee: UserInfo;
    channels: string[];
  }[];
}
```

**错误码**:
- `REMIND_LIMIT_EXCEEDED`: 超过催办次数限制
- `REMIND_INTERVAL_TOO_SHORT`: 催办间隔过短

---

### GET /:instanceId/reminders - 催办记录

获取流程的催办记录。

**权限**: 流程相关人员

**响应**:

```typescript
interface RemindersResponse {
  items: ReminderRecord[];
}

interface ReminderRecord {
  id: string;
  taskId: string;
  taskName: string;
  assignee: UserInfo;
  
  // 催办信息
  reminderType: 'MANUAL' | 'AUTO';
  channels: string[];
  message?: string;
  
  // 发起人
  triggeredBy?: UserInfo;     // 手动催办时的发起人
  
  // 时间
  createdAt: string;
}
```

---

### GET /my/reminders - 我收到的催办

获取当前用户收到的催办记录。

**权限**: 已认证用户

**查询参数**:

```typescript
interface MyRemindersQuery {
  page?: number;
  limit?: number;
  isRead?: boolean;
  timeFrom?: string;
  timeTo?: string;
}
```

---

## 🔧 流程定义管理

### GET /definitions - 流程定义列表

获取所有流程定义。

**权限**: `approval:admin`

**查询参数**:

```typescript
interface DefinitionsQuery {
  page?: number;
  limit?: number;
  category?: string;
  status?: ProcessStatus;
  keyword?: string;
}
```

**响应**:

```typescript
interface DefinitionsResponse {
  items: ProcessDefinitionItem[];
  total: number;
  page: number;
  limit: number;
}

interface ProcessDefinitionItem {
  id: string;
  key: string;
  name: string;
  category: string;
  description?: string;
  status: ProcessStatus;
  latestVersion: number;
  
  // 多区域支持
  regionScope?: string;
  tenantId?: string;
  
  // 统计
  activeInstanceCount: number;
  totalInstanceCount: number;
  
  // 时间
  createdAt: string;
  updatedAt: string;
}
```

---

### GET /definitions/:key - 流程定义详情

获取指定流程定义的详情。

**权限**: `approval:admin`

**响应**:

```typescript
interface DefinitionDetailResponse {
  id: string;
  key: string;
  name: string;
  category: string;
  description?: string;
  status: ProcessStatus;
  latestVersion: number;
  
  // 版本列表
  versions: ProcessVersionItem[];
  
  // 时间
  createdAt: string;
  updatedAt: string;
  createdBy?: UserInfo;
  updatedBy?: UserInfo;
}

interface ProcessVersionItem {
  id: string;
  version: number;
  name: string;
  isDefault: boolean;
  status: VersionStatus;
  
  // 部署信息
  deployedAt?: string;
  deployedBy?: UserInfo;
  changeLog?: string;
  
  // 统计
  activeInstanceCount: number;
  
  // 时间
  createdAt: string;
}
```

---

### GET /definitions/:key/versions/:version - 流程版本详情

获取指定版本的详细信息（含流程模型）。

**权限**: `approval:admin`

**响应**:

```typescript
interface VersionDetailResponse {
  id: string;
  definitionId: string;
  version: number;
  name: string;
  isDefault: boolean;
  status: VersionStatus;
  
  // 流程模型
  processModel: ProcessModel;
  
  // 全局设置
  settings: ProcessSettings;
  
  // 部署信息
  deployedAt?: string;
  deployedBy?: UserInfo;
  changeLog?: string;
  
  // 时间
  createdAt: string;
  updatedAt: string;
}
```

---

### POST /definitions/:key/versions - 创建新版本

创建流程定义的新版本。

**权限**: `approval:admin`

**请求体**:

```typescript
interface CreateVersionRequest {
  name: string;                       // 版本名称
  processModel: ProcessModel;         // 流程模型
  settings?: ProcessSettings;         // 全局设置
  changeLog?: string;                 // 变更说明
  setAsDefault?: boolean;             // 是否设为默认版本
}
```

**响应**:

```typescript
interface CreateVersionResponse {
  id: string;
  version: number;
  status: VersionStatus;
}
```

---

### POST /definitions/:key/versions/:version/deploy - 部署版本

部署指定版本（设为默认版本）。

**权限**: `approval:admin`

**响应**:

```typescript
interface DeployVersionResponse {
  success: boolean;
  version: number;
  previousDefaultVersion?: number;
}
```

---

### POST /definitions/:key/rollback/:version - 回滚版本

回滚到指定版本。

**权限**: `approval:admin`

**响应**:

```typescript
interface RollbackResponse {
  success: boolean;
  currentVersion: number;
  rolledBackFrom: number;
}
```

---

## 👨‍💼 管理员操作

> 以下接口需要 `approval:admin` 权限

### POST /admin/sync - 同步流程配置

> ⚠️ **已弃用**：流程配置已迁移至表单管理页面，此接口仅供历史兼容。

从表单管理模块同步流程定义。

**请求体**:

```typescript
interface SyncRequest {
  keys?: string[];            // 指定同步的流程 KEY，不传则同步全部
  force?: boolean;            // 是否强制覆盖
}
```

**响应**:

```typescript
interface SyncResponse {
  results: {
    created: number;
    updated: number;
    skipped: number;
    errors: SyncError[];
  };
}

interface SyncError {
  key: string;
  error: string;
}
```

---

### GET /admin/analytics - 管理员数据中心统计

按表单维度返回统计与趋势数据。

**权限**: `approval:admin`

**查询参数**:

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| formKey | string | 否 | 表单 Key（可选，空表示全部） |
| formDefinitionId | string | 否 | 表单定义 ID |
| businessType | string | 否 | 业务类型（FORM/EXPENSE/...） |
| approvalRequired | boolean | 否 | 是否需要审批 |
| status | string | 否 | 审批状态筛选 |
| timeFrom | string | 否 | 开始时间（ISO） |
| timeTo | string | 否 | 结束时间（ISO） |
| submitterId | string | 否 | 发起人 ID |
| organizationId | string | 否 | 组织 ID（按发起人归属） |
| departmentId | string | 否 | 部门 ID（按发起人归属） |
| keyword | string | 否 | 关键字（业务编号/表单名） |

**响应**:
```typescript
interface AdminAnalyticsResponse {
  summary: {
    totalSubmissions: number;
    approvalRate: number;
    rejectRate: number;
    avgDurationMs: number;
    runningCount: number;
  };
  trends: Array<{
    date: string;
    submissions: number;
    approvals: number;
    rejections: number;
  }>;
  distribution: Array<{
    formKey: string;
    formName: string;
    count: number;
  }>;
}
```

---

### GET /admin/instances - 管理员提交明细列表

**权限**: `approval:admin`

**查询参数**:

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| page | number | 否 | 页码 |
| limit | number | 否 | 每页数量 |
| formKey | string | 否 | 表单 Key |
| formDefinitionId | string | 否 | 表单定义 ID |
| businessType | string | 否 | 业务类型 |
| approvalRequired | boolean | 否 | 是否需要审批 |
| status | string | 否 | 状态 |
| submitterId | string | 否 | 发起人 |
| timeFrom | string | 否 | 开始时间（ISO） |
| timeTo | string | 否 | 结束时间（ISO） |
| organizationId | string | 否 | 组织 ID（按发起人归属） |
| departmentId | string | 否 | 部门 ID（按发起人归属） |
| keyword | string | 否 | 关键字（业务编号/表单名） |

**响应**:
```typescript
interface AdminInstancesResponse {
  items: Array<{
    instanceId?: string;
    businessInstanceId: string;
    businessType: string;
    formKey?: string;
    formName?: string;
    submitter: { id: string; name: string };
    status: string;
    currentNode?: string;
    submittedAt: string;
    durationMs?: number;
    approvalRequired: boolean;
  }>;
  total: number;
  page: number;
  limit: number;
  totalPages: number;
  hasNext: boolean;
  hasPrev: boolean;
}
```

---

### POST /admin/instances/export - 导出管理员明细

**权限**: `approval:admin`

**请求体**: 与 `GET /admin/instances` 相同筛选条件

**补充字段**:

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| format | string | 否 | `xlsx` / `csv`，默认 `xlsx` |

**响应**:
```typescript
interface AdminExportResponse {
  taskId: string;
}
```

---

### GET /admin/exports - 导出任务列表

返回当前管理员发起的导出任务列表（最近 20 条）。

**权限**: `approval:admin`

**响应**:
```typescript
interface AdminExportItem {
  id: string;
  status: 'PENDING' | 'PROCESSING' | 'SUCCESS' | 'FAILED' | 'EXPIRED';
  format: string;
  fileName?: string;
  fileSize?: number;
  contentType?: string;
  downloadUrl?: string;
  createdAt: string;
  completedAt?: string;
  expiresAt?: string;
}
```

---

### GET /admin/exports/:taskId - 导出任务详情

**权限**: `approval:admin`

**响应**: `AdminExportItem`

---

### GET /admin/settings - 管理员数据中心设置

**权限**: `approval:admin`

**响应**:
```typescript
interface AdminSettingsResponse {
  exportRetentionDays: number;
}
```

---

### PUT /admin/settings - 更新管理员数据中心设置

**权限**: `approval:admin`

**请求体**:
```typescript
interface AdminSettingsRequest {
  exportRetentionDays: number; // 7-365
}
```

**响应**: `AdminSettingsResponse`

---

### POST /admin/:instanceId/resume-with-approvers - 恢复挂起流程

节点审批人解析失败导致流程进入 `SUSPENDED` 时，管理员显式指派审批人列表，
通过 Temporal `resumeWithApprovers` signal 让 workflow 继续推进。详见
`04-state-machine.md` §1.3 与 §4.5、`.learnings/ERRORS/ERR-20260501-004.md`。

**权限**: `approval:admin`

**风险等级**: 🟠 中

**前置条件**: `instance.status === 'SUSPENDED'`，否则返回 `400 BAD_REQUEST`。

**请求头**:

```http
Authorization: Bearer <token>
Content-Type: application/json
X-Request-Id: <uuid>
X-Admin-Reason: <reason>  # 可选，用于日志系统统一收集
```

**请求体**（`AdminResumeWithApproversDto`）:

```typescript
interface AdminResumeWithApproversRequest {
  approverIds: string[];  // 必填，至少一个，UUID 数组（注入到当前挂起节点）
  reason: string;         // 必填，恢复原因（写入审计日志）
}
```

**响应**（`AdminActionResponse`）:

```typescript
interface AdminResumeWithApproversResponse {
  success: true;
  instanceId: string;
  status: 'RUNNING';        // 恢复后实例状态
  action: 'ADMIN_RESUME';   // 审计动作类型（响应字段）
  message: string;          // 例: "挂起流程已恢复"
  auditLogId: string;       // 审计日志 ID（底层 audit_logs.action 复用 ADMIN_REASSIGN, riskLevel=HIGH）
}
```

> 审计动作 `action` 字段为 `'ADMIN_RESUME'`（用于响应/前端展示），但底层
> `audit_logs.action` 复用 `'ADMIN_REASSIGN'`（避免新增审计枚举）。前端筛选审计
> 历史时若需要单独识别"恢复挂起"，应基于响应 `action` 字段或日志 `reason` 中
> 的"恢复挂起流程"前缀。

**错误响应**:

| 状态码 | 错误 | 触发条件 |
|--------|------|----------|
| 404 | `NOT_FOUND` | `instanceId` 不存在 |
| 400 | `BAD_REQUEST` | 实例 `status !== 'SUSPENDED'` |
| 400 | `BAD_REQUEST` | `approverIds` 为空数组 |
| 400 | `VALIDATION_ERROR` | `approverIds` 包含非 UUID / `reason` 缺失 |
| 403 | `FORBIDDEN` | 缺少 `approval:admin` 权限 |

**cURL 示例**:

```bash
curl -X POST "$API_URL/approval/admin/pi-001/resume-with-approvers" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Request-Id: $(uuidgen)" \
  -H "X-Admin-Reason: 节点审批人离职，临时指派代理人恢复流程" \
  -d '{
    "approverIds": ["user-uuid-1", "user-uuid-2"],
    "reason": "原节点审批人已离职，由直属经理代理"
  }'
```

**配套 signal**：服务端通过 `temporalService.sendSignal(workflowId, 'resumeWithApprovers', { approverIds, resolvedBy })` 投递；workflow 中 `setHandler(resumeWithApproversSignal)` 注入 `state.resumedApprovers` 后退出 `condition` 等待，进入正常审批流程。

---

### POST /admin/:instanceId/terminate - 强制结束流程

管理员强制终止流程。

**权限**: `approval:admin`

**风险等级**: 🔴 高

**请求头**:

```http
Authorization: Bearer <token>
Content-Type: application/json
X-Request-Id: <uuid>
X-Admin-Reason: <reason>  # 可选，用于日志系统统一收集
```

**请求体**:

```typescript
interface AdminTerminateRequest {
  reason: string;             // 终止原因（必填，用于业务展示）
  attachments?: Attachment[]; // 凭证附件（如业务变更通知、领导批示等）
}
```

**响应**:

```typescript
interface AdminTerminateResponse {
  success: boolean;
  instanceId: string;
  status: 'TERMINATED';
  endReason: 'ADMIN_TERMINATED';
}
```

**cURL 示例**:

```bash
curl -X POST "$API_URL/approval/admin/pi-001/terminate" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Request-Id: $(uuidgen)" \
  -H "X-Admin-Reason: 业务需求变更，流程作废" \
  -d '{
    "reason": "业务需求变更，流程作废"
  }'
```

**注意**: 此操作会记录详细审计日志，`X-Admin-Reason` header 会被日志系统特殊标记。

---

### POST /admin/:instanceId/approve - 代审批

管理员代替审批人处理任务。

**权限**: `approval:admin`

**风险等级**: 🟠 中

**请求头**:

```http
Authorization: Bearer <token>
Content-Type: application/json
X-Request-Id: <uuid>
X-Admin-Reason: <reason>  # 可选，用于日志系统统一收集
```

**请求体**:

```typescript
interface AdminApproveRequest {
  taskId: string;             // 任务 ID（必填）
  comment: string;            // 审批意见（必填，展示给发起人）
  reason: string;             // 代审批原因（必填，说明为何代审批）
  attachments?: Attachment[]; // 凭证附件（如邮件截图、领导批示等）
}
```

> **注意**: 代驳回请使用 `POST /admin/:instanceId/reject` 接口。
> 
> **附件用途**: 管理员代审批时可上传凭证（如原审批人授权邮件截图、电话录音、领导批示等），作为操作依据留存审计。

**响应**: 同 `ApprovalActionResponse`

**cURL 示例**:

```bash
curl -X POST "$API_URL/approval/admin/pi-001/approve" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Request-Id: $(uuidgen)" \
  -H "X-Admin-Reason: 原审批人请假，紧急代审批" \
  -d '{
    "taskId": "task-001",
    "comment": "同意",
    "reason": "原审批人请假，紧急代审批"
  }'
```

**注意**: 此操作会记录详细审计日志，包含代审批原因和操作人信息。

---

### POST /admin/:instanceId/reject - 代审批（驳回）

管理员代替审批人驳回任务。

**权限**: `approval:admin`

**风险等级**: 🟠 中

**请求头**:

```http
Authorization: Bearer <token>
Content-Type: application/json
X-Request-Id: <uuid>
X-Admin-Reason: <reason>  # 可选，用于日志系统统一收集
```

**请求体**:

```typescript
interface AdminRejectRequest {
  taskId: string;             // 任务 ID（必填）
  comment: string;            // 驳回意见（必填，展示给发起人）
  reason: string;             // 代审批原因（必填，说明为何代审批）
  rejectCode?: string;        // 驳回代码（用于统计）
  attachments?: Attachment[]; // 凭证附件（如邮件截图、领导批示等）
}
```

**响应**: 同 `ApprovalActionResponse`

**cURL 示例**:

```bash
curl -X POST "$API_URL/approval/admin/pi-001/reject" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Request-Id: $(uuidgen)" \
  -H "X-Admin-Reason: 原审批人请假，紧急代审批" \
  -d '{
    "taskId": "task-001",
    "comment": "不符合报销标准，请修改后重新提交",
    "reason": "原审批人请假，紧急代审批"
  }'
```

**注意**: 此操作会记录详细审计日志，包含代审批原因和操作人信息。

---

### POST /admin/:instanceId/reassign - 重新分配任务

管理员将任务重新分配给其他审批人。

**权限**: `approval:admin`

**风险等级**: 🟡 低

**请求头**:

```http
Authorization: Bearer <token>
Content-Type: application/json
X-Request-Id: <uuid>
X-Admin-Reason: <reason>  # 可选，用于日志系统统一收集
```

**请求体**:

```typescript
interface AdminReassignRequest {
  taskId: string;             // 任务 ID（必填）
  newAssigneeId: string;      // 新审批人 ID（必填）
  reason: string;             // 重新分配原因（必填）
  notifyOriginal?: boolean;   // 是否通知原审批人（默认 true）
  notifyNew?: boolean;        // 是否通知新审批人（默认 true）
}
```

**响应**:

```typescript
interface AdminReassignResponse {
  success: boolean;
  taskId: string;
  originalAssignee: UserInfo;
  newAssignee: UserInfo;
  reassignedAt: string;
}
```

**cURL 示例**:

```bash
curl -X POST "$API_URL/approval/admin/pi-001/reassign" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Request-Id: $(uuidgen)" \
  -H "X-Admin-Reason: 原审批人离职，转交给部门其他同事" \
  -d '{
    "taskId": "task-001",
    "newAssigneeId": "user-002",
    "reason": "原审批人离职，转交给部门其他同事"
  }'
```

**注意**: 此操作会记录审计日志，原审批人和新审批人都会收到通知。

---

### GET /admin/audit-logs - 管理员操作日志

查询管理员操作日志。

**权限**: `approval:admin`

**HTTP 状态码**: `200 OK`

**查询参数**:

```typescript
interface AuditLogsQuery {
  // 分页
  page?: number;                // 页码，默认 1
  limit?: number;               // 每页数量，默认 20，最大 100
  
  // 筛选条件
  operatorId?: string;          // 操作人 ID
  action?: 'ADMIN_APPROVE' | 'ADMIN_REJECT' | 'ADMIN_TERMINATE' | 'ADMIN_REASSIGN';
  riskLevel?: 'HIGH' | 'MEDIUM' | 'LOW';  // 风险等级筛选
  instanceId?: string;          // 流程实例 ID
  businessType?: string;        // 业务类型
  businessId?: string;          // 业务单据 ID
  
  // 时间范围
  timeFrom?: string;            // 开始时间（ISO 8601）
  timeTo?: string;              // 结束时间（ISO 8601）
  
  // 排序
  sortBy?: 'actionTime' | 'riskLevel';  // 排序字段，默认 actionTime
  sortOrder?: 'asc' | 'desc';   // 排序方向，默认 desc
}
```

**响应**:

```typescript
interface AuditLogsResponse {
  items: AuditLogItem[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
  hasNext: boolean;
  hasPrev: boolean;
}

interface AuditLogItem {
  id: string;
  action: 'ADMIN_APPROVE' | 'ADMIN_REJECT' | 'ADMIN_TERMINATE' | 'ADMIN_REASSIGN';
  riskLevel: 'HIGH' | 'MEDIUM' | 'LOW';
  operator: UserInfo;
  instanceId: string;
  taskId?: string;
  
  // 业务信息
  businessType: string;
  businessId: string;
  businessKey?: string;
  processTitle?: string;        // 流程标题
  
  // 详细信息
  reason: string;              // 请求体中的 reason
  adminReason?: string;        // X-Admin-Reason header（如有）
  comment?: string;
  attachments?: Attachment[];  // 凭证附件
  
  // 重新分配相关
  originalAssignee?: UserInfo;
  newAssignee?: UserInfo;
  
  // 审计信息
  requestId: string;           // X-Request-Id
  ipAddress: string;
  userAgent: string;
  
  // 时间
  actionTime: string;
}
```

#### 📊 审计日志字段说明

| 字段 | 说明 | 用途 |
|------|------|------|
| `action` | 操作类型 | 区分不同管理员操作 |
| `riskLevel` | 风险等级 | 用于告警规则和报表统计 |
| `reason` | 操作原因（请求体） | 业务展示、合规审计 |
| `adminReason` | 操作原因（Header） | 日志系统统一收集 |
| `attachments` | 凭证附件 | 操作依据留存（如授权邮件、领导批示） |
| `requestId` | 请求 ID | 跨系统追踪、问题排查 |
| `ipAddress` | 操作人 IP | 安全审计、异常检测 |
| `userAgent` | 客户端信息 | 操作来源追踪 |

#### 🔍 常见查询场景

**场景 1: 查询某操作人的所有高危操作**

```bash
GET /admin/audit-logs?operatorId=user-001&riskLevel=HIGH
```

**场景 2: 查询某流程的所有管理员操作**

```bash
GET /admin/audit-logs?instanceId=pi-001
```

**场景 3: 查询某时间段内的强制终止操作**

```bash
GET /admin/audit-logs?action=ADMIN_TERMINATE&timeFrom=2024-12-01&timeTo=2024-12-07
```

**场景 4: 导出审计报表（按风险等级排序）**

```bash
GET /admin/audit-logs?sortBy=riskLevel&sortOrder=desc&limit=100
```

---

## ❌ 错误码

### 通用错误

| 错误码 | HTTP 状态 | 说明 |
|--------|----------|------|
| `UNAUTHORIZED` | 401 | 未认证 |
| `FORBIDDEN` | 403 | 无权限 |
| `NOT_FOUND` | 404 | 资源不存在 |
| `VALIDATION_ERROR` | 400 | 参数验证失败 |
| `INTERNAL_ERROR` | 500 | 服务器内部错误 |

### 流程相关错误

| 错误码 | HTTP 状态 | 说明 |
|--------|----------|------|
| `PROCESS_DEFINITION_NOT_FOUND` | 404 | 流程定义不存在 |
| `PROCESS_VERSION_NOT_FOUND` | 404 | 流程版本不存在 |
| `PROCESS_INSTANCE_NOT_FOUND` | 404 | 流程实例不存在 |
| `PROCESS_ALREADY_EXISTS` | 409 | 该业务单据已存在运行中的审批流程 |
| `PROCESS_NOT_RUNNING` | 400 | 流程已结束，无法操作 |
| `PROCESS_SUSPENDED` | 400 | 流程已暂停 |

### 任务相关错误

| 错误码 | HTTP 状态 | 说明 |
|--------|----------|------|
| `TASK_NOT_FOUND` | 404 | 任务不存在 |
| `TASK_ALREADY_COMPLETED` | 400 | 任务已完成 |
| `TASK_ALREADY_CLAIMED` | 400 | 任务已被他人认领 |
| `NOT_TASK_ASSIGNEE` | 403 | 非任务处理人 |
| `NOT_CANDIDATE` | 403 | 不在候选人列表中 |
| `CONCURRENT_MODIFICATION` | 409 | 并发冲突，任务已被修改 |

### 操作相关错误

| 错误码 | HTTP 状态 | 说明 |
|--------|----------|------|
| `WITHDRAW_NOT_ALLOWED` | 400 | 当前节点不允许撤回 |
| `NOT_INITIATOR` | 403 | 非发起人无法撤回 |
| `NEXT_NODE_ALREADY_PROCESSED` | 400 | 下一级已处理，无法撤回 |
| `WITHDRAW_TIME_EXCEEDED` | 400 | 超过撤回时限 |
| `APPROVER_WITHDRAW_DISABLED` | 400 | 审批人撤回功能未启用 |
| `INVALID_TARGET_NODE` | 400 | 无效的目标节点 |
| `RETURN_NOT_ALLOWED` | 400 | 不允许退回到该节点 |
| `REMIND_LIMIT_EXCEEDED` | 400 | 超过催办次数限制 |
| `REMIND_INTERVAL_TOO_SHORT` | 400 | 催办间隔过短 |

### 验证相关错误

| 错误码 | HTTP 状态 | 说明 |
|--------|----------|------|
| `VALIDATION_FAILED` | 400 | 节点验证失败 |
| `FIELD_REQUIRED` | 400 | 必填字段未填写 |
| `FIELD_NOT_MODIFIED` | 400 | 必须修改的字段未修改 |
| `COMMENT_REQUIRED` | 400 | 必须填写审批意见 |
| `ATTACHMENT_REQUIRED` | 400 | 必须上传附件 |

### 工作流相关错误

| 错误码 | HTTP 状态 | 说明 |
|--------|----------|------|
| `WORKFLOW_ERROR` | 500 | Temporal 工作流执行错误 |
| `WORKFLOW_NOT_FOUND` | 404 | 工作流不存在 |
| `WORKFLOW_TIMEOUT` | 504 | 工作流执行超时 |
| `MAX_NODE_EXECUTIONS_EXCEEDED` | 400 | 节点执行次数超限 |
| `MAX_RETURN_COUNT_EXCEEDED` | 400 | 退回次数超限 |

---

## 🔄 业务规则

### 流程实例状态流转

```
                    ┌──────────────────────────────────────────┐
                    │                                          │
                    ▼                                          │
RUNNING ──通过──> APPROVED                                     │
    │                                                          │
    ├──驳回──> REJECTED                                        │
    │                                                          │
    ├──撤回──> WITHDRAWN                                       │
    │                                                          │
    ├──超时──> TERMINATED 或 APPROVED (自动通过)               │
    │                                                          │
    ├──管理员终止──> TERMINATED                                │
    │                                                          │
    └──失败──> FAILED                                          │
                    │                                          │
                    └──────────── 退回 ────────────────────────┘
```

### 任务状态流转

```
CREATED ──分配──> PENDING ──认领──> CLAIMED ──处理中──> IN_PROGRESS
                    │                                      │
                    │                                      ├──通过/驳回/退回──> COMPLETED
                    │                                      │
                    │                                      └──转发──> PENDING (新任务)
                    │
                    └──候选人认领──> CLAIMED
```

### 版本隔离策略

> ⚠️ **重要**: 运行中的流程实例继续按**原版本**执行，新版本只影响新实例。

| 场景 | 行为 |
|------|------|
| 流程定义升级 | 新实例使用新版本，运行中实例继续用旧版本 |
| 流程定义回滚 | 同上，只影响新创建的实例 |
| 版本废弃 | 运行中实例继续执行，新实例无法使用该版本 |

### 幂等性规则

| 场景 | 处理策略 |
|------|----------|
| 相同 `X-Request-Id` 重复请求 | 返回首次请求的结果 |
| 任务已完成后重复操作 | 返回 `TASK_ALREADY_COMPLETED` 错误 |
| 并发操作同一任务 | 乐观锁检查，后到请求返回 `CONCURRENT_MODIFICATION` |

### 回调失败处理

| 阶段 | 策略 |
|------|------|
| 首次回调失败 | 自动重试 3 次（指数退避） |
| 重试仍失败 | 写入补偿队列 |
| 补偿队列处理 | 后台任务定期重试（最多 10 次） |
| 最终失败 | 发送告警通知，人工介入 |

> **注意**: 审批流程的最终状态（`APPROVED`/`REJECTED`/`WITHDRAWN`/`TERMINATED`）一旦设置，不因回调失败而回滚。

---

## 🎯 常用场景

### 场景 1: 表单提交触发审批

```
1. POST /api/v1/approval/start                    # 表单引擎调用，启动审批
   {
     "processDefinitionKey": "OVERTIME_APPROVAL",
     "businessType": "FORM_INSTANCE",
     "businessId": "form-instance-001",
     "variables": { "formData": {...} }
   }
2. 审批引擎创建流程实例，返回 instanceId
3. 表单引擎保存 approvalInstanceId 到 FormInstance
```

### 场景 2: 审批人处理任务

```
1. GET /api/v1/approval/my/pending               # 获取待办列表
2. GET /api/v1/approval/tasks/:taskId            # 获取任务详情
3. POST /api/v1/approval/:instanceId/approve     # 通过审批
   {
     "taskId": "task-001",
     "comment": "同意"
   }
```

### 场景 3: 发起人查看进度

```
1. GET /api/v1/approval/my/initiated             # 我发起的列表
2. GET /api/v1/approval/:instanceId/status       # 查看流程状态
3. GET /api/v1/approval/:instanceId/diagram      # 获取流程图数据
```

### 场景 4: 发起人撤回申请

```
1. POST /api/v1/approval/:instanceId/withdraw
   {
     "reason": "申请内容有误，需要修改"
   }
```

### 场景 5: 管理员强制结束流程

```
1. POST /api/v1/approval/admin/:instanceId/terminate
   {
     "reason": "业务需求变更，流程作废"
   }
```

### 场景 6: 连续上级审批

```
流程配置:
  assignee: 'initiator:managerChain'  # 默认到部门负责人

执行过程:
1. 系统解析发起人的上级链：组长 → 经理 → 总监(部门负责人)
2. 依次创建任务给每级审批人
3. 同一审批人连续出现时自动通过
4. 审批人是发起人时可配置自动通过
```

---

## 📝 数据类型定义

### 枚举类型

```typescript
// 流程实例状态
enum InstanceStatus {
  RUNNING = 'RUNNING',           // 审批中
  SUSPENDED = 'SUSPENDED',       // 已暂停
  APPROVED = 'APPROVED',         // 已通过（所有节点同意）
  REJECTED = 'REJECTED',         // 已拒绝（某节点驳回）
  WITHDRAWN = 'WITHDRAWN',       // 已撤回（发起人撤回）
  TERMINATED = 'TERMINATED',     // 已终止（管理员强制终止）
  FAILED = 'FAILED',             // 失败（系统错误）
}

// 节点状态
enum NodeStatus {
  PENDING = 'PENDING',
  ACTIVE = 'ACTIVE',
  COMPLETED = 'COMPLETED',
  CANCELLED = 'CANCELLED',
  FAILED = 'FAILED',
  SKIPPED = 'SKIPPED',
}

// 任务状态
enum TaskStatus {
  CREATED = 'CREATED',
  PENDING = 'PENDING',
  CLAIMED = 'CLAIMED',
  IN_PROGRESS = 'IN_PROGRESS',
  COMPLETED = 'COMPLETED',
  CANCELLED = 'CANCELLED',
  WITHDRAWN = 'WITHDRAWN',
}

// 任务类型
enum TaskType {
  APPROVAL = 'APPROVAL',
  COUNTERSIGN = 'COUNTERSIGN',
  OR_SIGN = 'OR_SIGN',
  CC = 'CC',
  SEQUENTIAL = 'SEQUENTIAL',
}

// 节点类型
enum NodeType {
  START = 'START',
  END = 'END',
  USER_TASK = 'USER_TASK',
  SERVICE_TASK = 'SERVICE_TASK',
  EXCLUSIVE_GATEWAY = 'EXCLUSIVE_GATEWAY',
  PARALLEL_GATEWAY = 'PARALLEL_GATEWAY',
  INCLUSIVE_GATEWAY = 'INCLUSIVE_GATEWAY',
  SUB_PROCESS = 'SUB_PROCESS',
}

// 审批模式
enum ApprovalMode {
  SINGLE = 'SINGLE',
  AND = 'AND',
  OR = 'OR',
  SEQUENTIAL = 'SEQUENTIAL',
}

// 审批操作
enum ApprovalAction {
  APPROVE = 'APPROVE',
  REJECT = 'REJECT',
  RETURN = 'RETURN',
  FORWARD = 'FORWARD',
  WITHDRAW = 'WITHDRAW',
  APPROVER_WITHDRAW = 'APPROVER_WITHDRAW',
  ADD_SIGN = 'ADD_SIGN',
  REMOVE_SIGN = 'REMOVE_SIGN',
  CLAIM = 'CLAIM',
  UNCLAIM = 'UNCLAIM',
  AUTO_APPROVE = 'AUTO_APPROVE',
  AUTO_REJECT = 'AUTO_REJECT',
  ESCALATE = 'ESCALATE',
  REMIND = 'REMIND',
  ADMIN_APPROVE = 'ADMIN_APPROVE',
  ADMIN_TERMINATE = 'ADMIN_TERMINATE',
}

// 流程定义状态
enum ProcessStatus {
  ACTIVE = 'ACTIVE',
  SUSPENDED = 'SUSPENDED',
  ARCHIVED = 'ARCHIVED',
}

// 版本状态
enum VersionStatus {
  DRAFT = 'DRAFT',
  DEPLOYED = 'DEPLOYED',
  SUPERSEDED = 'SUPERSEDED',
  ARCHIVED = 'ARCHIVED',
}
```

---

## 📚 相关文档

- [PRD 需求文档](PRD.md)
- [架构设计](ARCHITECTURE.md)
- [开发待办](TODO.md)
- [流程配置指南](PROCESS_CONFIG_GUIDE.md)

---

**创建日期**: 2024-11-15  
**最后更新**: 2025-12-07  
**版本**: v3.0
