☰
影刀RPA深度实战:合同自动化管理与到期续签自动提醒系统
2026/10/15 4:18:14 网站建设 项目流程

影刀RPA深度实战:合同自动化管理与到期续签自动提醒系统


作者:林焱


一、痛点切入

我在之前做内控咨询的时候,遇到过这样一个真实案例:一家制造企业跟核心供应商的年度框架合同过期了3个月,双方都还在按旧合同执行,直到供应商涨价才意识到合同早就到期了。因为没有续签,导致价格谈判失去主动权,多花了80多万。

这还不是最夸张的。更常见的是合同审批流程卡在某个人那里,等到业务急着要用的时候才发现合同还没签——这种情况每个做过合同管理的人都深有体会。

合同管理的核心痛点就四个:到期没人管、审批没人催、归档到处丢、续签老忘记。

我用影刀RPA搭建了一套合同全生命周期管理系统,覆盖了从到期预警→模板填充→审批催办→签署归档→续签提醒的完整流程。这篇文章把这套系统的搭建方法完整分享出来。


二、核心方法

场景描述

企业合同管理需要覆盖以下环节:

  1. 到期提醒:合同到期前自动提醒相关人员
  2. 续签/新建:根据模板自动生成新合同
  3. 审批推进:监控审批流程,超时自动催办
  4. 签署归档:签署完成后自动归档到指定位置
  5. 续签提醒:周期性合同的续签循环提醒

整体架构

合同数据源(Excel/ERP) → 到期监测 → 提醒通知 → 合同生成 → 审批推进 → 签署归档 ↑ ↓ └──────────────── 状态更新 ←──────────────────────────────────┘

分步操作

第1步:建立合同台账

首先要有一个规范的合同电子台账。如果公司有合同管理系统(如泛微、致远),直接从系统导出;如果没有,用Excel维护也行。

合同台账建议包含以下字段:

字段说明示例
合同编号唯一标识HT-2024-0001
合同类型采购合同/销售合同/租赁合同/服务合同采购合同
合同名称合同标题XX设备采购合同
对方单位合同对方全称XX科技有限公司
合同金额金额500000
签订日期合同签订日2024-01-15
生效日期合同生效日2024-01-15
到期日期合同到期日2025-01-14
续签方式自动续签/提前续签/到期终止提前续签
负责人谁负责续签张三
审批状态待审批/审批中/已完成/已归档已完成
合同文件路径电子版合同存储位置/contract/2024/HT-2024-0001.pdf
备注其他重要信息提前30天通知是否续签
第2步:自动到期监测

这是整个系统的核心——到期前自动提醒。

子流程名称:合同到期监测 触发方式:每天定时运行(建议每天早上8:00运行) 步骤: 1. 【读取Excel】读取合同台账 文件路径:{合同台账路径} 2. 【日期计算】计算每个合同距离到期还有多少天 影刀公式:DateDiff("d", 当前日期, 到期日期) 3. 【筛选】筛选出需要提醒的合同 筛选条件: ── 距离到期 ≤ 7天:紧急提醒(尚未续签的) ── 距离到期 ≤ 30天:常规提醒 ── 距离到期 ≤ 90天:提前提醒(需要提前续签的合同类型) 4. 【条件判断】是否有需要提醒的合同 ── 有:继续第5步 ── 没有:记录日志"今日无到期合同",流程结束 5. 【发送通知】通过飞书/钉钉/邮件发送提醒 (通知模板见下文) 6. 【写入日志】记录本次监测结果
第3步:合同模板自动填充

合同模板通常是一个Word文档,里面有很多占位符(如{{甲方}}、{{合同金额}}等),需要根据业务数据自动填充。

子流程名称:合同模板自动填充 参数: 合同模板路径 填充数据(字典) 输出路径 步骤: 1. 【打开Word】使用影刀「Word-打开文档」指令打开合同模板 注意:先复制一份模板副本,不要修改原始模板 2. 【循环】遍历填充数据字典: 对每个键值对: ── 【Word-查找替换】 查找内容:{{字段名}}(如{{甲方}}) 替换为:对应的值 ⚠️ 注意:影刀的Word替换不支持替换占位符中的特殊字符时, 可以考虑先导出为文本替换,再导入Word 或者使用Python方案(更灵活): ```python from docx import Document def fill_contract_template(template_path, data, output_path): """填充合同模板""" doc = Document(template_path) # 替换段落中的占位符 for paragraph in doc.paragraphs: for key, value in data.items(): placeholder = f"{{{{{key}}}}}" # 生成 {{key}} 格式的占位符 if placeholder in paragraph.text: # 获取原始格式 inline = paragraph.runs for run in inline: if placeholder in run.text: run.text = run.text.replace(placeholder, str(value)) # 替换表格中的占位符 for table in doc.tables: for row in table.rows: for cell in row.cells: for paragraph in cell.paragraphs: for key, value in data.items(): placeholder = f"{{{{{key}}}}}" if placeholder in paragraph.text: for run in paragraph.runs: if placeholder in run.text: run.text = run.text.replace(placeholder, str(value)) doc.save(output_path) return output_path
  1. 【保存文档】保存填充后的合同
  2. 【转换PDF】可选:使用影刀「Word-导出PDF」将Word转为PDF
#### 第4步:审批流程自动推进 如果公司使用OA系统(飞书审批、钉钉审批、企业微信审批),可用影刀对接:

子流程名称:合同审批催办

触发方式:每天下午14:00运行

步骤:

  1. 【读取Excel】读取合同台账中"审批状态"为"审批中"的记录

  2. 【循环】遍历每条记录的审批流程:

    ── 【调用OA API】查询当前审批节点和审批人
    飞书审批API:GET /approval/v4/instances/{instance_id}
    返回:当前节点审批人列表

    ── 【条件判断】判断审批是否超时
    如果当前审批人超过24小时未处理:
    ── 【发送飞书消息】给审批人发送催办消息
    ── 【抄送】抄送其直属上级(超过48小时)

    ── 【更新台账】更新审批状态

#### 第5步:签署后自动归档 合同签署后(电子签或实体签),自动归档到指定位置:

子流程名称:合同自动归档

步骤:

  1. 【监测文件】监测合同签署完成目录
    目录:{合同签署完成目录}
    文件类型:*.pdf

  2. 【OCR解析】解析合同封面/首页的关键信息
    ── 合同编号
    ── 合同名称
    ── 签署日期

  3. 【重命名】按规则重命名文件
    格式:{合同编号}{合同名称}{签署日期}.pdf

  4. 【移动文件】移动到归档目录
    目录结构:
    {归档根目录}/
    ├── 2024/
    │ ├── 采购合同/
    │ ├── 销售合同/
    │ └── 服务合同/

  5. 【更新台账】更新合同台账中的"合同文件路径"和"审批状态"

#### 第6步:续签自动提醒 对于设置了"自动续签"或"需要续签"的合同:

子流程名称:续签提醒与发起

触发方式:合同到期前X天(可配置,如90天/60天/30天)

步骤:

  1. 【筛选】筛选出即将到期且需要续签的合同
    条件:到期日期 - 当前日期 ≤ 续签提前天数
    条件:续签状态 = “待续签” 且 合同状态 ≠ “已终止”

  2. 【发送提醒】给合同负责人发送续签提醒
    通知模板:

    📋 合同续签提醒

    合同编号:{合同编号}
    合同名称:{合同名称}
    合同对方:{对方单位}
    合同金额:{合同金额}元
    到期日期:{到期日期}
    距离到期:{剩余天数}天

    操作建议:请尽快确认是否续签

    ── 如果确认续签:点击链接发起续签审批
    ── 如果不续签:点击链接确认终止

  3. 【自动续签】对于设置了"自动续签"的合同
    ── 生成新的续签合同(基于原合同模板,更新日期)
    ── 自动发起审批流程
    ── 更新台账

--- ## 三、代码实现 ### 3.1 合同到期提醒完整代码 ```python """ 合同到期自动监测与提醒引擎 每天定时运行,扫描所有合同,对即将到期的合同发送提醒 """ import pandas as pd from datetime import datetime, timedelta class ContractExpiryMonitor: """合同到期监测器""" def __init__(self, contract_file_path, alert_days_config=None): """ 初始化监测器 contract_file_path: 合同台账Excel文件路径 alert_days_config: 提醒时间配置 """ self.contract_file = contract_file_path self.alert_days = alert_days_config or { "紧急": 7, # 7天内到期为紧急 "常规": 30, # 30天内到期为常规提醒 "提前": 90 # 90天内为提前提醒(适用于需提前续签的合同) } def scan_contracts(self): """扫描合同台账,找出需要提醒的合同""" # 读取合同台账 df = pd.read_excel(self.contract_file) today = datetime.now().date() alerts = { "紧急": [], "常规": [], "提前": [] } expired = [] # 已过期的合同 for _, row in df.iterrows(): contract_id = row["合同编号"] contract_name = row["合同名称"] expiry_date = pd.to_datetime(row["到期日期"]).date() contract_type = row.get("续签方式", "提前续签") responsible = row.get("负责人", "") status = row.get("合同状态", "") # 跳过已终止/已完成的合同 if status in ["已终止", "已续签", "已归档-历史"]: continue days_remaining = (expiry_date - today).days # 分类 if days_remaining < 0: # 已过期 expired.append({ "合同编号": contract_id, "合同名称": contract_name, "到期日期": expiry_date.strftime("%Y-%m-%d"), "已过期天数": abs(days_remaining), "负责人": responsible, "警示级别": "严重", "建议": "合同已过期,请立即处理!如需续签请走紧急审批通道" }) elif days_remaining <= self.alert_days["紧急"]: alerts["紧急"].append({ "合同编号": contract_id, "合同名称": contract_name, "到期日期": expiry_date.strftime("%Y-%m-%d"), "剩余天数": days_remaining, "负责人": responsible, "警示级别": "紧急", "建议": "合同即将到期,请立即联系对方确认续签事宜" }) elif days_remaining <= self.alert_days["常规"]: alerts["常规"].append({ "合同编号": contract_id, "合同名称": contract_name, "到期日期": expiry_date.strftime("%Y-%m-%d"), "剩余天数": days_remaining, "负责人": responsible, "警示级别": "关注", "建议": "请在本周内启动续签评估流程" }) elif contract_type == "提前续签" and days_remaining <= self.alert_days["提前"]: alerts["提前"].append({ "合同编号": contract_id, "合同名称": contract_name, "到期日期": expiry_date.strftime("%Y-%m-%d"), "剩余天数": days_remaining, "负责人": responsible, "警示级别": "提醒", "建议": f"距离合同到期还有{days_remaining}天,建议提前启动续签评估" }) return alerts, expired def format_alert_message(self, alerts, expired): """格式化提醒消息""" message_lines = [] message_lines.append(f"📋 合同到期监测报告 - {datetime.now().strftime('%Y-%m-%d')}") message_lines.append("=" * 40) if expired: message_lines.append(f"\n🔴 已过期合同({len(expired)}份):") for item in expired: message_lines.append(f" [{item['合同编号']}] {item['合同名称']}") message_lines.append(f" 到期日:{item['到期日期']}(已过期{item['已过期天数']}天)") message_lines.append(f" 负责人:{item['负责人']} | {item['建议']}") if alerts["紧急"]: message_lines.append(f"\n🟠 紧急提醒({len(alerts['紧急'])}份,7天内到期):") for item in alerts["紧急"]: message_lines.append(f" [{item['合同编号']}] {item['合同名称']}") message_lines.append(f" 到期日:{item['到期日期']}(剩余{item['剩余天数']}天)") if alerts["常规"]: message_lines.append(f"\n🟡 常规提醒({len(alerts['常规'])}份,30天内到期):") for item in alerts["常规"]: message_lines.append(f" [{item['合同编号']}] {item['合同名称']} - {item['剩余天数']}天后到期") if alerts["提前"]: message_lines.append(f"\n🔵 提前提醒({len(alerts['提前'])}份,90天内到期):") for item in alerts["提前"]: message_lines.append(f" [{item['合同编号']}] {item['合同名称']} - {item['剩余天数']}天后到期") if not expired and not any(alerts.values()): message_lines.append("\n✅ 暂无即将到期或已过期的合同。") message_lines.append(f"\n---\n此消息由RPA合同管理系统自动生成") return "\n".join(message_lines)

3.2 续签合同自动生成

""" 续签合同自动生成 基于原始合同模板,更新关键日期和序列号,自动生成续签合同 """defgenerate_renewal_contract(original_contract,renewal_data):""" 生成续签合同 original_contract: 原合同信息 {合同编号, 对方单位, 合同金额, 模板路径} renewal_data: 续签信息 {新生效日期, 新到期日期, 调整金额(如有), 新合同编号} """fromdocximportDocumentfromdatetimeimportdatetimeimportshutilimportos template_path=original_contract["模板路径"]old_contract_id=original_contract["合同编号"]new_contract_id=renewal_data["新合同编号"]# 生成新的合同编号(如果未指定)ifnotnew_contract_id:# 格式:HT-2025-0001-续1parts=old_contract_id.split("-")seq=int(parts[-1])ifparts[-1].isdigit()else0new_contract_id=f"HT-{datetime.now().year}-{seq+1:04d}"# 复制模板new_doc_path=f"{os.path.dirname(template_path)}/{new_contract_id}_续签合同.docx"shutil.copy2(template_path,new_doc_path)# 打开新文档并填充数据doc=Document(new_doc_path)# 替换合同编号forparagraphindoc.paragraphs:forruninparagraph.runs:ifold_contract_idinrun.text:run.text=run.text.replace(old_contract_id,new_contract_id)# 替换日期replace_dates={"生效日期":renewal_data.get("新生效日期",datetime.now().strftime("%Y年%m月%d日")),"合同有效期":f"{renewal_data.get('新生效日期','')}至{renewal_data.get('新到期日期','')}","签订日期":datetime.now().strftime("%Y年%m月%d日"),}forparagraphindoc.paragraphs:forkey,valueinreplace_dates.items():forruninparagraph.runs:ifkeyinrun.text:run.text=run.text.replace(run.text.split(key)[0],str(value))# 如果有金额调整if"调整金额"inrenewal_dataandrenewal_data["调整金额"]!=original_contract["合同金额"]:old_amount=str(original_contract["合同金额"])new_amount=str(renewal_data["调整金额"])forparagraphindoc.paragraphs:forruninparagraph.runs:ifold_amountinrun.text:run.text=run.text.replace(old_amount,new_amount)doc.save(new_doc_path)return{"新合同编号":new_contract_id,"文件路径":new_doc_path,"生成时间":datetime.now().isoformat()}

四、踩坑经验

坑1:合同台账的到期日期格式不统一

现象:合同台账是多个部门维护的,有些日期是2024/12/31,有些是2024.12.31,有些甚至写了2024年12月31日,导致日期计算失败。

原因:人工维护Excel没有做数据校验,各人填写的格式五花八门。

解决:

  • 在读取合同台账后,先用正则提取各种日期格式做统一转换
  • 对无法解析的日期标记为"日期格式异常,请手动修正"
  • 如果公司有合同管理系统,直接从系统导出(格式统一)
  • 如果只能用Excel,用数据验证限制日期格式输入

坑2:OA审批流程API的权限问题

现象:用影刀调用飞书/钉钉的审批API时,返回"没有权限"。

原因:调用审批API需要特定的应用权限(飞书需要approval:instance:readonly等权限scope)。

解决:

  • 先在飞书开放平台的应用中配置好审批权限scope
  • 确保应用被安装到了需要查询审批的空间中
  • 使用管理员账号获取access_token
  • 查询审批实例时只能查到当前应用发起的审批(跨应用查不到)

坑3:PDF合同OCR识别关键字段失败

现象:OCR识别PDF合同的合同编号时,把"HT-2024-0001"识别成了"HT-2024-0O01"(零和大写O混淆)。

原因:扫描件质量不高 + OCR引擎对数字字母的识别精度有限。

解决:

  • 合同编号设计时避免使用容易混淆的字符(0/O, 1/I/l, 2/Z等)
  • 使用正则修正:合同编号如果有固定格式(如HT-数字-数字),做格式校验和修正
  • 优先使用电子签署的合同(文字可复制),而非扫描件

坑4:续签提醒重复发送

现象:每天都发一遍续签提醒,负责人烦死了,把机器人消息屏蔽了。

原因:没有记录已发送状态,每天扫描台账都会重新发送提醒。

解决:

  • 增加"提醒记录"表,记录每次提醒的发送时间、接收人和合同编号
  • 同一合同同一级别提醒7天内不重复发送(可配置)
  • 如果接收人已确认"已知晓",不再发送提醒

坑5:合同模板中的嵌套表格

现象:合同模板中有表格(如付款计划表、验收标准表),用简单文本替换无效。

原因:Word表格中的文字替换需要遍历到表格层,不能只处理段落层。

解决:

  • Python方案:使用python-docx库逐层遍历(段落→表格→单元格→段落)
  • 影刀方案:把表格单独放在Excel中处理,生成后再插入Word
  • 更简单的:把复杂表格放在合同附件中,正文不包含表格

坑6:合同文件路径失效

现象:归档后的合同,过了一段时间在台账里点的文件路径打不开了。

原因:合同文件被手动移动了位置、文件服务器迁移了、或者路径中的中文文件夹被重命名了。

解决:

  • 合同文件路径使用绝对路径(如\\fileserver\contract\2024\HT-2024-0001.pdf),不要用相对路径
  • 定期(如每月)扫描一次合同台账中的文件路径是否可访问
  • 如果文件不存在,自动发通知给负责人

五、常见问题速查

#现象原因解决方式
1合同台账读取后部分行空白Excel中有合并单元格导致pandas读取异常读取前先取消合并单元格,或使用pd.read_excel()的skiprows参数跳过多余表头
2飞书消息推送后对方收不到机器人不在对应群组或不具备消息推送权限检查飞书机器人的权限scope;确认机器人已被添加到目标群组
3Word替换占位符后格式丢失占位符被拆分成多个run,替换后合并导致格式错乱使用python-docx时注意保持run的格式;先清除所有格式再替换,最后统一设置格式
4续签合同生成后找不到文件生成路径中有不存在的子文件夹生成前先检查目标路径是否存在,不存在则用os.makedirs()创建
5OA审批催办发给了错误的人审批节点中可能有会签/或签等多重规则,未正确处理查询API返回的审批节点详情,区分"审批人"和"抄送人",只催办当前审批节点的人
6合同长期处于"审批中"但实际已签完签署完成后没有更新台账中的审批状态增加"每日核对"任务:比对OA系统审批状态和台账状态,不一致的自动同步

六、推荐资源

  • python-docx库:https://python-docx.readthedocs.io/ — Python操作Word文档
  • 飞书审批开发文档:飞书开放平台-审批应用API
  • 合同管理最佳实践:参考《企业内部控制应用指引第16号——合同管理》
  • 影刀官方文档:Word/Excel操作指令参考
  • 电子签章对接:法大大、上上签等电子签平台提供API对接方案

七、内容标签

影刀RPA合同管理到期提醒续签自动化模板填充审批催办归档管理合同生命周期法务自动化OA对接


注意:合同文件属于企业核心法律文件,自动化处理时请确保文件存储安全、权限管控到位。合同模板中的条款变更需要经过法务审核,自动化流程仅负责技术层面的字段填充和流程推进,不涉及条款内容的自动修改。涉及电子签署的方案,请选择具有《电子签名法》合规资质的电子签平台。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询