前言

挺长时间没发文了,因为公司有一个紧急项目要赶进度,加班如吃饭喝水,久违的进入到码农的状态。

之所以抽空来发个文,是这个项目才刚上线,时间不长却因为一位新同事的代码引起了生产环境的事故,造成了一批短款,差点让整个团队这段时间的努力付诸东流。

所以,本着好人一生平安的处事原则,百忙之中我依然抽空以文章的形式把这次事故记录下来,希望有做支付相关功能的同行们能够引以为鉴。

经过

1、包装简历不是错

  大家知道,每年到这个时候就是一个公司人员变动最频繁的时候,有些是拿了年终奖走人,有些人是骑驴看唱本,找到下家然后走人,反正这个时间就是传说中的金三银四。

  如果公司这个时间有紧急项目,一定是你难受我难受大家一起难受的阶段,因此,为了应付这种情况,人事顶着很大压力招人,往往不一定能招到合适的。

  我们公司年后就入职了一位新同事,就是专门为这个紧急项目招过来的,简历我有看过,3年工作经验,熟悉Java、SpringBoot、SpringCloud、SpringCloudAlibaba,还会docker、k8s,会前端vue、uniapp用法,有过微信小程序开发经验,且熟悉微信支付、支付宝支付开发。

  其实,前面熟悉java、springboot、springcloud什么的,我们面试没遇到过不写的,基本上没当回事,而后面的docker、k8s也只是个添头,因为来公司没人会让开发同事来操作,会点前端的vue倒是加分项,最重要的是最后一句,熟悉微信支付、支付宝支付开发,这一点一下戳中了人事的软肋,因为主管特别给人事提过会支付功能的优先考虑,所以人事打电话了解后极力推荐给主管。

  主管看后发给我看,我们其实心里都有数,这简历里面的项目一看就是包装过的,因为大家都这么过来的,写的3年经验,大部分其实只有1年或2年,偶尔能遇到没有经验但盲写3年的,对于中小公司而言,面试人员包装简历不是什么问题,重要的是能干活就行。

  改天主管就喊我一起去给这位同事面试了,这人口条很好,我们问的很基本,都是挑他项目里写过的技术来问,基本上都能答出来主要的,主管心里也很满意,面试完后就给人事说让他入职了。

2、能干活不一定会干活

  新同事来周周一入职的,经过了简单的培训,立马就要投入到紧急项目中,好在这是个新项目,不需要他额外熟悉既有的业务,产品评审过后就开始分配工作,因为人员变动导致的工作积压,我和另一位老同事手头上都有离职人员交接的工作,需要日常维护老项目,加上同时参与新项目开发,有些力不从心,主管考虑到这点,就把新项目微信支付相关的功能都交给了这名新同事,因为他简历中确实写了会这部分,而且面试时连我也觉得他明显是会的而且做过的,后面事实证明只是他口条好罢了……



  从真正开始做项目,我就发现此人会的确实挺多,但问题更多,比如git工具只会pull和push,遇到代码冲突不知道如何解决,瞬间拉低了他原本通过面试在我心中塑造的等级。

  再比如他很喜欢使用try..catch,每个接口都加,却不知道我们本身就统一了异常管理,更可怕的是catch中竟然写e.printStackTrace()这玩意也提交上去,我专门给他提醒后他才改成log.error("xxxx:{}", e.getMessage()),我当时看到也无语了,又跑去说你不要getMessage(),这样生产环境不好排查问题,改成log.error("xxxx:", e)就行,他当时反问我这样能打印出来东西?此时我心里已经蒙上了一片阴霾,也发觉到他对我三番四次跑去指点他似乎有些许不满。



  工作忙起来后很多时候你不是领导,你也不太想管那么多,管好自己就行,我也懒得再管他了。后来证明我这种心态也是隐患之一,团队开发成果是由大家一起决定的,果不其然最后出事故连坐了,谁也逃不掉。

3、事故来的就像龙卷风

  等到项目进入提测阶段,测试出来的BUG虽多,但总体不错,新同事负责的那部分之后也通过测试了,我当时觉得他其实还是挺有能力的,支付这块业务上还是有很多痛点的,他能自己一个人做完确实不容易,简历上虽然包装过可干活还是可以的。



  项目上线后,大家都松了一口气,毕竟是个紧急项目,也牵扯到公司今年的战略规划,能完成对我们老同事来讲是一种解脱,对新同事来讲是一种自我价值的证明,3个月没到他提前顺利转正了。



  那句话怎么讲来着,是福不是祸,是祸躲不过,今年属蛇的人据说犯太岁,这才刚开年,我果然间接中弹了,项目上线不到一个月,某天上午客户方突然出现大量短款,造成直接损失接近十万元,好在我们驻点人员较多,紧急情况也有备案,接到通知后马上关闭了开关,等到高峰期一过,才重新打开开关继续运行,否则很可能持续造成更大损失。



  大家心都快跳出来了,好在接下来整个下午都很平静,没有再出现问题,我们根据经验立马判断是上午那段时间处于流量高峰期造成的。

  排查日志后发现竟然是微信退款的回调中进行业务确认时大量失败,从而造成了大量短款。



  至于分析和解决的过程,在后面分析小节中会说明,这里提一嘴,这名新同事知道问题是自己造成的后脸都白了,那会儿说话也不利索,还被其他同事埋怨了几句,样子别提多可怜了,看着让人心酸,事故处理完成后他自己就离职走人了。



  我能感受到这次事故对他的整个程序员人生都是一次重大打击,临走前还专门找他说了些安慰的话之类的,把改正后的代码片段也发给他作为参考,我这人比较心软,见不得相处一段时间的同事那么难受,还把他推给了以前公司熟悉的人事,帮忙给他找新工作,我能做的也就这么多了。



  最后客户方追回了损失大概六七万,还有两三万追不回来了,因为是短款,人家不退你也没办法,这还是在我们驻点人员响应及时的情况下,否则可能今年和这个客户的生意彻底要黄了,公司后来交代事故原因时也没有说是我们代码写的有问题,全部推给调用业务确认接口超时引起的,同时赔偿了没追回的金额,最主要是领导层关系很硬,这一劫算是度过了。

分析

  前面讲了,排查日志后发现是微信退款成功后回调中进行业务确认时大量失败,从而造成了大量短款。那么很多小猿猴就很奇怪,我说的是什么意思。

  这里就要提下微信支付等支付功能开发过程中的必要流程了:

支付:创建业务订单---> 创建支付订单---> 唤起支付收银台---> 输入密码或指纹支付---> 进入支付回调处理 ---> 更改支付状态及业务状态



退款:创建退款订单---> 发起退款---> 进入退款回调处理 ---> 更改支付状态及业务状态

  主要的流程大概是这样,里面还有一些细节,这里针对本次事故着重说下回调处理这块,当一个支付完成后,微信会发给我们的回调接口进行最后的业务处理,正常来讲只需要更改我们自己的业务表状态就行。



  但如果你的项目不是公司自有产品,而是给客户做的,牵扯到客户自己的业务系统,那么就很有可能需要在回调中专门去调用客户系统提供的业务确认接口,简单来讲,就是你要告诉客户系统这笔支付完成了或失败了好让业务系统做一些自己的逻辑处理。

  我拿这次事故项目的业务为例,客户系统的支付环节有一个前提是从号源池中取号,而退款时要进行退号或销号。这里就不单单是一个微信支付的简单实现了,而是一个重要的业务逻辑实现。

  大家可以思考下,我们到底是要先取号再支付还是先支付再取号,同样的,退款时我们是要先退款再退号,还是先退号再退款呢?

我相信,如果不是经常做支付相关业务的小伙伴,在没有经验的情况下一定会写错的,而这里就是我们这位新同事犯的问题:

在退款时,他的代码逻辑写的先退款再退号,在流量高峰期,因为try...catch中写了异常时自动退款,结果刚好发生了预料之外的异常,开始自动退款,好死不死的客户方业务系统也有问题,最终导致退款执行成功了,调用客户方业务系统退号确认接口时却确认失败了,结果就是钱先退给用户了,号却没退掉(人活着,钱没了),在客户的业务系统里这就是一笔短款。

究竟正常的逻辑是怎样的,我以这个案例给大家梳理下就清楚了:

1)、支付时,一定要先完成支付,再处理业务系统的业务。

  比如这里就是先支付再取号,这样的好处就是,正常情况下没事,异常情况下要么支付失败就不走后续,要么支付成功但后续取号失败,这样你服务的客户方没损失,损失的是用户,因为用户已经付钱了,这就是一笔长款,客户方只需要根据用户反应的情况审查一下然后退款给用户就行了。



  相反,如果你写反了,导致的其中一种后果就是用户先取了号,但支付失败了,用户依然可以拿着号来用,这对客户方就是一种损失,钱没收到号还能用。

2)、退款时,一定要先完成业务确认,再执行退款。

  比如这里就是先退号,再退款,这样要么退号失败不会往下走,要么退号成功,但退款失败,损失的依然是用户的钱,客户方怎样都没损失,形成一笔长款,审查过后确认情况没问题再退款给用户即可。



  相反,如果你写反了,导致的后果就是先退款,但退号确认失败了,客户方的号还没退回来,但钱已经打进用户账户了,客户方就损失了,还得找用户追回来,如果用户不给你也没办法,因为是你自己的失误导致的,这就形容严重事故了。

  案例分析梳理的逻辑,不仅仅在这里适用,在其他场景也是适用的,大家可以多看看仔细揣摩下,任何和钱打交道的功能开发都要谨慎,支付功能实现一点都不难,有现成的工具可以使用,难的其实还是业务,遵循一个原则:不管你的业务逻辑怎么写,只要正常及异常情况下,你服务的客户都不会亏钱,那么你的支付逻辑就是正确的。

总结

现在新技术非常多,学起来累不说,还卷,我诚心给大家一点建议:

1)、减少对热门流行技术的病态追逐,沉下心来把基本功打扎实,进入公司后需要用到的开发工具、开发流程、代码规范等等多花点心思学习,因为你就算把分布式技术学的再厉害,这些盖房子用的工具不熟悉,会很容易被看破手脚;

2)、进入公司第一件事,熟悉业务场景,这往往才是你在公司往后的立身之本,工作时间越长你越会明白;

3)、简历不要过分包装,包装无可厚非,很多人都这么干,都是为了生活,但过分包装会带来隐患,比如这里讲的这个同事,专门把微信支付等功能作为亮点,结果缺乏真正的经验导致事故。



最好不要在简历中牵扯到支付相关的介绍,可以说参与过,但不能形成亮点,因为和钱有关的技能都不简单,多对简历中的项目进行润色更有效果。


纯手打,觉得有一滴滴帮助的话,麻烦点个推荐吧啦吧啦~~~~~~

也可以关注下我,本人专注分享工作中的趣事和实战经验哦~~~~~~~~

【Java分享客栈】一个包装过简历的新同事写完微信支付引起事故后果断离职了的更多相关文章

  1. 【Java分享客栈】我为什么极力推荐XXL-JOB作为中小厂的分布式任务调度平台

    前言   大家好,我是福隆苑居士,今天给大家聊聊XXL-JOB的使用.   XXL-JOB是本人呆过的三家公司都使用到的分布式任务调度平台,前两家都是服务于传统行业(某大型移动基地和某大型电网),现在 ...

  2. 【Java分享客栈】SpringBoot整合WebSocket+Stomp搭建群聊项目

    前言 前两周经常有大学生小伙伴私信给我,问我可否有偿提供毕设帮助,我说暂时没有这个打算,因为工作实在太忙,现阶段无法投入到这样的领域内,其中有两个小伙伴又问到我websocket该怎么使用,想给自己的 ...

  3. 【Java分享客栈】一文搞定CompletableFuture并行处理,成倍缩短查询时间。

    前言   工作中你可能会遇到很多这样的场景,一个接口,要从其他几个service调用查询方法,分别获取到需要的值之后再封装数据返回.   还可能在微服务中遇到类似的情况,某个服务的接口,要使用好几次f ...

  4. 【Java分享客栈】一文搞定京东零售开源的AsyncTool,彻底解决异步编排问题。

    一.前言 本章主要是承接上一篇讲CompletableFuture的文章,想了解的可以先去看看案例: https://juejin.cn/post/7091132240574283813 Comple ...

  5. 【Java分享客栈】SpringBoot线程池参数搜一堆资料还是不会配,我花一天测试换你此生明白。

    一.前言   首先说一句,如果比较忙顺路点进来的,可以先收藏,有时间或用到了再看也行:   我相信很多人会有一个困惑,这个困惑和我之前一样,就是线程池这个玩意儿,感觉很高大上,用起来很fashion, ...

  6. 【Java分享客栈】超简洁SpringBoot使用AOP统一日志管理-纯干货干到便秘

    前言 请问今天您便秘了吗?程序员坐久了真的会便秘哦,如果偶然点进了这篇小干货,就麻烦您喝杯水然后去趟厕所一边用左手托起对准嘘嘘,一边用右手滑动手机看完本篇吧. 实现 本篇AOP统一日志管理写法来源于国 ...

  7. 【Java分享客栈】我曾经的两个Java老师一个找不到工作了一个被迫转行了

    前言 写这篇文章的初衷主要是最近发生了两件事,让我感慨良多,觉得踏入这个行业的初始,有些事情就应该长远考虑,这样对职业发展才更有利,仅仅停留在技术的追求上固然能壮大自身,可逆水行舟的程序员们终究会面临 ...

  8. 【Java分享客栈】从线上环境摘取了四个代码优化记录分享给大家

    前言 因为前段时间新项目已经完成目前趋于稳定,所以最近我被分配到了公司的运维组,负责维护另外一个项目,包含处理客户反馈的日常问题,以及对系统缺陷进行优化. 经过了接近两周的维护,除了日常问题以外,代码 ...

  9. 【Java分享客栈】我有一个朋友,和前端工程师联调接口被狠狠鄙视了一番。

    前言 我有一个朋友,昨天和前端工程师联调一个接口,然后被狠狠鄙视了一番. 大家知道,自从前后端分离以后,像我一样一直以Java工程师为傲而自居的码圣们就砍掉了一半脊梁,从此被贴上了"Java ...

随机推荐

  1. 聊一聊如何用C#轻松完成一个TCC分布式事务

    背景 银行跨行转账业务是一个典型分布式事务场景,假设 A 需要跨行转账给 B,那么就涉及两个银行的数据,无法通过一个数据库的本地事务保证转账的 ACID ,只能够通过分布式事务来解决. 在 聊一聊如何 ...

  2. LCT 入门

    这是一份 \(\rm LCT\) 入门总结. 关于 \(\rm LCT\) 的复杂度这里不会提及,只会记录 \(\rm LCT\) 的基本操作和经典例题,但神奇的 \(\rm LCT\) 虽然常数巨大 ...

  3. LaunchScreen原理

    会自动加载LaunchScreen是因为在Target当中,指定了Launch Screen file 它的底层实现其实把LaunchScreen上的东西,生成了一张图片,然后把这张图片设为程序的启动 ...

  4. iOS 性能优化系列

    Objective-C 高性能的循环 使用 Swift 和 Objective-C 执行 iOS 内存管理的 7 个简单技巧 @autoreleasepool-内存的分配与释放

  5. CephFS分布式文件系统

    目录 组件 基本组件 块存储 文件存储 对象存储 特点: 1.高性能: 2.高可用性: 3.高可扩展性: 4.特性丰富: 详细配置 一.准备机器 1.修改主机名 2.修改hosts文件 二.Ceph节 ...

  6. SpringBoot源码解读系列——开篇

    什么是SpringBoot? 定义可以参考官网:SpringBoot官网,其定义通俗易懂,这里就不赘述. 官网也给出了一个通用的SpringBoot工程样例,其中包含了这么几个元素: 1.pom依赖 ...

  7. 框架3.2--搭建V·P·N

    目录 部署OpenVPN 一.服务端 1.安装openvpn和证书工具 2.生成服务器配置文件 3.准备证书签发相关文件 4.准备签发证书相关变量的配置文件 5.初始化PKI生成PKI相关目录和文件 ...

  8. Netty核心原理

    Netty核心原理 1. Netty介绍 1.1 原生NIO存在的问题 NIO的类库和API使用繁杂 需要具备其他额外的技能,如java多线程编程等才能编写出高质量的NIO程序 开发工作量和难度都非常 ...

  9. 04.python语法入门--基本数据类型

    # python是一门解释型的.强类型的.动态语言​​# 一:数字类型# 1.1 整型int:记录人的年龄.等级.号码.个数# age = 18# print(type(age))​# 1.2 浮点数 ...

  10. Solution -「十二省联考2019」春节十二响

    题目 题意简述   link.   给一棵 \(n\) 个结点的有根树,点带权.把点分为若干组,并要求同组内不存在任何祖先-后代关系.最小化每组内的最大点权之和. 数据规模   \(n\le2\tim ...