esign-contract-template-usage.md 26 KB

e签宝合同模板填充使用说明

功能概述

实现了两种合同模板填充方式:

  1. 本地Word模板填充:使用poi-tl填充本地Word模板,转换为PDF后上传到e签宝
  2. e签宝工作台模板填充:直接使用e签宝工作台中已有的模板,通过API填充字段并生成文件(推荐)

主要功能

方式一:本地Word模板(适用于开发和测试)

  1. Word模板填充:使用poi-tl库填充Word模板中的变量
  2. PDF转换:将填充后的Word文档转换为PDF格式(使用LibreOffice)
  3. 文件上传:将PDF文件上传到e签宝平台,获得fileId
  4. 签署流程创建:基于上传的文件创建e签宝签署流程

方式二:e签宝工作台模板(推荐用于生产环境)

  1. 获取模板列表:从e签宝工作台获取已创建的模板列表
  2. 获取模板详情:查看模板的详细信息和控件配置
  3. 模板字段填充:使用e签宝API直接填充模板中的控件字段
  4. 生成文件:自动生成填充后的PDF文件,获得fileId
  5. 签署流程创建:基于生成的文件创建e签宝签署流程

核心组件

1. EsignContractUtil(工具类)

提供以下静态方法:

  • fillTemplateAndUpload(): 填充模板、转换PDF、上传到e签宝(一步完成)
  • fillWordTemplate(): 仅填充Word模板,返回字节数组
  • fillTemplateAndConvertToPdf(): 填充模板并转换为PDF
  • fillTemplateAndSavePdf(): 填充模板并保存为PDF文件

2. EsignService(服务接口)

  • uploadDocument(): 上传文件到e签宝,返回fileId
  • createSignFlow(): 创建签署流程

3. API接口

3.1 完整流程接口(推荐)

接口地址: POST /api/wechat/esign/flow/createFromTemplate

功能: 填充模板、上传文件、创建签署流程,一步完成

请求体示例:

{
  "contractId": 123,
  "caseId": 456,
  "templatePath": "template1.docx",
  "contractName": "浙江宝路同典当抵押合同",
  "contract": {
    "contractNo": "HT20250101001",
    "name": "张三",
    "idNumber": "330123199001011234",
    "mobile": "13800138000",
    "contractAmount": 100000.0,
    "contractPeriod": 30,
    "amountRate": 1.5,
    "serviceCost": 2.0,
    "loanRate": 3.0,
    "bankName": "中国工商银行",
    "bankAccount": "6222021234567890123",
    "address": "杭州市西湖区文三路123号",
    "collateralNumber": "101",
    "collateralArea": "100平方米",
    "contractType": "抵押合同",
    "contractAttr": "不动产抵押",
    "leastDay": 30,
    "contactAddress1": "杭州市西湖区",
    "contactAddress2": "杭州市西湖区",
    "userMobile": "13900139000",
    "signLocation1": "杭州",
    "signLocation2": "杭州"
  },
  "customerName": "张三",
  "customerMobile": "13800138000",
  "customerIdNumber": "330123199001011234",
  "businessName": "浙江宝路同典当有限公司",
  "businessMobile": "13900139000",
  "businessIdNumber": "",
  "signDeadline": 1735632000000,
  "remark": "测试合同"
}

响应示例:

{
  "code": 200,
  "message": "模板填充并创建签署流程成功",
  "data": "flowId123456789"
}

3.2 仅上传接口

接口地址: POST /api/wechat/esign/template/upload

功能: 仅填充模板并上传到e签宝,不创建签署流程

请求体: 与完整流程接口相同

响应示例:

{
  "code": 200,
  "message": "模板填充并上传成功",
  "data": "fileId123456789"
}

3.3 创建签署流程接口

接口地址: POST /api/wechat/esign/flow/create

功能: 使用已上传的文件ID创建签署流程

请求体示例:

{
  "contractId": 123,
  "caseId": 456,
  "fileId": "fileId123456789",
  "documentName": "浙江宝路同典当抵押合同.pdf",
  "customerName": "张三",
  "customerMobile": "13800138000",
  "customerIdNumber": "330123199001011234",
  "businessName": "浙江宝路同典当有限公司",
  "businessMobile": "13900139000",
  "signDeadline": 1735632000000
}

Word模板制作

支持的变量类型

  1. 文本变量: 使用 {{variableName}} 格式
  2. 图片变量: 使用 {{@pictureVariable}} 格式(用于签名图片)

模板变量列表

根据 ContractInformation 类,支持的变量包括:

变量名 说明 示例值
contractNo 合同编号 HT20250101001
customerName 客户姓名 张三
customerIdNumber 客户身份证号 330123199001011234
customerMobile 客户手机号 13800138000
contractType 业务类型 抵押合同
contractAttr 业务属性 不动产抵押
contractAmount 合同金额 100000.0
contractPeriod 合同期限(天) 30
interestRate 当金利率 1.5%
serviceCost 综合服务费 2.0%
loanRate 借款利率 3.0%
bankName 客户银行 中国工商银行
bankAccount 客户银行账号 6222021234567890123
collateralAddress 押品地址 杭州市西湖区文三路123号
numberAndArea 押品编号和面积 101 100平方米
contactAddress1 客户联系地址 杭州市西湖区
contactAddress2 业务方联系地址 杭州市西湖区
userMobile 业务方手机号 13900139000
signLocation1 客户签署地点 杭州
signLocation2 业务方签署地点 杭州
signature1 客户签名图片 (图片)
signature2 业务方签名图片 (图片)
startDateTime 借款日期 2025-01-01
endDateTime 还款日期 2025-01-31

模板示例

在Word模板中使用以下格式:

合同编号:{{contractNo}}
客户姓名:{{customerName}}
身份证号:{{customerIdNumber}}
合同金额:{{contractAmount}}元
合同期限:{{contractPeriod}}天
当金利率:{{interestRate}}
综合服务费:{{serviceCost}}
借款利率:{{loanRate}}
押品地址:{{collateralAddress}}
押品信息:{{numberAndArea}}
客户签名:{{@signature1}}
业务方签名:{{@signature2}}

配置说明

application-dev.yaml / application-prod.yaml

system:
  esign:
    app-id: your-app-id
    app-secret: your-app-secret
    api-url: https://smlopenapi.esign.cn
    project-id: your-project-id
    enabled: true

upload:
  templatePath: /app/upload/template/  # 模板文件路径

libreoffice:
  remote-url: http://libreoffice-server:8080  # 或本地模式
  office-home: ""  # 留空使用远程模式

使用示例

Java代码示例

@Autowired
private EsignService esignService;

// 方式1: 使用工具类(推荐)
ContractInformation contract = new ContractInformation();
contract.setContractNo("HT20250101001");
contract.setName("张三");
// ... 设置其他字段

String templatePath = "/app/upload/template/template1.docx";
String fileId = EsignContractUtil.fillTemplateAndUpload(
    templatePath, 
    contract, 
    "浙江宝路同典当抵押合同",
    esignService
);

// 方式2: 分步执行
// 1. 填充模板
byte[] docxBytes = EsignContractUtil.fillWordTemplate(templatePath, contract);

// 2. 转换为PDF
byte[] pdfBytes = LibreOfficePdfUtil.convertDocxToPdf(docxBytes);

// 3. 上传到e签宝
String fileId = esignService.uploadDocument(pdfBytes, "合同.pdf", "application/pdf");

// 4. 创建签署流程
EsignCreateFlowDTO flowDTO = new EsignCreateFlowDTO();
flowDTO.setFileId(fileId);
flowDTO.setDocumentName("合同.pdf");
// ... 设置其他字段
String flowId = esignService.createSignFlow(flowDTO);

注意事项

  1. 模板路径:

    • 如果使用相对路径(如 template1.docx),会自动拼接配置的 upload.templatePath
    • 如果使用绝对路径,直接使用该路径
  2. PDF转换:

    • 需要确保LibreOffice服务正常运行
    • 开发环境可以使用本地LibreOffice
    • 生产环境建议使用远程LibreOffice服务
  3. 文件上传:

    • e签宝上传分为三步:获取上传URL -> 上传到OSS -> 通知上传完成
    • 上传成功后返回fileId,后续需要使用fileId创建签署流程
  4. 错误处理:

    • 所有步骤都有异常处理和日志记录
    • 如果某个步骤失败,会返回详细的错误信息
  5. 签名图片:

    • 签名图片路径应该是文件的绝对路径或相对路径
    • 如果图片加载失败,会记录警告日志但不会中断流程

常见问题

Q1: 模板填充后格式错乱?

A: 检查Word模板中的变量格式是否正确,确保使用 {{variableName}} 格式。

Q2: PDF转换失败?

A: 检查LibreOffice服务是否正常运行,查看日志中的错误信息。

Q3: 上传到e签宝失败?

A: 检查e签宝配置是否正确(app-id、app-secret),确保网络连接正常。

Q4: 如何自定义模板变量?

A: 修改 EsignContractUtil.fillWordTemplate() 方法中的变量映射部分。


方式二:使用e签宝工作台模板(推荐)

准备工作

  1. 在e签宝工作台创建模板

    • 登录e签宝工作台(https://smlopen.esign.cn)
    • 进入"合同模板"管理页面
    • 点击"新建模板",上传您的合同文件(支持Word或PDF格式)
    • 在模板编辑器中,添加需要填充的控件:
      • 文本控件:用于填充文本字段(如合同编号、客户姓名等)
      • 为每个控件设置唯一的控件ID(key),这个ID将用于API填充
    • 保存模板,记录模板ID(docTemplateId)
  2. 了解模板控件ID

    • 在模板详情中可以查看所有控件的ID
    • 也可以通过API获取模板详情查看控件配置

API接口

1. 获取模板列表

接口地址: GET /api/wechat/esign/template/list

请求参数:

  • pageNum (可选): 页码,默认1
  • pageSize (可选): 每页大小,默认20

响应示例:

{
  "code": 200,
  "message": "获取模板列表成功",
  "data": {
    "list": [
      {
        "docTemplateId": "template123456",
        "docTemplateName": "浙江宝路同典当抵押合同",
        "docTemplateType": "DOCX",
        "createTime": "2025-01-01 10:00:00"
      }
    ],
    "total": 10,
    "pageNum": 1,
    "pageSize": 20
  }
}

2. 获取模板详情

接口地址: GET /api/wechat/esign/template/{templateId}

功能: 获取模板的详细信息,包括所有控件的配置

响应示例:

{
  "code": 200,
  "message": "获取模板详情成功",
  "data": {
    "docTemplateId": "template123456",
    "docTemplateName": "浙江宝路同典当抵押合同",
    "structComponents": [
      {
        "key": "contractNo",
        "componentType": "TEXT",
        "componentName": "合同编号"
      },
      {
        "key": "customerName",
        "componentType": "TEXT",
        "componentName": "客户姓名"
      }
    ]
  }
}

3. 完整流程:填充模板并创建签署流程(推荐)

接口地址: POST /api/wechat/esign/flow/createFromEsignTemplate

功能: 一步完成模板填充、文件生成、签署流程创建

请求体示例:

{
  "contractId": 123,
  "caseId": 456,
  "templateId": "template123456",
  "contractName": "浙江宝路同典当抵押合同",
  "formFields": {
    "contractNo": "HT20250101001",
    "customerName": "张三",
    "customerIdNumber": "330123199001011234",
    "customerMobile": "13800138000",
    "contractAmount": "100000",
    "contractPeriod": "30",
    "interestRate": "1.5",
    "bankName": "中国工商银行",
    "bankAccount": "6222021234567890123"
  },
  "customerName": "张三",
  "customerMobile": "13800138000",
  "customerIdNumber": "330123199001011234",
  "businessName": "浙江宝路同典当有限公司",
  "businessMobile": "13900139000",
  "signDeadline": 1735632000000
}

请求体说明:

  • templateId: e签宝工作台中的模板ID(必填)
  • formFields: 字段映射(key为模板控件ID,value为填充值)
    • 如果formFields为空,可以传入contract对象,系统会自动映射常见字段
  • customerName, customerMobile等: 用于创建签署流程的签署人信息

响应示例:

{
  "code": 200,
  "message": "使用e签宝模板创建签署流程成功",
  "data": "flowId123456789"
}

4. 仅填充模板生成文件

接口地址: POST /api/wechat/esign/template/fill

功能: 仅填充模板并生成文件,不创建签署流程

请求体: 与完整流程接口相同(但不创建流程)

响应示例:

{
  "code": 200,
  "message": "模板填充并生成文件成功",
  "data": "fileId123456789"
}

自动字段映射

如果您不想手动构建formFields,可以传入contract对象,系统会自动将常见字段映射到e签宝模板控件。

支持的自动映射字段

ContractInformation字段 映射到的模板控件ID
contractNo contractNo, 合同编号
name customerName, 客户姓名, name
idNumber customerIdNumber, 客户身份证号, idNumber
mobile customerMobile, 客户手机号, mobile
contractAmount contractAmount, 合同金额, 金额
contractPeriod contractPeriod, 合同期限, 期限
amountRate interestRate, 当金利率
serviceCost serviceCost, 综合服务费
loanRate loanRate, 借款利率
bankName bankName, 银行名称
bankAccount bankAccount, 银行账号
address collateralAddress, 押品地址, address
contractType contractType, 业务类型
contractAttr contractAttr, 业务属性
startDateTime startDateTime, 借款日期
endDateTime endDateTime, 还款日期

使用示例:

{
  "templateId": "template123456",
  "contractName": "浙江宝路同典当抵押合同",
  "contract": {
    "contractNo": "HT20250101001",
    "name": "张三",
    "idNumber": "330123199001011234",
    "mobile": "13800138000",
    "contractAmount": 100000.0,
    "contractPeriod": 30
  },
  "customerName": "张三",
  "customerMobile": "13800138000",
  "businessName": "浙江宝路同典当有限公司",
  "businessMobile": "13900139000"
}

Java代码示例

@Autowired
private EsignService esignService;

// 方式1: 使用手动字段映射
Map<String, String> formFields = new HashMap<>();
formFields.put("contractNo", "HT20250101001");
formFields.put("customerName", "张三");
formFields.put("contractAmount", "100000");

String fileId = esignService.fillTemplateAndGenerateFile(
    "template123456",
    "合同.pdf",
    formFields
);

// 方式2: 使用自动字段映射
ContractInformation contract = new ContractInformation();
contract.setContractNo("HT20250101001");
contract.setName("张三");
// ... 设置其他字段

Map<String, String> formFields = EsignContractUtil.convertContractToTemplateFields(contract);
String fileId = esignService.fillTemplateAndGenerateFile(
    "template123456",
    "合同.pdf",
    formFields
);

e签宝模板控件命名建议

为了使用自动字段映射功能,建议在e签宝工作台中创建模板时,使用以下控件ID:

合同基本信息

  • contractNo合同编号
  • contractAmount合同金额
  • contractPeriod合同期限

客户信息

  • customerName客户姓名name
  • customerIdNumber客户身份证号idNumber
  • customerMobile客户手机号mobile

利率信息

  • interestRate当金利率
  • serviceCost综合服务费
  • loanRate借款利率

银行信息

  • bankName银行名称
  • bankAccount银行账号

押品信息

  • collateralAddress押品地址address
  • numberAndArea押品信息

日期信息

  • startDateTime借款日期
  • endDateTime还款日期

两种方式对比

特性 本地Word模板 e签宝工作台模板
模板管理 需要自行管理模板文件 e签宝工作台统一管理
模板编辑 需要使用Word编辑器 e签宝可视化编辑器
字段填充 使用变量名 {{variable}} 使用控件ID(key)
PDF转换 需要LibreOffice服务 e签宝自动转换
适用场景 开发和测试 生产环境(推荐)
模板更新 需要重新上传文件 在e签宝工作台直接更新

使用e签宝模板的注意事项

  1. 模板ID获取

    • 模板ID可以从e签宝工作台查看,也可以通过API获取模板列表
    • 模板ID是固定的,不会因为模板更新而改变
  2. 控件ID(key)

    • 控件ID是在e签宝工作台创建模板时设置的
    • 控件ID必须唯一,且一旦设置不建议修改
    • 如果修改了控件ID,需要同步更新API调用中的字段映射
  3. 字段映射

    • formFields中的key必须与模板中的控件ID完全一致
    • 如果控件ID使用中文,key也必须使用中文
    • 建议使用英文控件ID,便于维护
  4. 自动映射

    • 自动映射功能基于常见的字段名规则
    • 如果模板控件ID不符合命名规则,需要手动构建formFields
    • 可以通过获取模板详情API查看实际的控件ID
  5. 模板更新

    • 在e签宝工作台更新模板后,不需要修改代码
    • 只要控件ID不变,API调用就可以正常工作

完整业务流程说明

方式一:使用e签宝工作台模板(推荐)

准备工作(一次性)

  1. 在e签宝工作台创建模板
    • 登录e签宝工作台,上传合同文件
    • 在模板编辑器中添加文本控件
    • 为每个控件设置控件ID(如 contractNocustomerName
    • 记录模板ID(docTemplateId),例如:template123456

业务流程(每次签署合同)

步骤1:使用模板ID填充字段并创建签署流程

POST /api/wechat/esign/flow/createFromEsignTemplate

这个接口会一步完成以下操作:

  • ✅ 根据 templateId 填充模板字段(使用 formFieldscontract
  • ✅ 生成填充后的PDF文件
  • ✅ 创建签署流程
  • ✅ 返回 flowId

请求示例

{
  "contractId": 123,
  "caseId": 456,
  "templateId": "template123456",
  "contractName": "浙江宝路同典当抵押合同",
  "formFields": {
    "contractNo": "HT20250101001",
    "customerName": "张三",
    "customerMobile": "13800138000"
  },
  "customerName": "张三",
  "customerMobile": "13800138000",
  "businessName": "浙江宝路同典当有限公司",
  "businessMobile": "13900139000"
}

响应示例

{
  "code": 200,
  "message": "使用e签宝模板创建签署流程成功",
  "data": "flowId123456789"
}

步骤2:获取签署链接

获取客户签署链接:

GET /api/wechat/esign/flow/{flowId}/customer/sign-url?mobile=13800138000&name=张三

获取业务方签署链接:

GET /api/wechat/esign/flow/{flowId}/business/sign-url?mobile=13900139000&name=公司名称

步骤3:启动签署流程

POST /api/wechat/esign/flow/{flowId}/start

启动后,签署人才能通过签署链接进行签署。

步骤4:签署和查询

  • 客户和业务方通过签署链接完成签署
  • 查询签署状态:GET /api/wechat/esign/flow/{flowId}/status
  • 下载已签署文档:GET /api/wechat/esign/flow/{flowId}/download

方式二:分步执行(如果需要更精细的控制)

如果不想一步完成,可以分步执行:

步骤1:填充模板生成文件

POST /api/wechat/esign/template/fill

返回 fileId

步骤2:使用fileId创建签署流程

POST /api/wechat/esign/flow/create

传入 fileId,返回 flowId

步骤3-5:同方式一的步骤2-4


完整流程图

┌─────────────────────────────────────────────────────────────┐
│  准备工作(一次性)                                          │
│  ┌───────────────────────────────────────────────────────┐  │
│  │ 1. 在e签宝工作台创建模板                               │  │
│  │ 2. 设置控件ID(如:contractNo, customerName)         │  │
│  │ 3. 记录模板ID(templateId)                           │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│  业务流程(每次签署)                                        │
│  ┌───────────────────────────────────────────────────────┐  │
│  │ 步骤1: 填充模板并创建签署流程                         │  │
│  │ POST /api/wechat/esign/flow/createFromEsignTemplate  │  │
│  │ 输入: templateId + formFields/contract               │  │
│  │ 输出: flowId                                         │  │
│  └───────────────────────────────────────────────────────┘  │
│                            ↓                                 │
│  ┌───────────────────────────────────────────────────────┐  │
│  │ 步骤2: 获取签署链接                                   │  │
│  │ GET /api/wechat/esign/flow/{flowId}/customer/sign-url│  │
│  │ GET /api/wechat/esign/flow/{flowId}/business/sign-url│  │
│  └───────────────────────────────────────────────────────┘  │
│                            ↓                                 │
│  ┌───────────────────────────────────────────────────────┐  │
│  │ 步骤3: 启动签署流程                                   │  │
│  │ POST /api/wechat/esign/flow/{flowId}/start           │  │
│  └───────────────────────────────────────────────────────┘  │
│                            ↓                                 │
│  ┌───────────────────────────────────────────────────────┐  │
│  │ 步骤4: 客户和业务方签署                               │  │
│  │ 通过签署链接完成签署                                  │  │
│  └───────────────────────────────────────────────────────┘  │
│                            ↓                                 │
│  ┌───────────────────────────────────────────────────────┐  │
│  │ 步骤5: 查询状态和下载                                 │  │
│  │ GET /api/wechat/esign/flow/{flowId}/status           │  │
│  │ GET /api/wechat/esign/flow/{flowId}/download         │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

关键概念说明

  1. templateId(模板ID)

    • 这是e签宝工作台中模板的唯一标识
    • 在创建模板时获得,不会改变
    • 不需要从合同列表获取,模板ID是固定的
  2. flowId(流程ID)

    • 每次创建签署流程时生成的唯一标识
    • 用于后续的签署链接获取、状态查询等操作
    • 一个模板ID可以创建多个flowId(对应多个合同)
  3. fileId(文件ID)

    • 填充模板后生成的PDF文件在e签宝中的标识
    • 如果使用 createFromEsignTemplate 接口,fileId会自动处理,无需关心

常见误解

错误理解

  1. 先填充模板字段
  2. 从合同列表中获取填充完的templateId
  3. 开始签署合同

正确理解

  1. templateId是固定的(在e签宝工作台创建模板时获得)
  2. 调用 createFromEsignTemplate 接口时,传入templateId和要填充的数据
  3. 接口会:填充模板 → 生成文件 → 创建签署流程,返回flowId
  4. 使用flowId获取签署链接、启动流程、完成签署

后续流程

模板填充和签署流程创建成功后,可以:

  1. 获取签署链接:调用 /api/wechat/esign/flow/{flowId}/customer/sign-url
  2. 启动签署流程:调用 /api/wechat/esign/flow/{flowId}/start
  3. 查询流程状态:调用 /api/wechat/esign/flow/{flowId}/status
  4. 下载已签署文档:调用 /api/wechat/esign/flow/{flowId}/download

详细接口说明请参考 EsignController.java