# 表单引擎架构设计

> 技术架构文档 - 系统架构、数据模型、核心设计决策

---

## 📐 系统架构

### 整体架构

```
┌─────────────────────────────────────────────────────────────┐
│                        前端                                  │
├─────────────────────────────────────────────────────────────┤
│  FormDesigner          │  FormRenderer        │  Pages      │
│  ├─ FieldPalette       │  ├─ JSON Schema 解析  │  ├─ 列表    │
│  ├─ DesignCanvas       │  ├─ 字段渲染          │  ├─ 详情    │
│  └─ PropertyPanel      │  └─ 验证处理          │  └─ 设计    │
├─────────────────────────────────────────────────────────────┤
│                      API Client                              │
│  forms.ts - 44 个接口                                        │
├─────────────────────────────────────────────────────────────┤
│                        后端                                  │
├─────────────────────────────────────────────────────────────┤
│  FormDefinitionController  │  FormInstanceController         │
│  FormVersionController     │  FormTemplateController         │
│  FormTranslationController │  FormApprovalCallbackController │
├─────────────────────────────────────────────────────────────┤
│  FormDefinitionService     │  FormInstanceService            │
│  FormVersionService        │  FormTemplateService            │
│  FormTranslationService    │  FormApprovalIntegrationService │
├─────────────────────────────────────────────────────────────┤
│                     Prisma ORM                               │
├─────────────────────────────────────────────────────────────┤
│                PostgreSQL (platform_form schema)             │
└─────────────────────────────────────────────────────────────┘
```

### ⚠️ 边界声明

> **表单引擎仅负责「表单结构定义」与「数据采集」，不负责业务含义与流程逻辑。**

| 表单引擎负责 | 表单引擎不负责 |
|-------------|---------------|
| ✅ 表单结构定义（JSON Schema） | ❌ 业务逻辑（如加班时长计算） |
| ✅ 表单版本管理 | ❌ 审批流程编排 |
| ✅ 数据采集与存储 | ❌ 业务数据写入（如 ERP 同步） |
| ✅ 前后端数据验证 | ❌ 业务规则校验（如金额限制） |
| ✅ 提供标准化集成接口 | ❌ 具体业务系统实现 |

**集成方式**：
- 审批流程：通过 `approvalProcessKey` 配置，由审批引擎处理
- 业务写入：通过 Webhook / Event 通知业务系统
- 业务规则：由业务系统在集成层实现

---

## 🔄 核心流程

### 表单版本审核发布流程

```
┌─────────────────────────────────────────────────────────────────────┐
│                         设计者操作                                   │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   [创建表单] ──> [拖拽设计] ──> [保存草稿] ──> [提交审核]              │
│       │              │             │              │                  │
│       ▼              ▼             ▼              ▼                  │
│   FormDef        设计器编辑    FormVersion    FormVersion            │
│   (DRAFT)                      (DRAFT)       (PENDING_REVIEW)        │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────┐
│                         管理员操作                                   │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   [待审核列表] ──> [预览表单] ──> [审核决策]                          │
│                                      │                               │
│                          ┌───────────┴───────────┐                   │
│                          │                       │                   │
│                          ▼                       ▼                   │
│                     [审核通过]              [审核驳回]                │
│                          │                       │                   │
│                          ▼                       ▼                   │
│                   FormVersion              FormVersion               │
│                   (PUBLISHED)              (REJECTED)                │
│                          │                       │                   │
│                          ▼                       ▼                   │
│                   [发布表单]              返回给设计者修改             │
│                          │                                           │
│                          ▼                                           │
│                   FormDefinition                                     │
│                   (PUBLISHED)                                        │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────┐
│                         普通用户操作                                 │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   [查看可用表单] ──> [动态渲染] ──> [填写数据] ──> [提交]              │
│         │                │              │            │               │
│         ▼                ▼              ▼            ▼               │
│   只显示 PUBLISHED   读取 Schema    前端验证    FormInstance          │
│   状态的表单         动态生成 UI    实时校验    (SUBMITTED)           │
│                                                      │               │
│                                                      ▼               │
│                                              (可选) 启动审批          │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘
```

### 与审批流集成

```
┌─────────────────┐      提交      ┌─────────────────┐      启动      ┌─────────────────┐
│   前端表单      │ ──────────────> │   表单引擎      │ ──────────────> │   审批流引擎    │
│   FormRenderer  │                 │   FormInstance  │                 │   ApprovalFlow  │
└─────────────────┘                 └─────────────────┘                 └─────────────────┘
                                            │                                    │
                                            │        ┌───────────────────────────┘
                                            │        │  回调通知
                                            ▼        ▼
                                    ┌─────────────────────┐
                                    │   状态同步服务      │
                                    │   (Webhook/Event)   │
                                    └─────────────────────┘
                                            │
                                            ▼
                                    ┌─────────────────────┐
                                    │   业务系统集成      │
                                    │   (可选)           │
                                    └─────────────────────┘
```

---

## 🗄️ 数据模型

### ER 图

```
┌─────────────────────┐       1:N        ┌─────────────────────┐
│   FormDefinition    │◄─────────────────│    FormVersion      │
├─────────────────────┤                  ├─────────────────────┤
│ id (PK)             │                  │ id (PK)             │
│ key (UNIQUE)        │                  │ definitionId (FK)   │
│ slug (UNIQUE)       │                  │ version             │
│ name                │                  │ schema (JSON)       │
│ category            │                  │ status              │
│ status              │                  │ reviewedBy          │
│ requiresApproval    │                  │ reviewComment       │
│ approvalProcessKey  │                  │ isDefault           │
│ latestVersion       │                  │ publishedAt         │
└─────────────────────┘                  └─────────────────────┘
         │                                        │
         │ 1:N                                    │ 1:N
         ▼                                        ▼
┌─────────────────────┐                  ┌─────────────────────┐
│   FormInstance      │                  │  FormTranslation    │
├─────────────────────┤                  ├─────────────────────┤
│ id (PK)             │                  │ id (PK)             │
│ formDefinitionId(FK)│                  │ versionId (FK)      │
│ formVersionId (FK)  │ ◄── 不可变       │ locale              │
│ businessKey(UNIQUE) │                  │ translations (JSON) │
│ regionId            │ ◄── 区域隔离     └─────────────────────┘
│ data (JSON)         │ ◄── GIN 索引（规划）
│ status              │
│ approvalInstanceId  │
│ deletedAt           │ ◄── 软删除
└─────────────────────┘
```

### 核心实体

#### 1. FormDefinition（表单定义）

表单的元信息，不包含具体结构。

```typescript
interface FormDefinition {
  id: string;                    // UUID
  key: string;                   // 唯一标识（不可变）
  slug: string;                  // URL 友好标识（可变）
  slugHistory: string[];         // Slug 历史记录
  aliases: string[];             // 业务别名
  name: string;                  // 表单名称
  category: string;              // 分类
  description?: string;
  icon?: string;
  color?: string;
  defaultLocale: string;         // 默认语言
  supportedLocales: string[];    // 支持的语言
  latestVersion: number;         // 最新版本号
  status: FormStatus;            // DRAFT | PUBLISHED | ARCHIVED
  requiresApproval: boolean;     // 是否需要审批
  approvalProcessKey?: string;   // 审批流程 Key
  createdBy: string;
  createdAt: Date;
  updatedAt: Date;
}
```

#### 2. FormVersion（表单版本）

表单的具体结构定义，与版本绑定。

```typescript
interface FormVersion {
  id: string;
  definitionId: string;
  version: number;               // 版本号（从 1 开始）
  nameI18n: Record<string, string>;        // 名称多语言
  descriptionI18n?: Record<string, string>;
  schema: FormSchema;            // JSON Schema
  uiSchema?: UISchema;           // UI 配置
  validation?: object;           // 验证规则
  changelog?: string;            // 变更日志
  isDefault: boolean;            // 是否默认版本
  status: VersionStatus;         // DRAFT | PENDING_REVIEW | PUBLISHED | REJECTED | DEPRECATED
  
  // 审核相关
  reviewedBy?: string;           // 审核人
  reviewedAt?: Date;             // 审核时间
  reviewComment?: string;        // 审核意见
  
  createdBy: string;
  createdAt: Date;
  publishedAt?: Date;
}
```

#### 3. FormInstance（表单实例）

用户填写的表单数据。

```typescript
interface FormInstance {
  id: string;
  formDefinitionId: string;      // 表单定义 ID（强约束）
  formVersionId: string;         // 版本 ID（强约束，不可变）
  formKey: string;               // Key 快照
  formVersion: number;           // 版本号快照
  businessKey: string;           // 业务单号（唯一）
  regionId?: string;             // 区域 ID（多区域场景，规划中）
  data: Record<string, any>;     // 表单数据
  status: InstanceStatus;        // DRAFT | SUBMITTED | PENDING_APPROVAL | APPROVED | REJECTED | CANCELLED
  createdBy: string;
  submittedBy?: string;
  submittedAt?: Date;
  approvalInstanceId?: string;   // 关联审批实例
  approvalStatus?: string;       // 审批状态快照
  createdAt: Date;
  updatedAt: Date;
  deletedAt?: Date;              // 软删除
}
```

### 状态枚举

#### FormStatus（表单定义状态）

```typescript
enum FormStatus {
  DRAFT = 'DRAFT',           // 草稿
  PUBLISHED = 'PUBLISHED',   // 已发布
  ARCHIVED = 'ARCHIVED',     // 已归档
}
```

#### VersionStatus（版本状态）

```typescript
enum VersionStatus {
  DRAFT = 'DRAFT',                   // 草稿（设计中）
  PENDING_REVIEW = 'PENDING_REVIEW', // 待审核
  PUBLISHED = 'PUBLISHED',           // 已发布
  REJECTED = 'REJECTED',             // 已驳回
  DEPRECATED = 'DEPRECATED',         // 已废弃
}
```

#### InstanceStatus（实例状态）

```typescript
enum InstanceStatus {
  DRAFT = 'DRAFT',                   // 草稿
  SUBMITTED = 'SUBMITTED',           // 已提交（无审批）
  PENDING_APPROVAL = 'PENDING_APPROVAL', // 审批中
  APPROVED = 'APPROVED',             // 已通过
  REJECTED = 'REJECTED',             // 已驳回
  WITHDRAWN = 'WITHDRAWN',           // 申请人主动撤回
  CANCELLED = 'CANCELLED',           // 管理员强制取消 / 系统超时
}
```

### 业务单号（businessKey）

`FormInstance.businessKey` 用于与外部业务系统和审批流程关联：

| 示例 | 说明 |
|------|------|
| `overtime_2025_0001` | 加班申请单号 |
| `expense_2025_0042` | 报销单号 |
| `leave_2025_0103` | 请假单号 |

**生成规则**（可配置）：
```typescript
// 默认格式
const businessKey = `${formKey}_${year}_${paddedSequence}`;

// 示例实现
async function generateBusinessKey(formKey: string): Promise<string> {
  const year = new Date().getFullYear();
  const sequence = await getNextSequence(formKey, year);
  return `${formKey}_${year}_${String(sequence).padStart(4, '0')}`;
}
```

**用途**：
- 审批系统关联
- 业务系统查询
- 日志追踪
- 用户可见的单号

---

## 📊 数据库设计

### Prisma Schema

```prisma
// platform_form.prisma

model FormDefinition {
  id                String   @id @default(uuid()) @db.Uuid
  key               String   @unique
  slug              String   @unique
  slugHistory       Json     @default("[]") @map("slug_history")
  aliases           Json     @default("[]")
  
  name              String
  category          String
  description       String?  @db.Text
  icon              String?
  color             String?
  
  defaultLocale     String   @default("zh-CN") @map("default_locale")
  supportedLocales  Json     @default("[\"zh-CN\"]") @map("supported_locales")
  
  latestVersion     Int      @default(1) @map("latest_version")
  status            FormStatus @default(DRAFT)
  
  requiresApproval  Boolean  @default(false) @map("requires_approval")
  approvalProcessKey String? @map("approval_process_key")
  
  createdBy         String?  @map("created_by") @db.Uuid
  updatedBy         String?  @map("updated_by") @db.Uuid
  createdAt         DateTime @default(now()) @map("created_at")
  updatedAt         DateTime @updatedAt @map("updated_at")
  
  versions          FormVersion[]
  instances         FormInstance[]
  
  @@map("form_definitions")
  @@schema("platform_form")
}

model FormVersion {
  id              String   @id @default(uuid()) @db.Uuid
  definitionId    String   @map("definition_id") @db.Uuid
  version         Int
  
  nameI18n        Json     @map("name_i18n")
  descriptionI18n Json?    @map("description_i18n")
  
  schema          Json
  uiSchema        Json?    @map("ui_schema")
  validation      Json?
  
  changelog       String?  @db.Text
  isDefault       Boolean  @default(false) @map("is_default")
  status          VersionStatus @default(DRAFT)
  
  // 审核相关
  reviewedBy      String?  @map("reviewed_by") @db.Uuid
  reviewedAt      DateTime? @map("reviewed_at")
  reviewComment   String?  @map("review_comment") @db.Text
  
  createdBy       String?  @map("created_by") @db.Uuid
  createdAt       DateTime @default(now()) @map("created_at")
  publishedAt     DateTime? @map("published_at")
  
  definition      FormDefinition @relation(fields: [definitionId], references: [id])
  translations    FormTranslation[]
  
  @@unique([definitionId, version])
  @@map("form_versions")
  @@schema("platform_form")
}

model FormInstance {
  id                 String   @id @default(uuid()) @db.Uuid
  formDefinitionId   String   @map("form_definition_id") @db.Uuid
  formVersionId      String   @map("form_version_id") @db.Uuid
  formKey            String   @map("form_key")
  formVersion        Int      @map("form_version")
  businessKey        String   @unique @map("business_key")
  regionId           String?  @map("region_id") @db.Uuid  // 区域支持（规划中）
  
  data               Json
  status             InstanceStatus @default(DRAFT)
  
  createdBy          String   @map("created_by") @db.Uuid
  updatedBy          String?  @map("updated_by") @db.Uuid
  submittedBy        String?  @map("submitted_by") @db.Uuid
  submittedAt        DateTime? @map("submitted_at")
  
  approvalInstanceId String?  @map("approval_instance_id") @db.Uuid
  approvalStatus     String?  @map("approval_status")
  approvalStartTime  DateTime? @map("approval_start_time")
  approvalEndTime    DateTime? @map("approval_end_time")
  
  createdAt          DateTime @default(now()) @map("created_at")
  updatedAt          DateTime @updatedAt @map("updated_at")
  deletedAt          DateTime? @map("deleted_at")
  
  definition         FormDefinition @relation(fields: [formDefinitionId], references: [id])
  
  @@index([formKey])
  @@index([businessKey])
  @@index([createdBy])
  @@index([status])
  @@index([approvalInstanceId])
  @@index([regionId])  // 区域查询优化
  @@map("form_instances")
  @@schema("platform_form")
}

enum FormStatus {
  DRAFT
  PUBLISHED
  ARCHIVED
  @@schema("platform_form")
}

enum VersionStatus {
  DRAFT
  PENDING_REVIEW
  PUBLISHED
  REJECTED
  DEPRECATED
  @@schema("platform_form")
}

enum InstanceStatus {
  DRAFT
  SUBMITTED
  PENDING_APPROVAL
  APPROVED
  REJECTED
  CANCELLED
  @@schema("platform_form")
}
```

---

## 📈 索引策略建议

### JSONB 字段索引（规划中）

`FormInstance.data` 是核心的 JSONB 字段，随着数据量增长，需要考虑索引优化：

#### 1. GIN 索引

适用于通用的 JSON 查询场景：

```sql
-- 通用 GIN 索引（支持 @>, ?, ?| 等操作符）
CREATE INDEX idx_form_instances_data_gin 
ON platform_form.form_instances 
USING GIN (data jsonb_path_ops);
```

**适用场景**：
- 查询包含特定键值的记录
- 全文搜索 JSON 内容
- 不确定查询模式时的通用方案

#### 2. Expression Index（表达式索引）

针对高频查询字段创建精确索引：

```sql
-- 按提交人姓名查询（假设 data 中有 submitterName 字段）
CREATE INDEX idx_form_instances_data_submitter 
ON platform_form.form_instances 
((data->>'submitterName'));

-- 按金额范围查询
CREATE INDEX idx_form_instances_data_amount 
ON platform_form.form_instances 
(((data->>'amount')::numeric));

-- 按日期查询
CREATE INDEX idx_form_instances_data_date 
ON platform_form.form_instances 
(((data->>'applyDate')::date));
```

**适用场景**：
- 明确知道高频查询字段
- 需要范围查询（数值、日期）
- 需要排序

#### 3. 索引策略选择

| 场景 | 推荐索引 | 说明 |
|------|---------|------|
| 初期/不确定查询模式 | GIN | 通用性强，覆盖多种查询 |
| 高频特定字段查询 | Expression Index | 性能更优，但需明确字段 |
| 全文搜索 | GIN + to_tsvector | 支持模糊匹配 |
| 复合条件查询 | 多个 Expression Index | 按查询频率优先 |

#### 4. 注意事项

- GIN 索引会增加写入开销，大量写入时需评估
- Expression Index 需要确保查询时使用相同表达式
- 定期使用 `EXPLAIN ANALYZE` 验证索引效果
- 建议在迁移脚本中创建，而非 Prisma Schema（Prisma 不支持 GIN 索引语法）

---

## 🖥️ 前端渲染模式

### FormRenderer 渲染模式

`FormRenderer` 组件需要从一开始就支持多种渲染模式：

```typescript
interface FormRendererProps {
  schema: FormSchema;
  uiSchema?: UISchema;
  viewSchema?: ViewSchema;  // 详情视图配置
  data?: Record<string, any>;
  mode: 'edit' | 'readonly' | 'view';  // 渲染模式
  onSubmit?: (data: Record<string, any>) => void;
  onChange?: (data: Record<string, any>) => void;
}
```

#### 渲染模式说明

| 模式 | 说明 | 使用场景 |
|------|------|----------|
| `edit` | 可编辑模式 | 用户填写表单、编辑草稿 |
| `readonly` | 只读模式 | 历史记录查看、审批详情 |
| `view` | 详情视图模式 | 使用 viewSchema 定制布局 |

#### readonly 模式特点

```typescript
// readonly 模式下的字段渲染
const renderField = (field: FieldSchema, value: any, mode: string) => {
  if (mode === 'readonly') {
    return (
      <div className="form-field-readonly">
        <label>{field.label}</label>
        <div className="value">{formatValue(value, field.type)}</div>
      </div>
    );
  }
  // edit 模式渲染可编辑控件...
};
```

**优化点**：
- 无需渲染表单控件，使用纯文本/格式化显示
- 隐藏验证逻辑，提升渲染性能
- 支持打印友好的样式

### ViewSchema 详情视图配置

`viewSchema` 用于定制详情查看时的布局和字段可见性：

```typescript
interface ViewSchema {
  // 隐藏的字段（内部字段不对外展示）
  hiddenFields?: string[];
  
  // 字段分组
  groups?: {
    title: string;
    fields: string[];
    collapsed?: boolean;  // 默认折叠
  }[];
  
  // 布局调整
  layout?: {
    columns?: 1 | 2 | 3;
    labelPosition?: 'top' | 'left';
  };
  
  // 字段显示覆盖
  fieldOverrides?: {
    [fieldName: string]: {
      label?: string;       // 覆盖标签
      format?: string;      // 格式化模式
      hidden?: boolean;     // 隐藏
    };
  };
}
```

**示例**：

```typescript
const viewSchema: ViewSchema = {
  // 隐藏内部字段
  hiddenFields: ['internalNote', 'systemFlag'],
  
  // 重新分组
  groups: [
    { title: '基本信息', fields: ['name', 'department', 'date'] },
    { title: '申请详情', fields: ['reason', 'amount'] },
    { title: '附件', fields: ['attachments'], collapsed: true },
  ],
  
  // 两列布局
  layout: { columns: 2, labelPosition: 'left' },
  
  // 字段覆盖
  fieldOverrides: {
    amount: { format: 'currency' },  // 金额格式化
    date: { format: 'YYYY年MM月DD日' },
  },
};
```

**使用场景**：
- 审批详情页面：隐藏内部备注，重新排列布局
- 历史记录查看：简化展示，突出关键信息
- 打印视图：调整为打印友好的布局

---

## 🌍 区域设计（规划中）

### 概述

为支持多区域差异化表单场景，系统需要在两个层面增加区域支持：

1. **FormDefinition 层面**：同一表单可在多个区域使用，也可创建区域专属表单
2. **FormInstance 层面**：记录表单实例所属区域，便于数据隔离和统计

### 业务场景

| 场景 | 说明 | 示例 |
|------|------|------|
| 共享表单 | 同一表单多区域使用 | 「请假申请」全公司通用 |
| 区域专属表单 | 某区域特有的表单 | 「华东区采购申请」仅华东使用 |
| 区域差异化审批 | 相同表单，不同区域不同审批流 | 「报销」北京区和上海区审批人不同 |
| 区域数据统计 | 按区域统计表单提交情况 | 各区域月度加班申请统计 |

### 数据模型扩展

#### FormDefinition 区域配置

```typescript
interface FormDefinition {
  // ... 其他字段
  
  // 区域配置
  regionScope: 'ALL' | 'SPECIFIC';  // 全部区域 | 指定区域
  allowedRegions?: string[];         // 允许使用的区域列表（regionScope=SPECIFIC 时）
  
  // 区域差异化审批配置
  regionApprovalConfig?: {
    [regionId: string]: {
      approvalProcessKey: string;  // 该区域使用的审批流程
    };
  };
}
```

#### FormInstance 区域记录

```typescript
interface FormInstance {
  // ... 其他字段
  regionId?: string;  // 提交时所属区域
}
```

### 区域与审批流的关系

```
┌─────────────────────────────────────────────────────────────────────┐
│                      表单提交时的区域处理                            │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   用户提交表单                                                       │
│        │                                                             │
│        ▼                                                             │
│   获取用户所属区域 (user.regionId)                                   │
│        │                                                             │
│        ▼                                                             │
│   检查表单是否允许该区域使用                                          │
│        │                                                             │
│        ├── 不允许 → 返回错误                                         │
│        │                                                             │
│        ▼                                                             │
│   记录 formInstance.regionId = user.regionId                        │
│        │                                                             │
│        ▼                                                             │
│   获取区域对应的审批流程                                              │
│        │                                                             │
│        ├── 有区域配置 → 使用 regionApprovalConfig[regionId]          │
│        │                                                             │
│        └── 无区域配置 → 使用默认 approvalProcessKey                  │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘
```

### 实现要点

#### 1. 表单可见性控制

```typescript
// 获取用户可用的表单列表
async getAvailableForms(user: User): Promise<FormDefinition[]> {
  return prisma.formDefinition.findMany({
    where: {
      status: 'PUBLISHED',
      OR: [
        { regionScope: 'ALL' },
        { 
          regionScope: 'SPECIFIC',
          allowedRegions: { has: user.regionId }
        },
      ],
    },
  });
}
```

#### 2. 提交时区域处理

```typescript
async submitForm(instanceId: string, user: User) {
  const instance = await this.findById(instanceId);
  const form = await this.formDefinitionService.findById(instance.formDefinitionId);
  
  // 检查区域权限
  if (form.regionScope === 'SPECIFIC' && 
      !form.allowedRegions.includes(user.regionId)) {
    throw new BusinessException('FORM_NOT_AVAILABLE_IN_REGION');
  }
  
  // 记录区域
  instance.regionId = user.regionId;
  
  // 获取区域对应的审批流程
  const approvalKey = form.regionApprovalConfig?.[user.regionId]?.approvalProcessKey 
    || form.approvalProcessKey;
  
  // 启动审批...
}
```

#### 3. 索引优化

```sql
-- 复合索引：区域 + 状态
CREATE INDEX idx_form_instances_region_status 
ON platform_form.form_instances (region_id, status);

-- 复合索引：区域 + 创建时间（区域数据统计）
CREATE INDEX idx_form_instances_region_created 
ON platform_form.form_instances (region_id, created_at DESC);

-- 复合索引：表单 + 区域（区域表单统计）
CREATE INDEX idx_form_instances_form_region 
ON platform_form.form_instances (form_definition_id, region_id);
```

#### 4. 区域数据统计

```typescript
// 按区域统计表单提交情况
async getRegionStatistics(formId: string, dateRange: DateRange) {
  return prisma.formInstance.groupBy({
    by: ['regionId'],
    where: {
      formDefinitionId: formId,
      submittedAt: { gte: dateRange.start, lte: dateRange.end },
    },
    _count: { id: true },
  });
}
```

### 区域配置示例

```typescript
// 全公司通用表单
const leaveForm: FormDefinition = {
  key: 'leave-request',
  name: '请假申请',
  regionScope: 'ALL',
  approvalProcessKey: 'leave-approval-default',
  // 不同区域不同审批流
  regionApprovalConfig: {
    'region-beijing': { approvalProcessKey: 'leave-approval-beijing' },
    'region-shanghai': { approvalProcessKey: 'leave-approval-shanghai' },
  },
};

// 区域专属表单
const eastChinaPurchase: FormDefinition = {
  key: 'east-china-purchase',
  name: '华东区采购申请',
  regionScope: 'SPECIFIC',
  allowedRegions: ['region-shanghai', 'region-hangzhou', 'region-nanjing'],
  approvalProcessKey: 'purchase-approval-east',
};

---

## 🎯 关键设计决策

### 1. FormKey 不可变性

**决策**: `FormDefinition.key` 一旦创建，永不修改。

**原因**:
- 表单实例通过 `formKey` 引用表单定义
- 外部系统可能通过 `key` 集成
- 确保历史数据可追溯

**替代方案**:
- 使用 `slug` 作为可变的 URL 友好标识
- 使用 `aliases` 支持业务别名
- 旧 slug 自动加入 `slugHistory` 支持重定向

### 2. FormInstance.formVersionId 不可变性

**决策**: `FormInstance` 一旦创建，`formVersionId` 不可变（除非执行数据迁移）。

**原因**:
- ✅ **历史可追溯** - 任何时候都能还原当时的表单结构
- ✅ **数据一致性** - 历史数据永远按**当时所用的版本** Schema 渲染和校验
- ✅ **审计合规** - 满足合规审计对数据不可篡改的要求

**重要规则**:
- 查看历史表单时，系统自动使用提交时绑定的版本进行渲染
- 历史数据**不随最新 Schema 变化**
- 如需迁移数据到新版本，需显式执行迁移操作并保留迁移记录

### 3. 多语言策略：版本级 i18n

**决策**: 翻译与 `FormVersion` 绑定，而非 `FormDefinition`。

**原因**:
- 不同版本可能有不同的字段，需要不同的翻译
- 版本发布后翻译应固定，避免影响已提交的实例
- 支持翻译的版本控制

**实现**:
```typescript
// 版本级多语言
FormVersion.nameI18n = { "zh-CN": "报销申请", "en-US": "Expense Report" }

// 字段级翻译
FormTranslation = {
  versionId: "xxx",
  locale: "zh-CN",
  translations: {
    "fields.amount.label": "金额",
    "fields.amount.placeholder": "请输入金额"
  }
}
```

### 4. 软删除策略

**决策**: `FormInstance` 使用软删除（`deletedAt`），`FormDefinition` 不使用。

**原因**:
- 表单实例是业务数据，需要保留审计追踪
- 表单定义是配置数据，删除前需先归档
- 软删除支持数据恢复

**规则**:
- 有实例的表单定义不可删除，只能归档
- 已提交的实例不可硬删除

### 5. 应用层默认值

**决策**: 默认值在应用层处理，而非数据库层。

**原因**:
- Prisma JSON 字段的默认值有限制
- 应用层可以根据上下文动态设置
- 便于单元测试

**实现**:
```typescript
// Service 层设置默认值
const createFormDefinition = (dto: CreateFormDto) => {
  return prisma.formDefinition.create({
    data: {
      key: dto.key || generateKey(),
      slug: dto.slug,
      slugHistory: [],
      aliases: dto.aliases || [],
      defaultLocale: dto.defaultLocale || 'zh-CN',
      supportedLocales: dto.supportedLocales || ['zh-CN'],
      // ...
    }
  });
};
```

### 6. 发布语义："发布版本" vs "发布表单"

**决策**: 分离版本发布和表单发布两个动作。

**版本发布** (`POST /forms/:id/versions/:v/publish`):
- 改变 `FormVersion.status = PUBLISHED`
- 使版本可被表单实例引用
- 不影响表单定义状态

**表单发布** (`POST /forms/:id/publish`):
- 改变 `FormDefinition.status = PUBLISHED`
- 使表单对用户可见
- 前置条件：必须有已发布的默认版本

**流程**:
```
1. 创建表单 (DRAFT)
2. 设计版本 v1 (DRAFT)
3. 提交审核 (PENDING_REVIEW)
4. 管理员审核通过 (PUBLISHED, setAsDefault: true)
5. 发布表单 → 用户可见
```

### 7. 版本审核机制

**决策**: 表单版本发布前需经过管理员审核。

**状态流转**:
```
DRAFT ──提交审核──> PENDING_REVIEW ──审核通过──> PUBLISHED
                         │
                         └──审核驳回──> REJECTED ──修改后重新提交──> PENDING_REVIEW
```

**审核记录**:
```typescript
interface VersionReview {
  reviewedBy: string;      // 审核人 ID
  reviewedAt: Date;        // 审核时间
  reviewComment?: string;  // 审核意见
}
```

**权限控制**:
| 操作 | 角色 | 权限 |
|------|------|------|
| 创建/设计表单 | 设计者 | `form:design` |
| 提交审核 | 设计者 | `form:design` |
| 审核通过/驳回 | 管理员 | `form:admin` |
| 发布/归档表单 | 管理员 | `form:admin` |

---

## 🔗 审批流集成

### 提交时启动审批

```typescript
// 提交表单时
async submitWithApproval(instance: FormInstance, form: FormDefinition) {
  // 1. 检查是否需要审批
  if (!form.requiresApproval) {
    return { needsApproval: false };
  }
  
  // 2. 启动审批流
  const approval = await approvalService.startApproval({
    processDefinitionKey: form.approvalProcessKey,
    businessType: 'FORM_INSTANCE',
    businessId: instance.id,
    businessKey: instance.businessKey,
    variables: {
      formKey: form.key,
      formData: instance.data,
      submitterId: instance.submittedBy,
    },
    initiatorId: instance.submittedBy,
  });
  
  // 3. 更新表单实例
  await prisma.formInstance.update({
    where: { id: instance.id },
    data: {
      status: 'PENDING_APPROVAL',
      approvalInstanceId: approval.id,
      approvalStatus: 'RUNNING',
    },
  });
}
```

### 状态映射

| FormInstanceStatus | ApprovalInstanceStatus | 说明 |
|-------------------|------------------------|------|
| `DRAFT` | - | 草稿 |
| `SUBMITTED` | - | 已提交（无审批） |
| `PENDING_APPROVAL` | `RUNNING` | 审批中 |
| `APPROVED` | `APPROVED` | 审批通过 |
| `REJECTED` | `REJECTED` | 审批驳回 |
| `WITHDRAWN` | `WITHDRAWN` | 申请人主动撤回（统一语义） |
| `CANCELLED` | `TERMINATED` | 管理员强制终止 / 系统超时（被动取消） |

---

## 📁 代码结构

### 后端

```
backend/src/form-engine/
├── controllers/
│   ├── form-definition.controller.ts
│   ├── form-version.controller.ts
│   ├── form-instance.controller.ts
│   ├── form-translation.controller.ts
│   ├── form-template.controller.ts
│   └── form-approval-callbacks.controller.ts
├── services/
│   ├── form-definition.service.ts
│   ├── form-version.service.ts
│   ├── form-instance.service.ts
│   ├── form-translation.service.ts
│   ├── form-template.service.ts
│   ├── form-approval-integration.service.ts
│   ├── form-identifier-resolver.service.ts
│   └── form-business-key.service.ts    # 业务单号生成
├── dto/
│   ├── create-form-definition.dto.ts
│   ├── update-form-definition.dto.ts
│   ├── create-form-version.dto.ts
│   ├── submit-for-review.dto.ts        # 提交审核
│   ├── review-version.dto.ts           # 审核版本
│   ├── create-form-instance.dto.ts
│   └── approval-callback.dto.ts
└── types/
    └── form-schema.types.ts
```

### 前端

```
frontend/src/
├── app/forms/
│   ├── layout.tsx                    # 二级导航
│   ├── page.tsx                      # 概览
│   ├── definitions/
│   │   ├── page.tsx                  # 列表
│   │   ├── new/page.tsx              # 创建
│   │   └── [id]/
│   │       ├── design/page.tsx       # 设计器
│   │       └── versions/page.tsx     # 版本管理
│   ├── review/                       # 审核管理（管理员）
│   │   ├── page.tsx                  # 待审核列表
│   │   └── [id]/page.tsx             # 审核详情
│   ├── my-instances/page.tsx         # 我的表单
│   ├── instances/[id]/page.tsx       # 填写/详情
│   ├── templates/page.tsx            # 模板
│   ├── translations/page.tsx         # 翻译
│   └── statistics/page.tsx           # 统计
├── components/forms/
│   ├── FormRenderer.tsx              # 渲染引擎
│   ├── FormFieldRenderer.tsx         # 字段渲染
│   └── designer/
│       ├── FormDesigner.tsx          # 设计器主组件
│       ├── FieldPalette.tsx          # 字段面板
│       ├── DesignCanvas.tsx          # 画布
│       ├── PropertyPanel.tsx         # 属性面板
│       ├── types.ts                  # 类型定义
│       └── useDesignerStore.ts       # 状态管理
└── services/api/forms.ts             # API Client
```

---

## 🔮 高级字段规划（P2）

### 规划中的字段类型

| 字段类型 | 说明 | 实现复杂度 |
|----------|------|-----------|
| 动态表格（子表） | 支持多行数据录入 | 高 |
| 计算字段 / 公式字段 | 如 `amount = qty * price` | 中 |
| 条件显示 | 根据其他字段值显示/隐藏 | 中 |
| 关联字段 | 从用户、部门、字典中选择 | 高 |
| 级联选择 | 如：国家 → 省份 → 城市 | 中 |

### 关联字段设计

```typescript
// 关联字段 Schema 扩展
interface RelationFieldSchema {
  type: 'relation';
  relationConfig: {
    source: 'user' | 'department' | 'dictionary' | 'custom';
    sourceKey?: string;  // 字典 key 或自定义数据源
    displayField: string;
    valueField: string;
    filters?: Record<string, any>;
  };
}
```

### 公式字段设计

```typescript
// 公式字段 Schema 扩展
interface FormulaFieldSchema {
  type: 'formula';
  formula: string;  // 例如: "qty * price"
  dependencies: string[];  // 依赖的字段
  resultType: 'number' | 'string' | 'date';
}
```

---

## 📚 相关文档

- [PRD 需求文档](PRD.md)
- [API 接口文档](API.md)
- [开发待办](TODO.md)
- [变更记录](CHANGELOG.md)

---

**创建日期**: 2025-11-20  
**最后更新**: 2025-12-05  
**版本**: v2.2
