一、银行业务与 COBOL 代码简介

银行业务千头万绪,而银行核心系统就像是银行运转的“心脏”,承载着诸如账户管理、存款取款、转账汇款等核心交易。在很多老牌银行里,COBOL(Common Business - Oriented Language)一直是核心系统开发和维护的主力编程语言。COBOL 诞生于上个世纪 50 年代末,以其强大的数据处理能力和稳定性,在金融领域有着深厚的历史积淀。

1.1 COBOL 代码特点

COBOL 代码的风格比较“冗长”,有点像我们日常写的文章,它的语法结构清晰,每个部分都有很明确的标识。比如下面这个简单的 COBOL 程序示例,用于计算两个数的和:

IDENTIFICATION DIVISION.
PROGRAM-ID. SUM-CALC.
* 定义程序标识部分,程序名为 SUM-CALC
PROCEDURE DIVISION.
    DISPLAY '请输入第一个数:'.
    ACCEPT NUM1.
    DISPLAY '请输入第二个数:'.
    ACCEPT NUM2.
    COMPUTE RESULT = NUM1 + NUM2.
    DISPLAY '两数之和为: ' RESULT.
    STOP RUN.

在这个示例中,IDENTIFICATION DIVISION 是用来标识程序信息的,PROCEDURE DIVISION 是程序的执行部分。这种明确的结构可以让程序员很清楚地知道代码各个部分的作用,但是也导致代码量相对较大。

1.2 银行业务逻辑复杂性

银行的业务逻辑复杂度很高,比如一个普通的转账业务,它不仅仅是从一个账户扣款,向另一个账户存钱这么简单。还需要考虑账户余额是否充足、是否存在冻结情况、转账额度是否超出限制等众多因素。假如用户 A 向用户 B 转账 1000 元,程序需要先检查用户 A 的账户余额是否大于等于 1000 元,同时还要查看用户 A 的账户是否被冻结。如果一切正常,从用户 A 账户扣除 1000 元,再向用户 B 账户增加 1000 元,同时还要记录转账记录,更新相关的会计科目等。这一系列的操作都需要在 COBOL 代码中准确无误地实现。

二、常见业务逻辑异常场景

2.1 账户余额计算错误

这是一个比较常见的问题。比如,在进行存款或取款操作后,账户余额没有正确更新。假设一个 COBOL 程序处理存款业务,用户存入 500 元,原账户余额为 1000 元,正常情况下新的账户余额应该是 1500 元。

IDENTIFICATION DIVISION.
PROGRAM-ID. DEPOSIT-OP.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 ACCOUNT-BALANCE PIC 9(5). VALUE 1000.  * 初始账户余额
01 DEPOSIT-AMOUNT PIC 9(3). VALUE 500.    * 存款金额
01 NEW-BALANCE PIC 9(5).
PROCEDURE DIVISION.
    COMPUTE NEW-BALANCE = ACCOUNT-BALANCE + DEPOSIT-AMOUNT.
    DISPLAY '新的账户余额: ' NEW-BALANCE.
    STOP RUN.

如果在计算过程中出现问题,比如代码中出现了错误的变量引用或者计算逻辑错误,就会导致新的账户余额计算错误。

2.2 交易重复处理

在银行系统中,交易重复处理会带来严重的后果。比如用户发起了一次转账请求,由于网络问题或者系统故障,请求被多次提交,而程序没有进行有效的判断,就会重复执行转账操作。例如下面模拟一个简单的转账程序:

IDENTIFICATION DIVISION.
PROGRAM-ID. TRANSFER-OP.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 FROM-ACCOUNT PIC 9(5) VALUE 1000.    * 转出账户余额
01 TO-ACCOUNT PIC 9(5) VALUE 2000.      * 转入账户余额
01 TRANSFER-AMOUNT PIC 9(3) VALUE 100.  * 转账金额
PROCEDURE DIVISION.
    IF FROM-ACCOUNT >= TRANSFER-AMOUNT
        COMPUTE FROM-ACCOUNT = FROM-ACCOUNT - TRANSFER-AMOUNT
        COMPUTE TO-ACCOUNT = TO-ACCOUNT + TRANSFER-AMOUNT
    END-IF.
    DISPLAY '转出账户余额: ' FROM-ACCOUNT.
    DISPLAY '转入账户余额: ' TO-ACCOUNT.
    STOP RUN.

如果这个程序在没有对交易进行唯一标识和判断的情况下,重复执行,就会导致转出账户多次扣款,转入账户多次收款。

2.3 权限验证缺失或错误

银行的不同业务需要不同的权限,比如大额转账需要更高的权限验证。如果程序中权限验证缺失或者错误,就会造成安全隐患。例如一个普通用户可以绕过权限验证进行大额转账操作。以下是一个简单的权限验证示例:

IDENTIFICATION DIVISION.
PROGRAM-ID. PERMISSION-CHECK.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 USER-LEVEL PIC X(1). VALUE 'L'.  * 用户级别,L 表示普通用户
01 TRANSFER-AMOUNT PIC 9(5) VALUE 500000. * 转账金额
01 ALLOWED-AMOUNT PIC 9(5) VALUE 100000.  * 普通用户允许的转账金额
PROCEDURE DIVISION.
    IF USER-LEVEL = 'L' AND TRANSFER-AMOUNT > ALLOWED-AMOUNT
        DISPLAY '权限不足,无法进行此操作.'
    ELSE
        DISPLAY '可以进行转账操作.'
    END-IF.
    STOP RUN.

如果权限验证部分的代码逻辑错误,比如判断条件写错,就会导致权限验证失效。

三、业务逻辑异常的定位方法

3.1 日志分析

日志是排查问题的重要依据。银行系统会记录每一个交易的详细信息,包括交易时间、交易类型、涉及的账户、交易金额等。通过分析日志,可以发现异常交易的特征。比如在日志中发现某一个账户在短时间内有多次相同金额的转账记录,这很可能是交易重复处理的问题。以下是一个简单的日志记录示例:

IDENTIFICATION DIVISION.
PROGRAM-ID. LOG-RECORD.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 TRANSACTION-TIME PIC X(14) VALUE '20240101120000'.  * 交易时间
01 TRANSACTION-TYPE PIC X(5) VALUE 'TRANS'.            * 交易类型,转账
01 FROM-ACCOUNT PIC 9(5) VALUE 1000.                   * 转出账户
01 TO-ACCOUNT PIC 9(5) VALUE 2000.                     * 转入账户
01 TRANSFER-AMOUNT PIC 9(3) VALUE 100.                 * 转账金额
01 LOG-RECORD PIC X(33).
PROCEDURE DIVISION.
    MOVE TRANSACTION-TIME TO LOG-RECORD(1:14).
    MOVE TRANSACTION-TYPE TO LOG-RECORD(15:5).
    MOVE FROM-ACCOUNT TO LOG-RECORD(20:5).
    MOVE TO-ACCOUNT TO LOG-RECORD(25:5).
    MOVE TRANSFER-AMOUNT TO LOG-RECORD(30:3).
    WRITE LOG-RECORD.
    STOP RUN.

通过查看日志文件,我们可以根据这些记录来分析交易是否正常。

3.2 调试工具使用

COBOL 有自己的调试工具,可以单步执行代码,查看变量的值。比如在调试账户余额计算错误的问题时,我们可以在计算余额的代码行设置断点,然后逐行执行代码,查看每个变量的值是否正确。以下是一个简单的调试思路示例:

IDENTIFICATION DIVISION.
PROGRAM-ID. DEBUG-EXAMPLE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 ACCOUNT-BALANCE PIC 9(5) VALUE 1000.
01 DEPOSIT-AMOUNT PIC 9(3) VALUE 500.
01 NEW-BALANCE PIC 9(5).
PROCEDURE DIVISION.
    * 假设这里设置断点
    COMPUTE NEW-BALANCE = ACCOUNT-BALANCE + DEPOSIT-AMOUNT.
    DISPLAY '新的账户余额: ' NEW-BALANCE.
    STOP RUN.

在调试工具中,当程序执行到断点处时,我们可以查看 ACCOUNT-BALANCE、DEPOSIT-AMOUNT 和 NEW-BALANCE 的值,判断计算是否正确。

3.3 数据比对

我们可以将系统中的数据与其他可靠数据源进行比对。比如将银行系统中的账户余额与用户提供的对账单进行比对,如果发现有差异,就说明可能存在业务逻辑异常。例如我们有一个简单的程序来读取账户余额并输出:

IDENTIFICATION DIVISION.
PROGRAM-ID. BALANCE-READ.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 ACCOUNT-BALANCE PIC 9(5).
01 FILE-RECORD PIC 9(5).
01 INPUT-FILE.
    FD  INPUT-FILE
        LABEL RECORDS ARE STANDARD
        DATA RECORD IS FILE-RECORD.
PROCEDURE DIVISION.
    OPEN INPUT INPUT-FILE.
    READ INPUT-FILE INTO ACCOUNT-BALANCE.
    DISPLAY '账户余额: ' ACCOUNT-BALANCE.
    CLOSE INPUT-FILE.
    STOP RUN.

我们可以将这个输出的余额与用户对账单上的余额进行比较,找出差异并排查问题。

四、业务逻辑异常的修复思路

4.1 账户余额计算错误修复

如果发现账户余额计算错误,首先要检查计算逻辑是否正确,其次检查变量的引用是否正确。比如在上面的存款程序中,如果新的账户余额计算错误,我们可以这样修复:

IDENTIFICATION DIVISION.
PROGRAM-ID. DEPOSIT-OP-FIX.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 ACCOUNT-BALANCE PIC 9(5). VALUE 1000.
01 DEPOSIT-AMOUNT PIC 9(3). VALUE 500.
01 NEW-BALANCE PIC 9(5).
PROCEDURE DIVISION.
    * 确保计算逻辑和变量引用正确
    COMPUTE NEW-BALANCE = ACCOUNT-BALANCE + DEPOSIT-AMOUNT.
    MOVE NEW-BALANCE TO ACCOUNT-BALANCE.  * 更新账户余额
    DISPLAY '新的账户余额: ' ACCOUNT-BALANCE.
    STOP RUN.

在这个修复后的代码中,我们确保了计算逻辑的正确性,并且更新了账户余额的变量。

4.2 交易重复处理修复

为了避免交易重复处理,我们可以为每一个交易生成一个唯一的标识,在处理交易时先检查这个标识是否已经存在。以下是一个简单的示例:

IDENTIFICATION DIVISION.
PROGRAM-ID. TRANSFER-OP-FIX.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 FROM-ACCOUNT PIC 9(5) VALUE 1000.
01 TO-ACCOUNT PIC 9(5) VALUE 2000.
01 TRANSFER-AMOUNT PIC 9(3) VALUE 100.
01 TRANSACTION-ID PIC X(10) VALUE 'T000000001'.
01 TRANSACTION-LOG.
    05 LOG-ID PIC X(10).
    05 NEXT-LOG.
        REDEFINES TRANSACTION-LOG.
        05 LOG-EMPTY PIC X(1).
PROCEDURE DIVISION.
    SEARCH TRANSACTION-LOG
        AT END
            IF FROM-ACCOUNT >= TRANSFER-AMOUNT
                COMPUTE FROM-ACCOUNT = FROM-ACCOUNT - TRANSFER-AMOUNT
                COMPUTE TO-ACCOUNT = TO-ACCOUNT + TRANSFER-AMOUNT
                MOVE TRANSACTION-ID TO LOG-ID
            END-IF
    END-SEARCH.
    DISPLAY '转出账户余额: ' FROM-ACCOUNT.
    DISPLAY '转入账户余额: ' TO-ACCOUNT.
    STOP RUN.

在这个示例中,我们通过 SEARCH 语句检查交易标识是否已经存在,如果不存在则进行转账操作并记录交易标识。

4.3 权限验证缺失或错误修复

如果权限验证出现问题,我们要检查验证逻辑是否完善,判断条件是否正确。比如在上面的权限验证示例中,如果权限验证失效,我们可以这样修复:

IDENTIFICATION DIVISION.
PROGRAM-ID. PERMISSION-CHECK-FIX.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 USER-LEVEL PIC X(1). VALUE 'L'.
01 TRANSFER-AMOUNT PIC 9(5) VALUE 500000.
01 ALLOWED-AMOUNT PIC 9(5) VALUE 100000.
PROCEDURE DIVISION.
    * 确保判断条件正确
    IF USER-LEVEL = 'L' AND TRANSFER-AMOUNT > ALLOWED-AMOUNT
        DISPLAY '权限不足,无法进行此操作.'
        STOP RUN
    END-IF.
    DISPLAY '可以进行转账操作.'
    STOP RUN.

在修复后的代码中,我们确保了判断条件的正确性,并且在权限不足时及时终止程序。

五、应用场景与技术优缺点

5.1 应用场景

银行核心系统的 COBOL 代码排查主要应用于银行日常业务运营和系统升级过程中。在日常运营中,会不断有新的交易产生,可能会出现各种业务逻辑异常,需要及时排查和修复。在系统升级时,新的功能添加或者代码重构可能会引入新的问题,也需要对代码进行全面的排查。

5.2 技术优缺点

优点

  • 稳定性强:COBOL 经过了几十年的发展和实践检验,其代码的稳定性很高,能够处理大量的金融交易。
  • 数据处理能力强:COBOL 对数据的操作和处理非常方便,适合银行复杂的业务逻辑。
  • 历史沉淀丰富:很多银行积累了大量的 COBOL 代码和运维经验,技术人员对其比较熟悉。

缺点

  • 代码冗长:COBOL 代码相对来说比较冗长,增加了开发和维护的难度。
  • 学习成本高:对于新的程序员来说,COBOL 的语法和编程风格与现代编程语言有很大差异,学习成本较高。
  • 缺乏现代特性:COBOL 缺乏一些现代编程语言的特性,如面向对象编程、函数式编程等。

六、注意事项

在进行银行核心系统 COBOL 代码排查时,需要注意以下几点:

  • 数据安全:银行的数据涉及到用户的财产安全,在排查过程中要确保数据的保密性和完整性,避免数据泄露。
  • 测试环境使用:在修复代码问题之前,要先在测试环境中进行充分的测试,避免在生产环境中出现新的问题。
  • 版本控制:使用版本控制系统对 COBOL 代码进行管理,方便记录代码的变更历史,避免误操作导致代码丢失或错误。

七、文章总结

银行核心系统的稳定运行对于银行的业务至关重要,而 COBOL 代码作为很多银行核心系统的基础,对其进行业务逻辑异常的排查和修复是一项必要的工作。我们介绍了常见的业务逻辑异常场景,包括账户余额计算错误、交易重复处理、权限验证缺失或错误等。同时,还分享了定位异常的方法,如日志分析、调试工具使用和数据比对,以及相应的修复思路。此外,我们还分析了 COBOL 代码排查的应用场景、技术优缺点和注意事项。希望通过这篇文章,能帮助开发者更好地应对银行核心系统 COBOL 代码中的业务逻辑异常问题。