TT Lab
はじめる
学ぶ 学習パス コース

EAI 中間層をつくる

請求ファイルを受け取り、照合し、一度だけ入れる

TT Labで続きを見る

目標

提携保険会社の請求ファイルをSFTPで安全に受け取り(完了フラグ・チェックサム・原子的な名前の変更・再実行しても安全)、ヘッダー・トレーラーを検証し、ファイル単位で取り込んだうえで、自社の受付元帳と突合します。

なぜ重要なのか

ファイル連携の事故は境界で起きます。書き込み中のファイル、転送中の破損、再実行による二重の取り込み、突き合わせていない数字です。4つとも、エラーなく黙って通り過ぎ、決算や顧客からのクレームで初めて表に出ます。

ステップ

  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>、プレースホルダーはバイト数です)と、取得したファイルを照合してください。違っていればそのファイルを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、違っていれば1行目に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で終了)、ファイル1つは1トランザクション(すべてか、何もなしか)にします。
  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の2行を書き、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と比較します。1つのファイルが壊れていても残りを止めず、終了コードだけを覚えておいてください。

一時的な名前で受け取り、リモートにマークする

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でデコードします。

ファイル単位で1回だけ入れる

/root/eaimw/batch/load.py --db <パス> <ファイル>が、検証済みのファイルだけを1トランザクションで取り込み、load_logで再取り込みを防ぐようにしてください。

isolation_level=Noneで開き、BEGIN … COMMITを自分で書きます。請求番号の主キー衝突(IntegrityError)が起きたらROLLBACKして、前の行まで巻き戻します。

自社の元帳と突合する

/root/eaimw/batch/recon.pyでclaimsと元帳を突合し、当日のファイルを基準にした結果を/root/eaimw/batch/recon.csvに出力してください。

両側の請求番号の和集合を並べ替えて回します。片側だけで回すと、反対側にだけある件を見逃します。元帳はpartner_drop.py --ledgerで作ります。