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

银行现场的语言

在装载之前把收到的文件退回去

在 TT Lab 中继续学习

目标

做出一个守门程序 filegate.py,在加载之前检查对方机构发来的定长和 CSV 接收文件,一旦不符就拒绝整个文件。判定以机器可读的报告、退出码,以及要退还给对方的拒绝文件的形式留下来。

为什么重要

加载之后再去找错误的行,永远比在门口拒绝更贵。已经进去的行,被别的批处理拿走了,要撤回,就会跟着出现更正单据和道歉电话。即使 998 笔完好,只要有 2 笔与规格不符,这个文件也一行都不放进去。如果只放进去一半,对方的尾记录和我们的账本就永远对不上。 定长文件的宽度不是字符数,而是字节数。一个韩文字符在 CP949 中占 2 个字节,在 UTF-8 中占 3 个字节,所以编码一变,行的长度本身就变了。CSV 不能把引号内的逗号和换行当作分隔符。 评分器不会相信你写的文字。它会在临时目录里摆好它自己做出的接收文件,运行你的脚本,并对照判定和错误码。机构代码、笔数和金额每次运行都不同。

步骤

  1. 创建并运行 /root/bankfile/gen_inbound.py,在 /root/bankfile/inbound/ 中生成四家对方机构一整天的 5 个文件。
  2. 让 /root/bankfile/filegate.py 对照 CSV 接收文件的骨架与汇总尾记录的笔数和合计,并输出报告和退出码。
  3. 在 filegate.py 中加入定长(.txt)的处理。要按字节而不是字符数,来量一行是不是恰好 80 个字节。
  4. 让 filegate.py 把以与规格不同的编码送来的文件,用 ENC 拒绝。每个文件读取时使用的编码都写入报告。
  5. 把 filegate.py 的 CSV 解析按 RFC 4180 改好。被引用的逗号和换行不是字段分隔符。
  6. 在 filegate.py 中加入字段级校验,并为每个被拒绝的文件留下拒绝文件。只要有一个错误,这个文件就一行都不加载。
  7. 让 filegate.py 把同一机构、同一序列号的重发用 DUP 拒绝。哈希相同是 resend,不同则是 conflict。
  8. 处理你自己的收件箱,留下 /root/bankfile/gate_report.json、/root/bankfile/rejected/ 和 /root/bankfile/receipt.md。

参考

生成一整天的收件箱

创建并运行 /root/bankfile/gen_inbound.py,在 /root/bankfile/inbound/ 中生成 5 个文件。它们是:定长(CP949)120 笔的文件、把它连字节都原样再发一次的重发件、CSV 90 笔(至少有 3 行收款人姓名中带逗号)、尾记录笔数比实际少 1 的 CSV 60 笔,以及虽是定长却以 UTF-8 送来的文件。

名称是 IN-<YYYYMMDD>-<기관코드 6자리>-<꼬리>.txt|.csv(占位符依次为 6 位机构代码、尾部)。定长的宽度是字节,所以要先用 text.encode('cp949') 量好,再补空格。重发件只要把同样的字节换个名字再写一遍就行。CSV 里带逗号的姓名,请用双引号括起来。

先核对尾记录的笔数与合计

让 /root/bankfile/filegate.py 确认 CSV 接收文件的 H、D、T 骨架,并把尾记录里的笔数和合计,与实际数出来的值核对。输出报告和退出码 0、2。

笔数不一致和合计不一致是不同的事故,所以要分别设码(TRAILER_COUNT、TRAILER_TOTAL)。报告中的 records 和 total,不是尾记录里写的值,而是你自己从 D 记录里数出来的值。评分器每次都用不同的机构代码和笔数来测试。

定长的宽度是字节,不是字符

在 /root/bankfile/filegate.py 中加入 .txt 定长的处理。如果一行按 CP949 不是恰好 80 个字节,就用 WIDTH 拒绝,并留下出错的行号。

len(line) 是字符数,而规格所说的是 len(line.encode('cp949'))。切分字段的位置也在字节上,而不是在字符串上。只有按字符数填充了韩文姓名的那一行,宽度才会对不上。

拦住以与规格不同的编码送来的文件

让 /root/bankfile/filegate.py 把 .txt 按 CP949、.csv 按 UTF-8 来读,如果不能用该编码解码,就用 ENC 拒绝。每个文件读取时使用的编码写入报告的 encoding。

bytes.decode() 失败时会抛出 UnicodeDecodeError。把异常的 start 和 reason 写进 detail,对方负责人就能知道是在哪个字节处乱掉的。守门程序不能自动换用别的编码去读。

引号内的逗号不是分隔符

把 /root/bankfile/filegate.py 的 CSV 解析按 RFC 4180 改好。含有被引用的逗号和换行的文件,其 records 和 total 必须正确,引号没有闭合的文件必须拒绝。

标准库的 csv 模块原样实现了引用规则。要传入字符串,就用 io.StringIO 包起来。如果引号没有闭合,后面的行就会被整个吸进同一个字段里,使字段个数对不上。

字段校验、拒绝文件,以及禁止部分加载

在 /root/bankfile/filegate.py 中加入对交易编号、收款人姓名、账号和金额的校验,并为每个被拒绝的文件,以表头 line,code,detail 留下 <거절디렉터리>/<파일이름>.reject.csv(占位符依次为拒绝目录、文件名)。只要有一个错误,这个文件就不能进入接收列表。

交易编号在文件内必须唯一,如果重复,就指向后出现的那一行。从定长中切出来的字段,要去掉左右的空格再检查。拒绝文件必须能被对方机构的批处理直接读取,所以要以表格而不是人写的句子来输出。

区分重发的文件与冲突的文件

让 /root/bankfile/filegate.py 把与已经接收的机构、序列号相同的文件,用 DUP 拒绝。文件哈希相同,就在 detail 中写 resend,不同则写 conflict,并在报告中留下 sha256。

序列号在表头记录里。只需要记住前面已被接收的文件——被拒绝的文件并没有加载,所以之后来的同号文件不算重复。哈希要用 hashlib.sha256,对整个文件的字节来求。

处理自己的收件箱并提交接收报告

处理你自己的收件箱,留下 /root/bankfile/gate_report.json 和 /root/bankfile/rejected/,并在 /root/bankfile/receipt.md 中写下 ## 수신 요약 ## 거절한 파일과 사유 ## 재전송으로 판정한 파일 ## 적재하지 않은 이유 ## 상대 기관에 요청할 것 五个小节。

直接使用前面步骤中做好的 /root/bankfile/filegate.py。摘要里要用数字写出接收笔数、拒绝笔数,以及实际将要加载的金额合计。被拒绝的文件要把名称原样写出来,对方才找得到。评分器会重新读取 /root/bankfile/inbound,与你的报告对照,所以报告不要手写,请使用 filegate.py 输出的结果。