简要回答:如何防止重复记录?
为每项检验任务分配固定不变的来源标识;以该标识幂等地执行 ERP 传输,在数据库中强制保证唯一性,并只将已核实的目标单据关联到会计记录。
集成仅在正常流程下工作还不够。网络响应丢失、队列消息重复送达,或两个工作进程同时取得同一任务时,结果也应保持为一个客户、一个订单和一个开票关联。
为什么会产生重复记录?
简要回答:多数重复记录并非因为用户点击了两次按钮,而是因为第一次请求已成功、但响应丢失,在结果不明确时进行了不受控的重试。
检验系统向 ERP 发送创建订单的请求。ERP 已创建记录,但连接中断,集成端未收到响应。如果工作进程将同一请求当作新操作再次发送,就可能创建第二个订单。至少一次投递的队列、并行工作进程、超时后的人工重试以及批量同步中,都存在类似风险。
IETF RFC 9110 将幂等操作定义为:同一请求执行多次,其预期效果与执行一次相同。PUT 具有这一属性;使用 POST 创建新资源时,则需要额外保护。技术设计不应假定网络能够保证恰好一次投递。

应以哪个标识作为记录的主标识?
检验任务、ERP 订单、会计凭证和电子单据的标识各不相同。链条起点应保留固定不变的 源任务 UUID ;其他系统生成的标识应作为与其对应的值,保存在独立字段中。
| 标识 | 所属系统 | 何时生成? | 用途 |
|---|---|---|---|
| 源任务 UUID | 检验系统 | 首次创建任务时 | 所有传输和重试使用的固定任务键 |
| 事件 UUID | 检验系统 | 形成可开票版本时 | 队列消息及已处理事件检查 |
| ERP 客户或订单 ID | ERP | 目标记录被接受时 | 后续更新和状态查询 |
| 会计单据 ID | 会计系统 | 生成单据草稿或记录时 | 财务生命周期 |
| 电子单据 UUID 或编号 | 电子单据流程 | 开具单据时 | 单据自身的技术标识 |
OASIS UBL 2.4 的发票规范定义了发票与订单及其他单据之间的引用字段。GİB 电子发票门户指南也说明,具有相同 UUID 和发票编号的发票不能重复上传。对应的实施要求,是在双向关联表中分别保留各类标识,而不是用电子单据编号替换检验任务的来源标识。
如何生成幂等键?
不应在每次重试时随机生成新键;对于同一商业效果,所有尝试都应使用相同的键。例如,可由以下部分的规范化组合生成:
tenant + hedef_sistem + işlem_türü + kaynak_iş_uuid + kaynak_sürümü
来源版本很重要。再次发送同一报告版本时,应返回已有结果;若经授权的修订将产生新的商业效果,则应为此定义独立事件和明确业务规则。幂等记录除了键,还应保存请求数据的哈希值、首次出现时间、状态和目标响应标识。
Stripe 现行 API 参考说明,连接错误后可使用相同幂等键安全重试创建或更新请求;键相同但参数改变时,请求将被拒绝。这种供应商特定的行为,也可作为内部 ERP 适配器接口约定的参考:同一个键若携带不同的客户、金额或税务映射,不应静默覆盖,而应将冲突转交审查。
为什么仅有应用层检查不够?
采用“先查询是否存在,不存在再插入”的方式时,两个工作进程可能同时查到不存在。因此,数据库层也必须强制保证唯一性。
在集成关联表的 tenant_id + target_system + document_type + source_job_uuid 上建立复合唯一约束。PostgreSQL 现行 INSERT 文档说明,可以通过唯一约束与 ON CONFLICT 在并发竞争下保证插入或更新操作的原子性。如果业务规则不允许更新,应在发生冲突时返回已有目标记录的 ID;若数据不一致,则停止处理并创建审查记录。
UNIQUE (tenant_id, target_system, document_type, source_job_uuid)
aynı anahtar + aynı payload özeti -> mevcut sonucu döndür
aynı anahtar + farklı payload -> çakışma, otomatik belge açma如何设计 Outbox 和队列流程?
如果将检验报告标记为“可开票”和写入 ERP 消息分开执行,就可能产生只完成一半的状态。业务状态与集成事件应在同一本地事务中提交。
- 授权用户确认报告版本和开票范围。
- 应用在同一事务中更新业务状态,并写入带有事件 UUID 的 Outbox 记录。
- 工作进程使用固定的幂等键,将 Outbox 事件发送给目标适配器。
- 收到成功响应后,将 ERP 目标 ID 和响应摘要保存到关联表。
- 如果 ACK 丢失,同一事件会再次送达;消费者识别事件 UUID 和业务关联后,返回已有结果。
- 永久性数据错误不应进入重试循环,而应转入人工审查队列或死信队列。
Microsoft 的事务性 Outbox 指南建议将业务对象与事件保存在同一事务中;Service Bus 指南则指出,由于 ACK 可能丢失且消息可能重复投递,消费者应根据 MessageId 或业务标识实现幂等处理。发送端的重复检测不能替代接收端的唯一性校验。
如何安全映射客户、服务和税务信息?
防止重复记录并不只是技术层面的重试问题。仅因名称写法不同而再次创建同一客户,或使用不同编码对同一服务开票,也会造成业务重复。
- 客户: 在租户范围内,使用已验证的土耳其税号或身份号码(VKN/TCKN),或机构主数据标识进行匹配;不要仅以名称作为键。
- 服务: 将检验方法关联到经批准的 ERP 商品或服务编码表;对于无法匹配的编码,不要通过自由文本自动创建新条目。
- 税务和币种: 根据目标系统的代码表实施版本化映射;映射缺失或无效时,应停止生成单据。
- 金额: 将计算后的明细、折扣和税额与源版本摘要一并锁定;审批后的变更应进入新的修订流程。
- 权限: 分别管理客户创建、映射修改和财务单据生成角色,并保留各自的审计轨迹。
存在歧义时,不应自动选择“最接近的结果”。在人工审查完成前应暂停传输,并记录决策人、时间、变更前后值以及理由。
如何跟踪传输状态?
单一的“成功/失败”字段会掩盖不确定性。状态机应明确指出哪些步骤允许重试。
| 状态 | 含义 | 允许的操作 |
|---|---|---|
| 准备中 | 正在验证映射和请求数据 | 补齐或修正缺失信息 |
| 待发送 | 标识、版本和请求数据摘要已锁定 | 写入 Outbox |
| 结果待确认 | 请求超时,但目标系统可能已经处理成功 | 使用同一幂等键查询或重试 |
| 已传输 | 目标 ID 和响应证据已保存 | 查询状态,不创建副本 |
| 需要审查 | 键冲突或数据映射错误 | 等待授权人员作出决定 |
| 等待取消或冲销 | 需要进行财务更正 | 根据目标系统规则发起独立操作 |
删除已成功传输的记录再重新发送,并不是正确的更正方法。如果财务单据已经生成,取消、退回或冲销应使用各自的标识和审批流程。
对账应执行哪些检查?
每日对账应按标识逐条比对源系统和目标系统的记录,而不是只统计错误日志。
- 已具备开票条件但尚未建立目标关联的业务。
- 同一源业务 UUID 关联了多个目标单据。
- 目标系统中存在,但在源系统关联表中找不到对应项的单据。
- 源版本与已发送请求数据摘要不一致的记录。
- 处于结果待确认状态的时间超过规定时限的尝试。
- 人工映射、取消和冲销操作缺少审批或证据。
对账结果不应触发自动删除或批量合并,而应向授权用户展示可疑记录、其在两个系统中的标识,以及建议采取的安全操作。
如何开展 VizyonTech 集成试点?
Vizyon Periyodik 等检验流程对接 ERP 时,应先测试异常场景,再验证正常流程。测试应涵盖:同一业务被双击提交、ERP 已创建记录但未返回响应、同一队列消息被两个工作进程接收、同一幂等键对应不同请求数据、客户映射无效,以及报告修订。
试点的成功标准不能只是“集成可以运行”。每个场景都应证明目标系统创建了多少条记录、是否建立源与目标的关联、重试是否返回同一结果、不确定状态是否可见,以及未经授权的财务操作是否被阻止。为保护检验报告的决策链,也可结合 电子签名工作流程指南 一并评估。
参考来源
- IETF — RFC 9110,HTTP 语义:幂等方法 (2022 年 6 月;访问日期:2026 年 7 月 21 日)。
- Stripe — API 参考:幂等请求 (现行在线文档;访问日期:2026 年 7 月 21 日)。
- Microsoft Learn — 防止 Azure Service Bus 消息丢失和重复处理 (2026 年 6 月 29 日更新)。
- Microsoft Learn — 实现事务性 Outbox 模式 (2026 年;访问日期:2026 年 7 月 21 日)。
- PostgreSQL — INSERT 与 ON CONFLICT,现行文档 (访问日期:2026 年 7 月 21 日)。
- OASIS — UBL 2.4 发票模型概述 (2024).
- 土耳其税务局 — 电子发票门户用户指南 v1.5 (2013 年 11 月;用于核对技术标识和重复上传规则,访问日期:2026 年 7 月 21 日)。
ERP、会计和电子单据的字段及代码规则,还应根据所用服务商的现行技术文档和机构的财务流程另行核实。