AR模块中通用对账的优化尝试

背景:
用户在唯品会下单后,是可以自由选择不同支付方式进行支付的,支付后,支付系统会将一笔收款单传送给AR,AR财务可以从此处看到收款情况。但是,真实的资金是按照不同支付方式,由银行或者其他渠道与资金系统交互的。在数据传输中,会有可能出现短款,即支付系统有收款单数据,但是资金系统未收到款。这就需要有一种对账的功能,将我们这边的收款情况和银行的流水做匹配。目前是银行将各自的流水文件每天传送给支付系统,再由支付将银行流水文件传到vos上。AR这边每天去拉取vos上的文件,进行解析后,与AR系统的收退款单数据做比较。最后将比较结果做成报表给到用户分析。

目前的问题点:
早期的时候,因为支付方式不对,对接的渠道少,同时每个银行给到的对账单文件和格式都不一样,由此每一种对账方式都是单独的一套代码,目前仅这种支付方式对账的任务就已经达到了23个,相关数据库表46个。而且后续每新增一个支付方式,按照之前的代码框架,要新增一个任务两个表和一堆配置信息和报表,需要至少3天的开发和测试工作量。这种设计显得非常不合理,为后续运维拓展更加方便,同时减轻数据库压力,需要优化成易拓展运维的通用形式。

前期的业务和代码分析:
通用点:所有的支付方式都需要从vos上拉取文件,解析数据后,存入到接口表中。然后根据支付流水号去抓取AR收款单数据后,将金额进行比较,最后输出结果表。
不同点:银行给的对账单文件格式都不一样:第一是对账单格式和名称不一样,有.csv,.txt,.xlsx,.zip,.xls,.gzip,.bin文件,每种文件的解码方式不一样,而且有的对账单文件前几列是无法解析的。这一部分无法做成完全的通用,但是可以将每种解码方式都写好后,再根据文件的格式去走不通的解码即可。然后文件前面多少列无需解析也可以通过配置文件指定。第二是字段名称含义和顺序不一样,但是银行给的对账文件上大多数字段用户并不关注,同时我们对账用到的字段仅有3个:收退款标识,支付流水号,金额。所以没必要将每种对账方式解析出的数据都落到不同表中,仅需要新增一个attribute字段足够多的通用接口表,用于将vos上拉到的文件字段全部落到通用接口表中即可。同时增加一个配置,指定每种对账方式的三个对账关键字段的位置即可。

代码关键片段:
拉取文件

	public List<UnionPayObj> downLoadFile(List<ArCfgLookup> lookups){List<UnionPayObj> unionPayObjs = new ArrayList<>();for(ArCfgLookup arCfgLookup:lookups){if(arCfgLookup.getStartDate().equals(arCfgLookup.getEndDate())){continue;}ArVosSFTPSesionObj vosObj = getSftpSessionObj(arCfgLookup.getTagFlag());vosObj.setStartDate(FcsArDateUtil.convertDateToString2(arCfgLookup.getStartDate()));String fileName =vosObj.getStartDate().concat("-").concat(arCfgLookup.getAttrbiute1());vosObj.setFilename(fileName);creatLocalPath(vosObj);VosFileDownHandler vosFileDownHandler = VosFileDownHandlerFactory.INSTANCE.getVosHandler(vosObj.getVosBucket(),vosObj.getHost(), vosObj.getAccessKey(), vosObj.getSecretKey());try{if(!arCfgLookup.getDescription().contains("TEST")){vosFileDownHandler.dowload(vosObj.getFilename(), vosObj.getLocalPath());}if(arCfgLookup.getStartDate().before(arCfgLookup.getEndDate())){arCfgLookup.setStartDate(FcsArDateUtil.addDayByDate(arCfgLookup.getStartDate(), 1));//endDate要另起任务去根据当前时间轮训更新}arCfgLookupService.update(arCfgLookup);unionPayObjs.add(new UnionPayObj(fileName,arCfgLookup,vosObj));} catch (Exception e) {log.info("下载对账文件失败:"+vosObj.getFilename()+"地址:"+vosObj.getLocalPath());}}return unionPayObjs;}```按照类型解析```javapublic void parsingData(List<UnionPayObj> unionPayObjs){log.info("开始解析数据");for(UnionPayObj unionPayObj:unionPayObjs){List<List<String>> results = null;if(unionPayObj.getFileName().toLowerCase(Locale.ROOT).endsWith(".txt")){results = parsingTxt(unionPayObj);}else if(unionPayObj.getFileName().toLowerCase(Locale.ROOT).endsWith(".csv")){results = parsingCsv(unionPayObj);}else if(unionPayObj.getFileName().toLowerCase(Locale.ROOT).endsWith(".bin")){results = parsingBin(unionPayObj);}else if(unionPayObj.getFileName().toLowerCase(Locale.ROOT).endsWith(".xlsx")){results = parsingXlsx(unionPayObj);}else if(unionPayObj.getFileName().toLowerCase(Locale.ROOT).endsWith(".xls")){results = parsingXls(unionPayObj);}else if(unionPayObj.getFileName().toLowerCase(Locale.ROOT).endsWith(".zip")){results = parsingZip(unionPayObj);}else if(unionPayObj.getFileName().toLowerCase(Locale.ROOT).endsWith(".gzip")){results = parsingGzip(unionPayObj);}arIntCommonImportService.convToArIntCommon(results,unionPayObj.getArCfgLookup());}}
	public List<List<String>> parsingTxt(UnionPayObj unionPayObj) {log.info("开始解析数据txt文件");List<List<String>> txtData = new ArrayList<>();int inputStreamCache = 5 * 1024 * 1024;try (FileInputStream fileInputStream = new FileInputStream(unionPayObj.getArVosSftpSesionObj().getLocalPath() + "/" + unionPayObj.getFileName());InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);BufferedReader reader = new BufferedReader(inputStreamReader, inputStreamCache)) {String line;int index= 0;while ((line = reader.readLine()) != null) {index ++;if(index<= Integer.parseInt(unionPayObj.getArCfgLookup().getAttrbiute5())){continue;}String[] split = line.split("[|\\t]");txtData.add(new ArrayList<>(Arrays.asList(split)));}} catch (IOException e) {log.error(eventName + unionPayObj.getFileName()  + e.getMessage());}return txtData;}

数据落表之前根据配置将对账关键字段捞出放到前3

	public void getMainParam(List<String> sts,ArCfgLookup lookup){String payTypeSign = sts.get(Integer.parseInt(lookup.getAttrbiute2()));String payNumber = sts.get(Integer.parseInt(lookup.getAttrbiute3()));String amount = sts.get(Integer.parseInt(lookup.getAttrbiute4()));//根据金额来判断收退款if(lookup.getAttrbiute2().equals(lookup.getAttrbiute4())){payTypeSign = new BigDecimal(amount).signum() > 0 ? "收款" : "退款";}//此处插入为文件带下来的对账流水号,并不一定是真实流水号,对账时再调用osp查询List<String> newList = new ArrayList<>(sts.size());newList.add(lookup.getLookupCode());newList.add(payTypeSign);newList.add(payNumber);newList.add(amount);sts.addAll(0, newList);}

数据准备好后对账片段

	public void processRecData(ArIntCommonImport arIntCommonImport,List<ArMainReconciliationResult> arMainReconciliationResults){List<ArIntRecIn> recInts = arIntRecInService.listByPayNumber(arIntCommonImport.getAttribute2());BigDecimal recAmount = getRecAmount(arIntCommonImport.getAttribute2());if(CollectionUtils.isEmpty(recInts)){arIntCommonImport.setProcessFlag("E");arIntCommonImport.setErrorMessage("收款单缺失!");return;}if(!FcsArNumberUtil.compareToOnBigDecimal(recAmount,new BigDecimal(arIntCommonImport.getAttribute3()))){arIntCommonImport.setProcessFlag("E");arIntCommonImport.setErrorMessage("收款单金额与对账单金额不等!收款单金额"+recAmount.toString());return;}ArIntRecIn recIn = recInts.get(0);arIntCommonImport.setProcessFlag("S");arIntCommonImport.setErrorMessage("对账成功!");ArMainReconciliationResult arMainReconciliationResult = buildReconsResult(arIntCommonImport,"收款",recIn.getOrderNum(),recIn.getGlobalId());arMainReconciliationResults.add(arMainReconciliationResult);}

优化后的提升:
后续新增对账方式无需新增数据库表和任务和报表。在不新增文件类型的情况下,无需修改代码,仅在配置表上增加配置用于识别新的对账方式即可。用户查看对账结果的报表也无需新增,报表上新增对账方式后,按照筛选对账方式进行查看即可。

最后贴一下GIT地址代码对比
老的对账方式 仅给一个支付宝对账的示例
拉取对账文件 ARTASK下的ArIntfcAliPayMain.java
对账 ARTASK下的ArAliPayAccountExecMain.java

新的对账方式
拉取对账文件 ARTASK下的 ArCommonAccountFromVosMain.java
对账 ARTASK下的 ArExcelCommonImportMain.java

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/1962.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ffmpeg初体验

一&#xff1a;安装 sudo yum install epel-release -y sudo yum update -ysudo rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpmyum -y install …

在 Oracle 数据库中使用正则表达式

在 Oracle 数据库中使用正则表达式 0. 引言1. 什么是正则表达式&#xff1f;2. Oracle 数据库正则表达式支持3. 用于正则表达式的 Oracle 数据库 SQL 函数4. 正则表达式中支持的元字符5. 构建正则表达式 0. 引言 本文介绍 Oracle 数据库的正则表达式支持。本文涵盖以下主题&am…

Unity构建详解(10)——Unity构建流程

【前言】 我们知道从源代码到可执行文件有四个步骤&#xff1a;预编译、编译、汇编、链接 预编译&#xff1a;处理源代码文件中的以“#”开始的各种预编译指令编译&#xff1a;通过语法语义分析等将源代码文件转为中间语言文件并进行优化&#xff0c;再生成汇编代码文件汇编&…

Vs Code npm install 报错解决方法

用的人家的前端框架发现是封装过的&#xff0c;要修改人家前端的话还得把前端源码放在Vs Code 上运行&#xff0c;后端放在IDEA上运行&#xff0c;然后前后端并行开发&#xff0c;在配置前端环境时遇到&#xff1a; npm install 这个的原因是我把node下载到D盘了权限不够框框爆…

android学习笔记(五)-MVP模式

1、MVP模式demo的实现&#xff0c;效果下&#xff1a; 2、创建一个Fruit类&#xff1a; package com.example.listview; //Fruit类就是Model&#xff0c;表示应用程序中的数据对象。 public class Fruit {private int imageId;private String name;private String price;publi…

代码随想录算法训练营Day6 | 242.有效的字母异位词 ●349. 两个数组的交集 ● 202. 快乐数● 1. 两数之和

基础&#xff1a; 1.哈希表是根据关键值进行直接访问的数据结构&#xff0c;时间复杂度是O(1)&#xff0c;也就是通过数组的索引下标&#xff0c;直接访问数组中的元素哈希表的作用就是用来快速判断一个元素是否出现在集合里。 2.常见的哈希结构&#xff1a; 数组set &#…

计算机视觉 | 交通信号灯状态的检测和识别

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本项目旨在使用计算机视觉技术检测交通信号灯的状态&#xff0c;主要针对红色和绿色信号灯的识别。通过分析输入图像中的像素颜色信息&#xff0c;利用OpenCV库实现对信号灯状态的检测和识别。 目录 一、项目背景 二、项目功能…

CalcPad(2) 单位设置和绘制图表

CalcPad(2) 单位设置和绘制图表 Hi uu们&#xff0c;CalcPad用的还好吗&#xff1f;有发现一些问题吗&#xff1f; 在我的使用中&#xff0c;经常需要指定一些计算结果的符号&#xff0c;比如说我希望ADC最小分辨率的计算结果是以uV展示&#xff0c;那我们该怎么操作呢&#…

DB索引B+树SQL优化

数据库的索引就像一本书的目录&#xff0c;查数据快人一步&#xff0c;快速定位&#xff0c;精准打击&#xff01; 什么是数据库的索引&#xff1f; 官方介绍索引是帮助MySQL高效获取数据的数据结构。更通俗的说&#xff0c;数据库索引好比是一本书前面的目录&#xff0c;能加…

php时间人性化展示

在PHP中,可以使用date()函数和strtotime()函数来实现时间的人性化展示。下面是一个示例代码: <?php // 获取当前时间戳 $timestamp = time();// 格式化时间 $formattedTime = date(Y年m月d日 H:i:s, $timestamp);echo

入坑 Node.js 1

原文&#xff1a;https://blog.iyatt.com/?p14717 前言 前面刚刚对 Spring Boot 有了个概念&#xff0c;再来学学 Node.js&#xff0c;顺便当学 JavaScript&#xff0c;为后面入前端做准备。 环境 Node.js 20.12.2 官方 API 文档&#xff1a;https://nodejs.org/docs/lat…

IBM SPSS Statistics for Mac:强大的数据分析软件

IBM SPSS Statistics for Mac是一款功能强大的数据分析软件&#xff0c;专为Mac用户设计&#xff0c;提供了一系列专业的统计分析和数据管理功能。无论是科研人员、数据分析师还是学生&#xff0c;都能从中获得高效、准确的数据分析支持。 IBM SPSS Statistics for Mac v27.0.1…

day_8题解

利用最大公约数求最小公倍数 #include<iostream> using namespace std;int gcd(int a,int b) {return b?gcd(b,a%b):a; }int main() {long long a,b;cin>>a>>b;long long ansgcd(a,b);cout<<(a*b)/ans<<endl;return 0; }排序遍历&#xff0c;记…

支持中文繁体,支持同时配置并启用飞书和Lark认证,JumpServer堡垒机v3.10.8 LTS版本发布

2024年4月22日&#xff0c;JumpServer开源堡垒机正式发布v3.10.8 LTS版本。JumpServer开源项目组将对v3.10 LTS版本提供长期的支持和优化&#xff0c;并定期迭代发布小版本。欢迎广大社区用户升级至v3.10 LTS最新版本&#xff0c;以获得更佳的使用体验。 在v3.10.8 LTS版本中&…

一款支持串口、网口自定义协议的调试软件

ComMax通讯调试软件是一款支持自定义串口&#xff0c;网口通讯协议的调试软件&#xff0c;用户可以根据需要&#xff0c;自定义协议包&#xff0c;然后根据接受的数据选择要发送的数据包。是一款强大、好用的调试软件。 点击下载 提取码&#xff1a;wmfg 不用安装 下载解压即…

《史铁生》-随记

史铁生的文案进一段总是刷到&#xff0c;文字在某些时候真的是一种无形的动力。小时候学过的书&#xff0c;长大了才会更加理解其中的蕴意。如看到的文字所说&#xff0c;教育具有长期性和滞后性&#xff0c;就像一个闭环&#xff0c;多年后你有一个瞬间突然意识到什么&#xf…

怎么通过微信小程序实现远程控制包间内的电器

怎么通过微信小程序实现远程控制包间内的电器呢&#xff1f; 本文描述了使用微信小程序调用HTTP接口&#xff0c;实现控制包间内的电器&#xff0c;专用的包间控制器&#xff0c;可独立控制包间内的全部电器&#xff0c;包括空调。 可选用产品&#xff1a;可根据实际场景需求&…

Vue3 ts使用echarts

在 Vue 3 中&#xff0c;我们使用 ref 来访问元素节点&#xff0c;通过声明变量的方式来获取元素。 在template中&#xff0c;添加一个panel&#xff0c;需要指定尺寸 <div ref"area_panel" style"height:250px;width: 600px;"></div> 在sc…

ChatGPT在线网页版(与GPT Plus会员完全一致)

ChatGPT镜像 今天在知乎看到一个问题&#xff1a;“平民不参与内测的话没有账号还有机会使用ChatGPT吗&#xff1f;” 从去年GPT大火到现在&#xff0c;关于GPT的消息铺天盖地&#xff0c;真要有心想要去用&#xff0c;途径很多&#xff0c;别的不说&#xff0c;国内GPT的镜像…

【Qt】UDP使用注意事项

1、问题描述 使用Qt实现UDP时实现单播、广播时遇到一些意想不到的事情,比如:接受的IP前缀是“::ffff:”;广播时出现回环数据等,特此记录下。 2、UDP示例 下面是一个最简单的UDP示例 #include "UdpSocket.h" #include <QUdpSocket> #include <QDebug…