Files
leaudit-platform-frontend/docs/contract-drafting-solution.md
2025-12-05 00:09:32 +08:00

372 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 合同起草功能实施方案
## 功能概述
基于合同模板创建新合同,通过占位符替换和在线编辑实现合同起草。
---
## 方案对比
### 方案Adocxtemplater + 服务端生成
**技术栈**
- `docxtemplater` - DOCX 模板引擎
- 服务端处理(Node.js
- 生成新文档后上传到 MinIO
**工作流程**
1. 模板中预先定义占位符:`{甲方名称}``{合同金额}`
2. 用户填写表单
3. 服务端使用 docxtemplater 替换占位符
4. 生成新 DOCX 文件
5. 上传到 MinIO
6. 前端加载新文件用 Collabora 展示和编辑
**优点**
- ✅ 模板语法强大(支持循环、条件、表格等)
- ✅ 替换精确可控
- ✅ 可以在服务端验证数据
- ✅ 支持复杂的模板逻辑
**缺点**
- ❌ 需要安装新依赖
- ❌ 需要开发服务端 API
- ❌ 模板需要预先制作占位符
- ❌ 生成→上传→加载流程较长
- ❌ 无法实时预览替换效果
---
### 方案BCollaboraViewer 实时替换(推荐)
**技术栈**
- 现有的 `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流程
**优点**
- ✅ 灵活性最高
- ✅ 简单场景快速响应
- ✅ 复杂场景功能完整
**缺点**
- ❌ 开发成本高
- ❌ 维护两套逻辑
---
## 推荐方案:方案BCollaboraViewer 实时替换)
### 理由
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%):FilePreviewCollabora 编辑模式)
└── 右侧(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 文件
- 服务端实现
- 成本较高
---
## 技术风险与应对
### 风险1Collabora 替换性能
**风险**:多个占位符替换可能耗时较长
**应对**
- 使用 `unoReplaceAll` 而不是逐个替换
- 显示替换进度提示
- 考虑批量替换 API
### 风险2:并发编辑冲突
**风险**:用户同时编辑和替换可能冲突
**应对**
- 替换前提示用户
- 替换时禁用编辑
- 使用防抖避免频繁替换
### 风险3:占位符未完全替换
**风险**:用户忘记填写某些字段
**应对**
- 完成前检查是否还有 `{{}}` 占位符
- 高亮显示未替换的占位符
- 必填字段验证
---
## 扩展功能(可选)
1. **模板变量管理**:管理员可以配置模板的占位符字段
2. **历史记录**:记录每次替换的历史
3. **模板版本**:支持模板版本管理
4. **审批流程**:起草完成后进入审批流程
5. **电子签名**:集成电子签名功能
---
## 总结
推荐使用 **方案BCollaboraViewer 实时替换)**,理由:
- 开发成本低,复用现有代码
- 用户体验好,所见即所得
- 灵活性高,支持后续手动编辑
- 维护成本低,不引入新依赖
如果未来需要支持复杂模板逻辑(循环、条件等),可以再引入 docxtemplater 作为补充。