惨痛教训——记一次生产变更失败记录

这两天都在忙着补录数据,基本已补录OK,甚好没对系统造成太大影响。简单梳理一下事件经过,望对各位初入Java无甚经验的程序猿能起到一个很好的警示作用。

由于该系统要做一次大变更,各种报文规范进行了一次新更改,所以就由我带着刚入职的新同事一起完成该项工作,三月份的时候,基本代码已编写完毕,剩下的调试工作,就交给了新同事(主要配合人行、财政、机构部的人一起),检测代码异常或者漏洞。

代码修改注释一定记得备份.png

这个也没什么难度,所以我就投入到了其他的工作当中,只等财政通知上线。经过机构部进行测试(反复测试N多遍),同意上线变更。上周五晚上,进行了代码变更,周一的时候,有数据接入进来,正常的做业务,待下午的时候,系统退款异常,检测数据,发现数据库有一个字段为空值。

private void parseFmt5201List(Element ele, List<EcfnPayList> ecfnPayList) throws ParseException {
        @SuppressWarnings("unchecked")
        List<Element> eles = ele.elements("Detail");
        for (Element e : eles) {
            EcfnPayList epl = new EcfnPayList();
            epl.setStatus("0");
            @SuppressWarnings("unchecked")
            List<Element> list = e.elements();
            for (Element ee : list) {
                /* 支付明细Id */
                if ("Id".equals(ee.getName())) {
                    epl.setPayListId(ee.getStringValue());
                }
                /* 财政直接支付凭证Id */
                else if ("VoucherBillId".equals(ee.getName())) {
                    epl.setVoucherBillId(ee.getStringValue());
                }
                /* 财政直接支付凭证单号 */
                else if ("VoucherBillNo".equals(ee.getName())) {
                    epl.setVoucherBillNo(ee.getStringValue());
                }
                /* 支付申请序号 (原本需要删除的字段 取报文明细单VoucherDetailNo的值)  0421 ykx*/
                else if ("VoucherDetailNo".equals(ee.getName())) {
                    // epl.setVoucherNo(ee.getStringValue());
                }
                /* 资金性质编码 */
                else if ("FundTypeCode".equals(ee.getName())) {
                    epl.setFundTypeCode(ee.getStringValue());
                }
                ………………………………………………
                /* 备注 */
                else if ("Remark".equals(ee.getName())) {
                    epl.setRemark(ee.getStringValue());
                }
                /* 实际支付日期 */
                else if ("XPayDate".equals(ee.getName())) {
                    if (ee.getStringValue() != null && ee.getStringValue().trim().length() == 8) {
                        epl.setxPayDate(ee.getStringValue());
                    }
                }
                
                /* 预留字段1 */
                else if ("Hold1".equals(ee.getName())) {
                    epl.setHold1(ee.getStringValue());
                }
                /* 预留字段2 */
                else if ("Hold2".equals(ee.getName())) {
                    epl.setHold2(ee.getStringValue());
                }
                /* 预留字段3 */
                else if ("Hold3".equals(ee.getName())) {
                    epl.setHold3(ee.getStringValue());
                }
                /* 预留字段4 */
                else if ("Hold4".equals(ee.getName())) {
                    epl.setHold4(ee.getStringValue());
                }
                              /* 新增报文 */
                else if ("VoucherDetailNo".equals(ee.getName())) {
                    epl.setVoucherDetailNo(ee.getStringValue());
                } else if ("GovExpEcoCode".equals(ee.getName())) {
                    epl.setGovExpEcoCode(ee.getStringValue());
                } else if ("GovExpEcoName".equals(ee.getName())) {
                    epl.setGovExpEcoName(ee.getStringValue());
                } else if ("DepExpEcoCode".equals(ee.getName())) {
                    epl.setDepExpEcoCode(ee.getStringValue());
                } else if ("DepExpEcoName".equals(ee.getName())) {
                    epl.setDepExpEcoName(ee.getStringValue());
                } else if ("TrackingID".equals(ee.getName())) {
                    epl.setTrackingID(ee.getStringValue());
                }
            }
            ecfnPayList.add(epl);
        }
    }

我发现数据库VoucherDetailNo这个字段值为空,而同时新增加的字段GovExpEcoCode、GovExpEcoName等等都有值,一下子就有些不淡定了,怎么可能单单略过VoucherDetailNo字段不解析呢?
先暂停读取报文自动任务,截了一段xml报文至测试环境进行本地测验,发现没什么问题,可以读取VoucherDetailNo的值,可是生产为什么没有读取该值呢?变更还是自己亲自进行的,按理说不会有错,于是截取生产class文件,进行反编译,结果就得到了上面的代码。

  /* 支付申请序号 (原本需要删除的字段 取报文明细单VoucherDetailNo的值)  0421 ykx*/
                else if ("VoucherDetailNo".equals(ee.getName())) {
                    // epl.setVoucherNo(ee.getStringValue());
                }
  /* 支付申请序号 */
// else if ("VoucherNo".equals(ee.getName())) {
                   // epl.setVoucherNo(ee.getStringValue());
                //}

这一段真是坑死我,这一段是被我注释了的(第2个代码块),不知道为什么就被同事取消改为了代码块1。后来问了一下他原因,他说:财政人员说原VoucherNo的值跟新VoucherDetailNo的值时一样的,所以他就保留了。保留也就保留了,为什么这个地方要加注释呢?
// epl.setVoucherNo(ee.getStringValue());
[这个地方加了注释导致的结果就是==无法Update明细单VoucherDetailNo的值为VoucherNo]

注释就注释了,可是我比对SVN代码与我本地代码的时候,发现一模一样,结果拿该同事本地代码与SVN代码比对,发现他的这一个地方与SVN有异同。我向他递了一个大大的问号,他说:测试过程中忘了同步了!

上线变更的class文件是该同事提供的,我检查了变更文件,不多不少,所以就上线生产了(这个地方我也有责任,该同事算个新手,没什么项目经验,但功底还是挺不错的)我就问他,你这么改VoucherDetailNo有值吗?他说有值,我看了看测试环境数据库,该字段还真有值。然后我问他,你这段代码什么时候改的?上测试环境了吗?他说记不清了。。。。估计他后面不注意改了改,自己又忘记了,所以把编译好的class文件发给了我,就导致该次变更异常。

这种情况其实也正常,在测试过程中,基本也是他负责代码的调试(主要是数据验证、报文规范检查),所以我就发了这样一段代码给他:

public static void main(String[] args) {
        Example("2");
    }

    public static void Example(String msg) {
        if (msg.equals("1")) {
             System.out.println("1");
        } else if (msg.equals("2")) {
//          System.out.println("2");
        } else if (msg.equals("888")) {
            System.out.println("888");
        } else if (msg.equals("111")) {
            System.out.println("111");
        } else if (msg.equals("2")) {
            System.out.println("Hello World");
        }else {
            System.out.println("nothing");
        }
        System.err.println("end----");
    }

我问他:你说会输出Hello World吗?他犹豫了一下还是点了点头,结果他自己跑该代码的时候,也发现自己问题所在了。

如果多个else if并列,只要第一个if条件成立,后面的else ifelse都不会执行,即使满足else if的条件也不会执行。

如果多个if,那么最后的else会执行;else与最近的if匹配,包括else ififif满足条件执行,最后的else满不满足条件,都执行。

public static void main(String[] args) {
        MoreIf("2");
    }
public static void MoreIf(String msg){
        if (msg.equals("1")) {
            System.out.println("1");
        }
        if (msg.equals("2")) {
            System.out.println("2");
        }
        if (msg.equals("111")) {
            System.out.println("111");
        }
        if (msg.equals("888")) {
            System.out.println("888");
        } else if (msg.equals("666")) {
            System.out.println("666");
        } else {
            System.err.println("Hello World");
        }
    }
执行结果.png

由于VoucherNo为空,VoucherDetailNo也为空,是没办法直接修改数据了,只好把当天的日志拿下来,写了一段程序,读取日志中的xml报文,输出sql的Update语句进行变更,可看下一篇!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,458评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,030评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,879评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,278评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,296评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,019评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,633评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,541评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,068评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,181评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,318评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,991评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,670评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,183评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,302评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,655评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,327评论 2 358

推荐阅读更多精彩内容

  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,746评论 0 10
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,507评论 1 45
  • 第一部分 打好基础 Laying the Foundation 第一章 欢迎进入软件构建的世界 Welcome t...
    白桦叶阅读 4,638评论 0 17
  • 阿里巴巴 JAVA 开发手册 1 / 32 Java 开发手册 版本号 制定团队 更新日期 备 注 1.0.0 阿...
    糖宝_阅读 7,578评论 0 5
  • 诗歌里最常见的串联词是我。我是本源、是当下、是未来,既是攻击者,又是被攻击的受体。年轻的诗人一般都有路怒症...
    小尘199211阅读 202评论 1 0