美文网首页
实战--一个奇怪的对账需求

实战--一个奇怪的对账需求

作者: 红色的飞猪 | 来源:发表于2019-07-08 19:01 被阅读0次

    需求简介

    因响应国家号召,需要对提现用户进行实名认证,最终采用了公司内部支付部门的接口(便宜~~)。
    然后就出现了这个奇怪的对账逻辑。
    首先调用支付的对账接口,返回一个加密的zip文件的字节流。 然后zip文件里面是昨天的excel文件,excel里面是昨日
    的交易流水。
    其次,我方生成一个excel 包含两个sheet,第一个sheet是当日的交易笔数以及交易金额(我方调用支付方,每次要收费).
    第二个sheet里面是交易异常的流水。
    最后,每日通过邮箱定时发送第二步生成的对账文件。

    吐槽

    1. 不提供每日交易总账接口,最好的流程是先看总数是否一致,不一致再对流水。
    2. 流水不是以接口分页的形式进行,而是文件形式。 若是请求暴增,流水暴增靠文件流传输,是否稳妥。

    记录的意义

    这个需求涉及了以下功能

    1. 二进制流的加密解密。
    2. java读取zip文件,直接提取内部文件
    3. excel解析,excel生成。
    4. java发送邮件功能。
      这些功能在日常的业务开发中都是很少用到的, 属于下次做还是从头查起的开发逻辑。

    代码逻辑

    1. 获取二进制流并做加解密处理
    /**
    * 二进制流加密解密
    *
    */
    public String getFile(String date) {
           File decryptFile;
           Map parames = builderParames(date);
           try {
               HttpEntity httpEntity = HttpClientUtil.invokeGetRequestByte(url, parames);
               InputStream input = httpEntity.getContent();
               //密文文件名
               Path balanceFilePath = Paths.get(System.getProperty("server_log_home")).getParent().resolve("trade");
               File parent = balanceFilePath.toFile();
               File encryptFile = balanceFilePath.resolve("encrypt-" + date).toFile();
               if (!encryptFile.exists()) {
                   parent.mkdirs();
                   encryptFile.createNewFile();
               }
               //明文文件名
               decryptFile = balanceFilePath.resolve("decrypt-" + date + ".zip").toFile();
               if (!decryptFile.exists()) {
                   parent.mkdirs();
                   decryptFile.createNewFile();
               }
               //密文数据流
               OutputStream encryptOuput = new FileOutputStream(encryptFile);
               //明文数据流
               OutputStream decryptOuput = new FileOutputStream(decryptFile);
               if (input != null) {
                   IOUtils.copy(input, encryptOuput);
                   encryptOuput.close();
                   input.close();
                   InputStream encryptIS = new FileInputStream(encryptFile);
                   //解密-ras加密工具类,这个满大街都是
                   RSAUtilsNew.decrypt(encryptIS, decryptOuput, Base64.decodeBase64(AuthNameMapUtil.publicKey), RSAUtils.PUBLIC_KEY);
                   decryptOuput.close();
                   encryptIS.close();
               } else {
                   ApiLogger.info("获取对账文件流为空");
                   return "";
               }
           } catch ( Exception e ) {
               e.printStackTrace();
               return "";
           }
           return decryptFile.getPath();
       }
     /**
        * httpclient
        *
        * @param url
        * @param parames
        * @return
        */
       public static HttpEntity invokeGetRequestByte(String url, Object parames) {
           try {
               URIBuilder uriBuilder = getUriBuilder(url, parames);
               HttpGet httpGet = new HttpGet(uriBuilder.build());
               httpGet.setConfig(requestConfig);
               CloseableHttpClient closeableHttpClient = getCloseableHttpClient();
               //注意这个头部配置
               httpGet.setHeader("Content-type", "*/*");
               CloseableHttpResponse response = closeableHttpClient.execute(httpGet);
               return response.getEntity();
           } catch ( URISyntaxException | IOException e1 ) {
               e1.printStackTrace();
           }
           return null;
       }
    /**
        * RSA解密
        * 
        * @param inStream
        *            密文输入流
        * @param outStream
        *            明文输出流
        * @param keyByte
        *            密钥
        * @param keyType
        *            密钥类型(公钥/私钥)
        * @throws InvalidKeySpecException
        * @throws InvalidKeyException
        * @throws IOException
        */
       public static void decrypt(InputStream inStream, OutputStream outStream, byte[] keyByte, String keyType) throws InvalidKeySpecException, IOException, InvalidKeyException {
           try {
               Key key = null;
               if (keyType.equals(PUBLIC_KEY)) {
                   key = convertPublicKey(keyByte);
               }
               if (keyType.equals(PRIVATE_KEY)) {
                   key = convertPrivateKey(keyByte);
               }
               Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
               cipher.init(Cipher.DECRYPT_MODE, key);
               byte[] tmp = new byte[MAX_DECRYPT_BLOCK];
               byte[] cache;
               int len ;
               // 对数据分段解密
               while (-1 != (len = inStream.read(tmp))) {
                   cache = cipher.doFinal(tmp, 0, len);
                   outStream.write(cache, 0, cache.length);
               }
               outStream.close();
               inStream.close();
           } catch ( NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) {
               log.error(e.getMessage());
           }
       }
    
    

    2、解析zip,提取文件

     /**
         * 没有复用的可能直接沙盒处理
         *
         * @param decryptFilePath
         * @throws IOException
         */
        private void zipAnalysisBill(String decryptFilePath) throws IOException {
            //解析
            ZipFile zipFile = new ZipFile(decryptFilePath);
            Enumeration enumeration = zipFile.entries();
            if (enumeration == null) {
                ApiLogger.info("获取的zip文件有误!!!");
                return;
            }
            ZipEntry zipEntry = zipFile.entries().nextElement();
            if (zipEntry != null) {
                InputStream inputStream = zipFile.getInputStream(zipEntry);
                //真正的对账逻辑触发器
                ExcelReader excelReader = new ExcelReader(inputStream, ExcelTypeEnum.XLSX, SohuPayBillModel.class, analysisSohuPaySendExcelListener);
                excelReader.read();
            }
        }
    

    3、 easyExcel 解析excel

    /**
    * 对应的解析model
    */
    @Data
    public class SohuPayBillModel extends BaseRowModel {
        @ExcelProperty(value = {"产品ID"}, index = 0)
        private String productId;
        @ExcelProperty(value = {"产品名称"}, index = 1)
        private String produceName;
        @ExcelProperty(value = {"用户名"}, index = 2)
        private String userName;
        @ExcelProperty(value = {"业务订单号"}, index = 3)
        private String orederId;
        @ExcelProperty(value = {"交易流水号"}, index = 4)
        private String psTransId;
        @ExcelProperty(value = {"钱包流水号"}, index = 5)
        private String transId;
        @ExcelProperty(value = {"认证渠道"}, index = 6)
        private String channel;
        @ExcelProperty(value = {"手续费"}, index = 7)
        private double fee;
        @ExcelProperty(value = {"处理状态"}, index = 8)
        private String tradeStatus;
        @ExcelProperty(value = {"认证状态"}, index = 9)
        private String verifyStatus;
        @ExcelProperty(value = {"提交时间"}, index = 10)
        private String commitTime;
        @ExcelProperty(value = {"更新时间"}, index = 11)
        private String updateTime;
        @ExcelProperty(value = {"备注"}, index = 12)
        private String desrc;
    }
     /**
     * Created with IntelliJ IDEA.
     * Description:
     * 解析响应器
     *
     * @author: rd
     * @Date: 2019-06-27
     */
    @Service
    public class AnalysisSohuPaySendExcelListener extends AnalysisEventListener {
        private List<SohuPayBillModel> sohuPayBillModelList = Lists.newArrayList();
        @Autowired
        AuthRealNameRedisOperatorService realNameRedisOperatorService;
        @Autowired
        GenerateCheckBillExcelService generateCheckBillExcelService;
        @Autowired
        SohuMailService sohuMailService;
    
        /**
         * @param object  one row data
         * @param context analysis context
         */
        @Override
        public void invoke(Object object, AnalysisContext context) {
            SohuPayBillModel sohuPayBillModel = new SohuPayBillModel();
            ArrayList<String> arrayList = (ArrayList<String>) object;
            if (arrayList.size() <= 13) {
                ApiLogger.info("[解析zip] 出现空行!!!");
                return;
            }
            sohuPayBillModel.setProductId(arrayList.get(0));
            sohuPayBillModel.setProduceName(arrayList.get(1));
            sohuPayBillModel.setUserName(arrayList.get(2));
            sohuPayBillModel.setOrederId(arrayList.get(3));
            sohuPayBillModel.setPsTransId(arrayList.get(4));
            sohuPayBillModel.setTransId(arrayList.get(5));
            sohuPayBillModel.setChannel(arrayList.get(6));
            String feeString = arrayList.get(7);
            try {
                double fee = Double.parseDouble(feeString);
                sohuPayBillModel.setFee(fee);
            } catch ( Exception e ) {
                ApiLogger.info(feeString);
                sohuPayBillModel.setFee(0);
            }
            sohuPayBillModel.setTradeStatus(arrayList.get(8));
            sohuPayBillModel.setVerifyStatus(arrayList.get(9));
            sohuPayBillModel.setCommitTime(arrayList.get(10));
            sohuPayBillModel.setUpdateTime(arrayList.get(11));
            sohuPayBillModel.setDesrc(arrayList.get(12));
            sohuPayBillModelList.add(sohuPayBillModel);
        }
    
        /**
         * if have something to do after all  analysis
         *
         * @param context
         */
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            ApiLogger.info("[解析zip] 解析完成,开始对账逻辑");
             //此处是校验逻辑
            CheckBillModel checkBillModel = checkResult(sohuPayBillModelList, DateUtil.getYesterday());
             //生成excel逻辑
            String path = generateCheckBillExcelService.writeExcel(sohuPayBillModelList, checkBillModel);
            //此处是发送邮件逻辑
            sohuMailService.sendMessage(path);
            sohuPayBillModelList.clear();
        }
    }
    ***************************************************生成excel**********************
    
      
      public String writeExcel(List<SohuPayBillModel> sohuPayBillModelList, CheckBillModel checkBillModel) {
            File file = Paths.get(System.getProperty("server_log_home")).getParent().resolve("trade").resolve("3602-" + DateUtil.getYesterday() + ".xlsx").toFile();
            try {
                if (!file.exists()) {
                    file.createNewFile();
                }
                OutputStream out = new FileOutputStream(file);
                ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);
                Sheet checkBillSheet = new Sheet(1, 1, CheckBillModel.class);
                checkBillSheet.setSheetName("对账汇总");
                Sheet diffBillSheet = new Sheet(2, 1, DiffBillModel.class);
                diffBillSheet.setSheetName("差异订单明细");
                writer.write(Lists.newArrayList(checkBillModel), checkBillSheet);
                //差异订单明细
                List<DiffBillModel> diffBillModels = getCheckDiffModeList(sohuPayBillModelList, checkBillModel);
                writer.write(diffBillModels, diffBillSheet);
                writer.finish();
            } catch ( Exception e ) {
                e.printStackTrace();
                return "";
            }
            return file.getPath();
        }
    
    1. java发送邮件功能
     public void sendMessage(String path) {
            // 基础配置功能
            Session session = Session.getInstance(properties);
            Transport transport;
            try {
                transport = session.getTransport();
                transport.connect("发件人邮箱账号", "发件人邮箱密码");
                Message msg = getMimeMessage(session, path);
                transport.sendMessage(msg, new Address[]{
                        new InternetAddress("收件人"),
               });
                transport.close();
            } catch ( MessagingException | UnsupportedEncodingException e ) {
                e.printStackTrace();
            }
    
        }
    
    
        private static Message getMimeMessage(Session session, String path) throws MessagingException, UnsupportedEncodingException {
            MimeMessage msg = new MimeMessage(session);
            msg.setFrom(new InternetAddress("发件人"));
            msg.setRecipients(MimeMessage.RecipientType.TO, new Address[]{"收件人"
                   });
            msg.setSubject("日常对账");
            MimeBodyPart textContent = new MimeBodyPart();
            textContent.setContent("hi,all: <br/> 今日对账详情请看附件!", "text/html;charset=UTF-8");
            MimeBodyPart attachment = new MimeBodyPart();
            DataHandler dataHandler = new DataHandler(new FileDataSource(path));
            attachment.setDataHandler(dataHandler);
            attachment.setFileName(dataHandler.getName());
            MimeMultipart mimeMultipart = new MimeMultipart();
            mimeMultipart.addBodyPart(textContent);
            mimeMultipart.addBodyPart(attachment);
            msg.setContent(mimeMultipart);
            msg.setSentDate(new Date());
            return msg;
        }
    

    相关文章

      网友评论

          本文标题:实战--一个奇怪的对账需求

          本文链接:https://www.haomeiwen.com/subject/zcwahctx.html