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

2019-07-08  本文已影响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;
    }
上一篇下一篇

猜你喜欢

热点阅读