TT Lab
开始
学习 学习路径 课程

构建 EAI 中间层

接收理赔文件、核对、只入库一次

在 TT Lab 中继续学习

目标

通过 SFTP 安全地接收合作保险公司的理赔文件(完成标志、校验和、原子重命名、可安全重新运行),校验头记录和尾记录,按文件入库,然后与我们的受理账本对账。

为什么重要

文件对接的事故出在边界上——正在写的文件、传输中的损坏、因重新运行造成的重复入库、没有核对的数字。这四种都是没有错误、悄悄地过去,到了结账或客户投诉时才暴露。

步骤

  1. 用 python3 /opt/lab/fixtures/eaimw/batch/partner_drop.py --out /root/eaimw/batch/remote --day 20260923 创建合作方的远端目录,并通过 sftp -D /usr/lib/openssh/sftp-server -b <배치파일>(占位符为批处理文件)cd 到该目录后执行 ls -l,把输出原样保存到 /root/eaimw/batch/remote-ls.txt。
  2. 编写 /root/eaimw/batch/fetch.sh。读取环境变量 REMOTE_DIR(默认 /root/eaimw/batch/remote)·LOCAL_DIR(默认 /root/eaimw/batch/inbox)·QUAR_DIR(默认 /root/eaimw/batch/quarantine),通过 SFTP 获取远端清单,并只把存在 <파일>.done(占位符为文件名)的 .dat 下载到 LOCAL_DIR。
  3. 把标志内容(<바이트수> <sha256>,占位符依次为字节数、sha256)与收到的文件比对。如果不同,就把该文件移到 QUAR_DIR(不要留在 LOCAL_DIR),继续处理其余的,最后以非 0 的退出码结束。
  4. 接收期间,先以 LOCAL_DIR 内的 <파일>.part(占位符为文件名)接收,校验结束后再改成最终名称。接收之后,通过 SFTP 在远端文件和标志的名称末尾加上 .fetched。再次运行时,什么都不接收并以 0 结束。
  5. /root/eaimw/batch/check.py <파일>(占位符为文件名):每行 60 字节(EUC-KR,行尾 LF),第一行 H、最后一行 T、中间为 D,H 日期 = 文件名日期,T 笔数、合计 = D 的笔数、合计。通过时输出 OK <건수> <합계>(占位符依次为笔数、合计)并以 0 结束,不通过时,第一行输出 E <사유>(占位符为原因)并以 2 结束。
  6. /root/eaimw/batch/load.py --db <경로> <파일>(占位符依次为路径、文件名):只把通过校验的文件入库到 SQLite 表 claims(claim_no 기본키, policy_no, amount, claim_date, insured, src_file),并记录到 load_log(file 기본키, records, total, loaded_at)(两处表定义中的韩文词都意为“主键”)。同一个文件不再放入(以 0 结束),一个文件就是一个事务(全部或全无)。
  7. /root/eaimw/batch/recon.py --db <경로> --ledger <원장CSV> --out <결과CSV>(占位符依次为路径、账本 CSV、结果 CSV):用理赔编号把 claims 与账本(claim_no,amount)对起来,按理赔编号顺序写出 claim_no,status,partner_amount,our_amount。status 为 MATCHED·ONLY_PARTNER·ONLY_OURS·AMOUNT_MISMATCH,缺失一侧的金额留空。最后接收并入库今天的文件(CLM_401_20260923.dat),并把与 partner_drop.py --ledger /root/eaimw/batch/ledger.csv --day 20260923 所生成账本对账的结果,输出到 /root/eaimw/batch/recon.csv。

参考

通过 SFTP 查看远端

用 partner_drop.py 创建远端目录,并把通过 sftp -D 批处理列出的输出保存到 /root/eaimw/batch/remote-ls.txt。

在批处理文件中写上 cd 和 ls -l 两行,用 sftp -D /usr/lib/openssh/sftp-server -b 批处理文件 运行。把输出原样重定向。

只接收有标志的文件

让 /root/eaimw/batch/fetch.sh 只把 REMOTE_DIR 中存在 .done 的 .dat 下载到 LOCAL_DIR。

先用 ls -1 取得名称清单,然后对每个 .dat,查看清单中是否有相同名称 + .done(grep -qx)。下载用 get 远端 本地。

校验和不同就隔离

把标志中的字节数、sha256 与收到的文件比对,不同就移到 QUAR_DIR,并在最后以非 0 的退出码结束。

把标志也下载下来,用 read -r size sha 读取,并与 stat -c %s、sha256sum 比较。不要因为一个文件损坏就停下其余的,只要记住退出码就行。

用临时名称接收,并在远端做标记

先用 LOCAL_DIR 内的 .part 接收,校验后改名,并在远端文件和标志上加 .fetched,使重新运行是安全的。

临时文件必须放在与最终目录相同的位置,rename 才是原子的。远端标记用 SFTP 的 rename 命令。

核对头记录和尾记录

让 /root/eaimw/batch/check.py <文件> 校验行长度(字节)、H/D/T 结构、日期、笔数和合计(OK <笔数> <合计> / E <原因> 和退出码 2)。

用 rb 读取文件,按行拆分,并检查 len(行)==60。把 T 的笔数(1–8)、合计(8–23)与从 D 中亲自统计出的值比较。姓名用 euc_kr 解码。

按文件只放入一次

让 /root/eaimw/batch/load.py --db <路径> <文件> 只把通过校验的文件用一个事务入库,并用 load_log 防止重新入库。

用 isolation_level=None 打开,直接写 BEGIN … COMMIT。如果发生理赔编号主键冲突(IntegrityError),就 ROLLBACK,把前面的行也一并回退。

与我们的账本对账

用 /root/eaimw/batch/recon.py 对 claims 与账本对账,并把以今天文件为基准的结果输出到 /root/eaimw/batch/recon.csv。

对两边理赔编号的并集排序后遍历。只遍历一侧,就会漏掉只存在于另一侧的笔。账本用 partner_drop.py --ledger 生成。