372 lines
8.2 KiB
Markdown
372 lines
8.2 KiB
Markdown
# 合同起草功能实施方案
|
||
|
||
## 功能概述
|
||
|
||
基于合同模板创建新合同,通过占位符替换和在线编辑实现合同起草。
|
||
|
||
---
|
||
|
||
## 方案对比
|
||
|
||
### 方案A:docxtemplater + 服务端生成
|
||
|
||
**技术栈**:
|
||
- `docxtemplater` - DOCX 模板引擎
|
||
- 服务端处理(Node.js)
|
||
- 生成新文档后上传到 MinIO
|
||
|
||
**工作流程**:
|
||
1. 模板中预先定义占位符:`{甲方名称}`、`{合同金额}`
|
||
2. 用户填写表单
|
||
3. 服务端使用 docxtemplater 替换占位符
|
||
4. 生成新 DOCX 文件
|
||
5. 上传到 MinIO
|
||
6. 前端加载新文件用 Collabora 展示和编辑
|
||
|
||
**优点**:
|
||
- ✅ 模板语法强大(支持循环、条件、表格等)
|
||
- ✅ 替换精确可控
|
||
- ✅ 可以在服务端验证数据
|
||
- ✅ 支持复杂的模板逻辑
|
||
|
||
**缺点**:
|
||
- ❌ 需要安装新依赖
|
||
- ❌ 需要开发服务端 API
|
||
- ❌ 模板需要预先制作占位符
|
||
- ❌ 生成→上传→加载流程较长
|
||
- ❌ 无法实时预览替换效果
|
||
|
||
---
|
||
|
||
### 方案B:CollaboraViewer 实时替换(推荐)
|
||
|
||
**技术栈**:
|
||
- 现有的 `CollaboraViewer` 组件
|
||
- `replaceTextInPage` / `unoReplaceAll` 功能
|
||
- Collabora Online 编辑模式
|
||
|
||
**工作流程**:
|
||
1. 模板中约定占位符格式:`{{甲方名称}}`、`{{合同金额}}`
|
||
2. 用户点击"起草合同",复制模板文件
|
||
3. 在 Collabora 中以编辑模式打开复制的文件
|
||
4. 用户填写右侧表单
|
||
5. 实时调用 `replaceTextInPage` 或 `unoReplaceAll` 替换占位符
|
||
6. 用户可以继续手动编辑文档
|
||
7. 保存生成的新合同
|
||
|
||
**优点**:
|
||
- ✅ 无需新依赖,复用现有代码
|
||
- ✅ 实时预览替换效果
|
||
- ✅ 用户可以继续手动编辑
|
||
- ✅ 开发速度快
|
||
- ✅ 所见即所得
|
||
|
||
**缺点**:
|
||
- ❌ 依赖 Collabora Online 服务
|
||
- ❌ 替换逻辑相对简单(基于查找替换)
|
||
- ❌ 需要处理异步替换
|
||
|
||
---
|
||
|
||
### 方案C:混合方案
|
||
|
||
**技术栈**:
|
||
- 简单模板:使用 CollaboraViewer 实时替换
|
||
- 复杂模板:使用 docxtemplater 服务端生成
|
||
|
||
**工作流程**:
|
||
1. 模板元数据标记是否为"复杂模板"
|
||
2. 简单模板走方案B流程
|
||
3. 复杂模板走方案A流程
|
||
|
||
**优点**:
|
||
- ✅ 灵活性最高
|
||
- ✅ 简单场景快速响应
|
||
- ✅ 复杂场景功能完整
|
||
|
||
**缺点**:
|
||
- ❌ 开发成本高
|
||
- ❌ 维护两套逻辑
|
||
|
||
---
|
||
|
||
## 推荐方案:方案B(CollaboraViewer 实时替换)
|
||
|
||
### 理由
|
||
|
||
1. **快速落地**:复用现有代码,开发周期短
|
||
2. **用户体验好**:所见即所得,实时预览
|
||
3. **灵活性高**:替换后用户可以继续编辑
|
||
4. **维护成本低**:不引入新依赖
|
||
|
||
### 技术细节
|
||
|
||
#### 占位符约定
|
||
|
||
使用双花括号格式,便于识别和替换:
|
||
|
||
```
|
||
{{甲方名称}}
|
||
{{甲方地址}}
|
||
{{甲方法定代表人}}
|
||
{{甲方联系电话}}
|
||
{{乙方名称}}
|
||
{{乙方地址}}
|
||
{{乙方法定代表人}}
|
||
{{乙方联系电话}}
|
||
{{合同金额}}
|
||
{{签订日期}}
|
||
{{合同编号}}
|
||
```
|
||
|
||
#### 替换实现
|
||
|
||
使用 CollaboraViewer 的 `unoReplaceAll` 方法:
|
||
|
||
```typescript
|
||
// 批量替换所有占位符
|
||
async function replacePlaceholders(
|
||
collaboraRef: CollaboraViewerHandle,
|
||
values: Record<string, string>
|
||
) {
|
||
for (const [key, value] of Object.entries(values)) {
|
||
const placeholder = `{{${key}}}`;
|
||
await collaboraRef.unoCommands.replaceAll(placeholder, value);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 文件复制策略
|
||
|
||
**选项1:服务端复制**(推荐)
|
||
- API: `POST /api/contracts/draft`
|
||
- 请求参数: `{ templateId: string }`
|
||
- 返回: `{ newFileId: string, newFilePath: string }`
|
||
- 服务端将模板文件复制为新文件
|
||
|
||
**选项2:客户端复制**
|
||
- 下载模板文件 Blob
|
||
- 重命名后重新上传
|
||
- 成本高,不推荐
|
||
|
||
---
|
||
|
||
## 数据库设计
|
||
|
||
### 新增表:`drafted_contracts` (起草的合同)
|
||
|
||
```sql
|
||
CREATE TABLE drafted_contracts (
|
||
id SERIAL PRIMARY KEY,
|
||
template_id INTEGER REFERENCES contract_templates(id),
|
||
file_path TEXT NOT NULL, -- 起草后的文件路径
|
||
title TEXT NOT NULL, -- 合同标题
|
||
placeholder_values JSONB, -- 占位符填充值
|
||
status TEXT DEFAULT 'draft', -- draft | completed | archived
|
||
created_by INTEGER REFERENCES auth.users(id),
|
||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
## API 设计
|
||
|
||
### 1. 创建起草合同
|
||
|
||
```
|
||
POST /api/contracts/draft
|
||
```
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"templateId": 123,
|
||
"title": "智慧法务平台采购合同-草稿"
|
||
}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"id": 456,
|
||
"filePath": "drafts/contract_456_20250104.docx",
|
||
"title": "智慧法务平台采购合同-草稿",
|
||
"templateId": 123
|
||
}
|
||
```
|
||
|
||
### 2. 保存占位符值
|
||
|
||
```
|
||
PUT /api/contracts/draft/:id/placeholders
|
||
```
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"placeholders": {
|
||
"甲方名称": "广东省烟草专卖局",
|
||
"合同金额": "500000"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 完成起草
|
||
|
||
```
|
||
POST /api/contracts/draft/:id/complete
|
||
```
|
||
|
||
将草稿标记为已完成,可选地移动到正式合同目录。
|
||
|
||
---
|
||
|
||
## UI/UX 设计
|
||
|
||
### 页面结构
|
||
|
||
```
|
||
/contract-template/draft/:templateId
|
||
├── 左侧(60%):FilePreview(Collabora 编辑模式)
|
||
└── 右侧(40%):占位符表单
|
||
├── 基本信息
|
||
│ ├── 合同标题
|
||
│ └── 合同编号
|
||
├── 甲方信息
|
||
│ ├── 甲方名称
|
||
│ ├── 甲方地址
|
||
│ ├── 法定代表人
|
||
│ └── 联系电话
|
||
├── 乙方信息
|
||
│ ├── 乙方名称
|
||
│ ├── 乙方地址
|
||
│ ├── 法定代表人
|
||
│ └── 联系电话
|
||
├── 合同条款
|
||
│ ├── 合同金额
|
||
│ ├── 签订日期
|
||
│ └── 其他自定义字段
|
||
└── 操作按钮
|
||
├── 一键替换
|
||
├── 保存草稿
|
||
└── 完成起草
|
||
```
|
||
|
||
### 交互流程
|
||
|
||
1. 用户在模板详情页点击"起草合同"
|
||
2. 系统复制模板文件,创建草稿记录
|
||
3. 跳转到起草页面
|
||
4. 左侧加载文档(Collabora 编辑模式)
|
||
5. 右侧显示占位符表单(从模板中提取或预定义)
|
||
6. 用户填写表单
|
||
7. 点击"一键替换"批量替换所有占位符
|
||
8. 用户可以继续手动编辑文档
|
||
9. 点击"保存草稿"保存当前状态
|
||
10. 点击"完成起草"将合同标记为完成
|
||
|
||
---
|
||
|
||
## 占位符提取方案
|
||
|
||
### 方案1:预定义字段(推荐)
|
||
|
||
在模板表中增加字段:
|
||
|
||
```sql
|
||
ALTER TABLE contract_templates
|
||
ADD COLUMN placeholder_schema JSONB;
|
||
```
|
||
|
||
示例数据:
|
||
```json
|
||
{
|
||
"fields": [
|
||
{
|
||
"key": "甲方名称",
|
||
"label": "甲方名称",
|
||
"type": "text",
|
||
"required": true,
|
||
"group": "甲方信息"
|
||
},
|
||
{
|
||
"key": "合同金额",
|
||
"label": "合同金额(元)",
|
||
"type": "number",
|
||
"required": true,
|
||
"group": "合同条款"
|
||
},
|
||
{
|
||
"key": "签订日期",
|
||
"label": "签订日期",
|
||
"type": "date",
|
||
"required": true,
|
||
"group": "合同条款"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 方案2:动态提取
|
||
|
||
从 DOCX 文件中提取所有 `{{xxx}}` 占位符:
|
||
|
||
- 需要解析 DOCX 文件
|
||
- 服务端实现
|
||
- 成本较高
|
||
|
||
---
|
||
|
||
## 技术风险与应对
|
||
|
||
### 风险1:Collabora 替换性能
|
||
|
||
**风险**:多个占位符替换可能耗时较长
|
||
|
||
**应对**:
|
||
- 使用 `unoReplaceAll` 而不是逐个替换
|
||
- 显示替换进度提示
|
||
- 考虑批量替换 API
|
||
|
||
### 风险2:并发编辑冲突
|
||
|
||
**风险**:用户同时编辑和替换可能冲突
|
||
|
||
**应对**:
|
||
- 替换前提示用户
|
||
- 替换时禁用编辑
|
||
- 使用防抖避免频繁替换
|
||
|
||
### 风险3:占位符未完全替换
|
||
|
||
**风险**:用户忘记填写某些字段
|
||
|
||
**应对**:
|
||
- 完成前检查是否还有 `{{}}` 占位符
|
||
- 高亮显示未替换的占位符
|
||
- 必填字段验证
|
||
|
||
---
|
||
|
||
## 扩展功能(可选)
|
||
|
||
1. **模板变量管理**:管理员可以配置模板的占位符字段
|
||
2. **历史记录**:记录每次替换的历史
|
||
3. **模板版本**:支持模板版本管理
|
||
4. **审批流程**:起草完成后进入审批流程
|
||
5. **电子签名**:集成电子签名功能
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
推荐使用 **方案B(CollaboraViewer 实时替换)**,理由:
|
||
- 开发成本低,复用现有代码
|
||
- 用户体验好,所见即所得
|
||
- 灵活性高,支持后续手动编辑
|
||
- 维护成本低,不引入新依赖
|
||
|
||
如果未来需要支持复杂模板逻辑(循环、条件等),可以再引入 docxtemplater 作为补充。
|