Python Flask + SAP RFC实现简单系统集成使用 BAPI_PR_CREATE 创建采购申请


前言

         SAP系统与其他系统集成是我们最常见的业务,例如与OA系统集成,与MES系统集成,与WES系统集成。所以系统集成这块我们是必须要会的,本文简单介绍如何从技术层面实现sap系统与外部系统集成的方法,此方法仅仅只对系统集成层面有一个较浅的理解不适合实际业务,在实际业务中我们更多使用的是Web service 、PI、Odata、CPI、REST、等等技术。


一、案例介绍/实现效果

         我们使用 Python 作为后端服务前端可以使用某个 UI 框架或者前端框架,这样相当于我们的一个简单的系统,SAP 端我们创建一个 RFC 函数,然后使用 Python 的 Pyrfc 包就可以访问我们 SAP 端创建的函数,这样就实现了通讯,我们数据格式使用 json 串的形式来流转,数据流转方向为,前端界面获取数据给到 Python 服务端,Python 服务端拿到数据并调用 SAP RFC 携带数据,SAP 端拿到数据做业务处理返回数据,实现简单的系统集成。Python 端如何访问 SAP 的 RFC 笔者有一篇文章有介绍可以去翻看一下。python连接sap并调用 SAP RFC接口
在这里插入图片描述

二、学会SAP内使用BAPI

          首先我们要会在 SAP 中使用 BAPI ,这样我们在用外部系统调用的时候才会思路清楚。要一步一个脚印,想要在SAP中熟练的使用 BAPI ,先要会使用 BAPI 对应的 SAP 前端执行的标准程序,例如 BAPI_PR_CREATE 对应的 SAP 前端程序就是 ME51N 创建采购申请。会使用前端标准程序之后,才能在后台使用 SE37 执行 BAPI 创建业务的时候思路清楚,在后台会使用 BAPI 之后我们才能使用 ABAP 代码调用 BAPI 的时候能思路清楚。就是这样一环套一环步步稳扎。

            a.BAPI的介绍

                      这里我对 BAPI 简单的介绍一下,详细的 BAPI 概念以及由来大家还是去网上搜其他的吧。
                       1.为何要有BAPI
                            简单点说 BAPI 就是一个基于数据库表的处理,说白了就是 BAPI 可以对我们的数据库的数据进行增加修改等等,和我们操作执行标准程序会产生一样的结果,那么为什么我们不用代码直接对数据库进行增删改,而是要通过 BAPI 呢,实际从技术上应该可以实现代码直接对数据库的增删改,但是 SAP 是不允许我们这样做的,因为 SAP 这么庞大的系统你的每个业务每个操作它可不仅仅只对2三张表去增删改查的,比如你增加物料的这么一个需求,你肯定知道会在 MARA 和 MAKT ,这两个表产生数据,那么你知道还会在日志表产生数据吗?这个日志表又是哪个你知道吗?所以你无论哪个业务你都想不到所有要操作的表,所以 SAP 就帮我们做了这个事情,你只需要给 BAPI 对应的数据它就会帮你创建物料,会帮你执行操作所有的表,这就是为什么要有 BAPI。

                       2.对BAPI的理解
                            BAPI 本身就是一个函数,是业务应用编程接口,它实际上是一种特殊的 RFC , RFC 其实也是一个函数它和一般函数的区别就是可以被外部系统调用,全称叫 Remote(远程)Function Modules(函数组件) ,所有的 BAPI 存储于对象库 BOR 中。

RFC与BAPI的区别RFCBAPI
定义RFC是一种函数,用于外部程序调用。BAPI是SAP做好的实现业务操作的RFC。
特点面向过程,调用简单直接。面向对象,有属性、方法和事件。
用途主要用于系统间通讯,允许远程调用。主要用于处理主要业务流程,例如创建各模块主数据。

                       3.业务对象有四层
                            说到 BAPI 我们可以简单理解一下业务对象业务对象有四层

各个层解释
访问层 (Access)支持对象业务对象数据访问的具体技术,RFC/JCO等
接口层 (Interface)提供该业务对象类型的外部访问接口,包括属性、方法、BAPI方法、事件等。
整合层 (Integrity)包含对象的业务逻辑
最内层 (Kernel)SAP业务对象内核层,包含对象本身的内部数据结构及标准设定。

业务对象
                       4.如何查找BAPI
                            后面有时间了笔者会出另外一篇文章SAP查找BAPI到时候附上链接,一般常用的BAPI我们在网上都搜的到。自己要找的话基本是 先根据 swo3 找对应的业务方法 ,swo1 根据 swo3 中的方法找BAPI 对应的方法事务码。 bapi(事务码) 成为 BAPI 浏览器 BAPI 是把 swo3 中的方法全部展示罗列出来了 swo3 是以类的角度去看 展示属性 方法 事件 等等 但是我们常用的只有方法所以 BAPI 把这些方法都整理出来了。

            b.创建采购申请的BAPI

                       1.BAPI_PR_CREATE
                            创建采购申请的 BAPIBAPI_PR_CREATE 我们要想简单掌握这个 BAPI 肯定得先会用标准程序创建采购申请,让我们对创建采购申请业务有个初始理解,才能在脱离屏幕界面的情况下熟练操作 BAPI,因为你如果给你一个界面你都不知道要输入那些值更何况脱离界面呢?

                       2.使用 ME51N 创建采购申请
                            在我们创建采购申请的时候,下面红色框中的都是必填的,其他需要的数据都是回车可以自动带出来的,我们使用 BAPI 的时候也一样,回车带出来的数据可以不用填写。我们可以先尝试着使用标准程序创建一张采购订单,然后记录下这些数据,物料、工厂、交货日期、工厂、方便我们后续使用。
ME51N

                       3.查看 BAPI_PR_CREATE 的参数
                            我们可以使用 SE37 来查看创建采购申请的 BAPI BAPI_PR_CREATE ,它就是一个函数嘛,我们只用给它传对应的参数调用即可,那么传哪些参数这个就需要我们在导入 和 表传入传出中去查看了,一般行项目就是表传入传出,抬头的话一般在导入中,我们使用这个BAPI的话肯定是要传入抬头和行项目的,这两个是最基本的数据。
BAPI_PR_CREATE
                       4.BAPI_PR_CREATE 的参数含义
                            参数中对应的字段不一定就和屏幕中 F1 查看的字段一样,我们要根据描述去找字段,对应赋值。可以一边找一边做记录方便我们后续的赋值 可以简单记录如下。其中表传入 PRITEM 就是我们 me51 中行项目的数据,导入 PRHEADER 就是我们 me51n 抬头数据,但是我们还会发现有两个参数叫 PRITEMXPRHEADERX,它们长的很像就多了一个 X,数据结构是一模一样的但是可以发现 PRITEMXPRHEADERX 的字段都是 char1,其实这两个表中的这些字段就是代表我们这个输入框到底有没有输入值,如果你传参是时候 PRITEM-PLANT 工厂输入了但是 PRITEMX-PLANT 确没有打X,那么这个 BAPI 会认为你工厂就是没输入,尽管你传参了,这一点是我们使用这个 BAPI 要注意的。还有就是要输入行项目编号,在标准程序中我们回车会自动填写但是使用 BAPI 你必须传参。
基本数据

            c.使用SE37执行BAPI

                       1.如何填写数据
                            数据填写的时候需要我们去找对应的字段,耐心的去找填写,这里需要注意日期格式并不是YYYYMMDD,但是代码中传参的时候要是YYYYMMDD。根据下图的方法可以快速找到对应字段
se37执行BAPI
                       2.数据填写技巧
                            看下图中标注的文字即可
跳到对应字段
                       3.完整填写效果
                            以下是输入完整的效果 以下是输入完整的效果 以下是输入完整的效果
数据填写
                       4.COMMIT 和 ROLLBACK 机制
                            当我们数据填写完整之后可以执行查看结果,我们可以看到下图返回结果是创建了采购申请 1000008178,但此时这个采购申请并没有去创建,我们可以把这一步理解成我们标准程序中的检查,SAP 它也是引用了 COMMITROLLBACK 的原理的 这两个具体是什么大家可以去百度,当我们下面这步执行完之后其实还需要 COMMIT 提交一下,才能创建成功,但是并不是所有的 BAPI 都有 COMMIT 这个机制。
采购申请被创建
                       5.COMMIT 的 BAPI
                            填写数据执行并提交,这里需要在用到一个函数叫 BAPI_TRANSACTION_COMMIT 根据下图操作完之后 再次执行上述操作,填写数据、点击执行、查看返回结果、点击返回、执行提交函数、在查看返回结果的时候我们要记录下这个采购申请编号,提交完成后去标准程序查看是否创建成功。BAPI_TRANSACTION_COMMITRETURN 表没有返回结果是正常的。
序列执行
                            执行的时候 BAPI_TRANSACTION_COMMIT 中的 WAIT 参数需要给上 X
执行提交函数
                       6.查看结果
                            现在我们已经用函数后台创建了一张采购申请了,我们对这个函数也有了一定了解,也了解它需要传入那些参数,接下来我们将使用代码整合数据调用函数创建采购申请。
采购申请创建成功

            d.SE38使用ABAP代码执行BAPI

                       我们先创建一个报表程序 执行此程序来实现用ABAP代码执行创建采购申请的BAPI,创建程序的步骤省略,这里我直接附上源码。
                       1.源码
                            代码实现的详细步骤大家可以看其中的注释,注释也写的很清楚了。完成用代码可以创建采购申请后我们可以创建一个RFC函数,将我们创建的这个函数和代码创建的逻辑结合起来就可以实现一个创建采购申请的RFC来被我们外部系统调用即可完集成,当然我们其实也可以直接在外部系统调用 创建采购申请的 BAPI 因为它本身也是RFC,只不过我们再包装一层可以更灵活。

REPORT z_test7.
DATA:
  return    LIKE TABLE OF bapiret2, "创建采购申请 返回消息
  return1   LIKE  bapiret2, "提交 返回消息
  prheader  LIKE bapimereqheader,  "抬头
  prheaderx LIKE bapimereqheaderx, "抬头中的字段是否输入
  pritem    LIKE TABLE OF bapimereqitemimp WITH HEADER LINE, "行项目
  pritemx   LIKE TABLE OF bapimereqitemx WITH HEADER LINE.   "行项目中的字段是否输入

prheader-pr_type = 'NB'.
prheaderx-pr_type = 'X'.
* 行项目数据内表赋值 一共添加两行
pritem-preq_item     = '10'.
pritem-material     = '000300000000000001'."记得补前导零
pritem-quantity     = '50'.
pritem-plant        = '1201'.
pritem-deliv_date   = '20231130'.
APPEND pritem."带表头行内表直接 append就可以
CLEAR  pritem."清空表头行 养成习惯
pritem-preq_item     = '20'.
pritem-material     = '000310006001000007'."记得补前导零
pritem-quantity     = '55'.
pritem-plant        = '1201'.
pritem-deliv_date   = '20231130'.
APPEND pritem.
* 行项对应值是否输入内表赋值 要和上面数据行数对应起来
pritemx-preq_item     = '10'.
pritemx-material     = 'X'.
pritemx-quantity     = 'X'.
pritemx-plant        = 'X'.
pritemx-deliv_date   = 'X'.
APPEND pritemx."全部打X 不清空表头行下行接着用
pritemx-preq_item     = '20'."第二行只用改掉行项目编号即可
APPEND pritemx.

CALL FUNCTION 'BAPI_PR_CREATE'"调用创建采购申请的BAPI传入对应的参数
  EXPORTING "导入
    prheader  = prheader
    prheaderx = prheaderx
*   TESTRUN   =
* IMPORTING "导出 用不上
*   NUMBER    =
*   PRHEADEREXP                  =
  TABLES "表传入传出
    return    = return
    pritem    = pritem
    pritemx   = pritemx.

CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
  EXPORTING
 *这个参数可以让系统等待更新任务完成后再返回结果。如果没有设置这个参数,系统会异步执行更新任务
    wait   = 'X' 
  IMPORTING
    return = return1.
*打印返回结果   
LOOP AT return INTO DATA(ls_return).
  WRITE ls_return-message.
ENDLOOP.

            e.使用SE37创建RFC

                       1.se37创建RFC
                            我们创建函数的时候一定要勾选 RFC 远程启用模块不然外部系统是调用不到的
创建RFC函数

                       2.RFC参数的设定
                            创建这个 RFC 其实最主要是就是参数得我们自己去设置,只要保证外部系统传参的时候我们RFC定义的这些参数可以接收到即可。 RFC必须勾选值传递,它不像我们在SAP中可以 传递内存地址。因为外部系统的内存地址跟我SAP没啥关系啊。所以它只能是明确的值传递。值传递在一个程序内部本身的话就是变量传递过来后我再操作就不会影响原来数据。大家也可以了解一下设拷贝浅拷贝
定义参数
                       3.RFC源码
                            这个RFC的源码非常简单 里面一些变量还有赋值操作大家具体看注释即可。

FUNCTION ztest_lk02.
*"----------------------------------------------------------------------
*"*"本地接口:
*"  IMPORTING
*"     VALUE(INPUT_HEAD) TYPE  ZTEST_LK02_HEAD
*"     VALUE(INPUT_HEADX) TYPE  ZTEST_LK02_HEADX
*"  TABLES
*"      INPUT_PRITEM STRUCTURE  ZTEST_LK02_ITEM
*"      INPUT_PRITEMX STRUCTURE  ZTEST_LK02_ITEMX
*"      RETURN_RES STRUCTURE  BAPIRET2
*"----------------------------------------------------------------------
  DATA:
    return    LIKE TABLE OF bapiret2 WITH HEADER LINE, "调用创建po返回消息
    return1   TYPE  bapiret2, "确认提交返回消息
    prheader  LIKE bapimereqheader,  "抬头
    prheaderx LIKE bapimereqheaderx, "抬头中的字段是否输入
    pritem    LIKE TABLE OF bapimereqitemimp WITH HEADER LINE, "行项目
    pritemx   LIKE TABLE OF bapimereqitemx WITH HEADER LINE.   "行项目中的字段是否输入

  MOVE-CORRESPONDING input_pritem[] TO pritem[]."将外部系统传入的数据赋值到pritem行项目内表
  MOVE-CORRESPONDING input_pritemx[] TO pritemX[].
  MOVE-CORRESPONDING INPUT_head TO prheader."将外部系统传入的数据赋值到prheader结构
  MOVE-CORRESPONDING INPUT_headX TO prheaderx.

  CALL FUNCTION 'BAPI_PR_CREATE'
    EXPORTING
      prheader  = prheader
      prheaderx = prheaderx
*     TESTRUN   =
* IMPORTING
*     NUMBER    =
*     PRHEADEREXP                  =
    TABLES
      return    = RETURN_RES
      pritem    = pritem
      pritemx   = pritemx.

  CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
    EXPORTING
      wait   = 'X'
    IMPORTING
      return = return1."这个没有返回值正常,

ENDFUNCTION.

三、系统集成调用BAPI

           这里我们首先不搭建 WEB 服务,先只用 Python 来直接调用我们刚才创建的 RFC 。因为这样一步一步来 可以很快的排除错误。也是从易到难由浅入深 让人好接受一点。

            a.Python调用创建采购申请的RFC

                       1.Python 的 Pyrfc 调用 RFC
                            我们先可以跳过前端页面直接写死数据来调用 RFC 这样一步一步来可以很快排查出错误。等我们这样写死数据可以成功创建pr之后我们再去和前端结合。如果对下面的 Python 如何调用了 RFC 不清楚的去看笔者的这篇文章即可。Python连接sap并调用 SAP RFC接口

import pyrfc
ASHOST = 'xx.xx.xxx.xx'
CLIENT = '110'
SYSNR = '00'
USER = 'TEST100'
PASSWD = 'xxxxxx'
lang = 'ZH'  # 必须设置登录语言 不然会报错未维护EN语言的物料描述
conn = pyrfc.Connection(ashost=ASHOST, sysnr=SYSNR, client=CLIENT, user=USER, passwd=PASSWD, lang=lang)
# (同 me51n 行项目)  以下参数都必须注意大小写
INPUT_PRITEM = [
    {
        'PREQ_ITEM': "10",
        'MATERIAL': "000300000000000001",
        'QUANTITY': "4",
        'PLANT': "1201",
        'DELIV_DATE': "20231130",
    },
    {
        'PREQ_ITEM': "20",
        'MATERIAL': "000310006001000007",
        'QUANTITY': "4",
        'PLANT': "1201",
        'DELIV_DATE': "20231230",
    },
]

INPUT_PRITEMX = [
    {
        'PREQ_ITEM': "10",
        'MATERIAL': "X",
        'QUANTITY': "X",
        'PLANT': "X",
        'DELIV_DATE': "X",
    },
    {
        'PREQ_ITEM': "20",
        'MATERIAL': "X",
        'QUANTITY': "X",
        'PLANT': "X",
        'DELIV_DATE': "X",
    },
]
# (同 me51n 抬头)
INPUT_HEAD = {
    'PR_TYPE': "NB"
}
INPUT_HEADX = {
    'PR_TYPE': "X"
}
result = conn.call("ZTEST_LK02", INPUT_PRITEM=INPUT_PRITEM, INPUT_PRITEMX=INPUT_PRITEMX, INPUT_HEAD=INPUT_HEAD,
                   INPUT_HEADX=INPUT_HEADX)
print(result)

            b.Flask项目的搭建

                       1.Pycharm创建Flask项目
                            笔者这里使用的是 Pycharm 专业 操作步骤,如果你的 Pycharm 是社区版可以按照 Flask 的项目结构手动创建目录也可以。 Pycharm 专业办如果自己懒得搞直接去某宝搜就可以几块钱吧。
创建Flask项目

                       2.运行Flask项目
                            确保我们的Flask项目可以成功运行再进行后续步骤。
在这里插入图片描述

            c.前端界面实现

                       前端界面的实现我这里也不多解释了感兴趣的朋友可以去网上搜搜看看,也不难主要是用了JQ。我这里就直接上源码了。如果有问题可以评论私信我哦。
                       1.前端界面源码
                            这个代码要写到对应路由访问的 html 文件。也就是我项目中的 test.html

<!DOCTYPE html>
<html>
<head>
    <title>HTML Form to JSON</title>
    <script src="../static/jquery.js"></script>
</head>
<body>
        <h1 style="margin: 0 auto">创建采购申请</h1>
		<button type="button" id="addRow">+</button>
		<button type="button" id="removeRow">-</button>
        <input type="button" value="创建" onclick="createpr()">
        <select name="pr_type" id="pr_type">
            <option value="FO">FO 框架申请</option>
            <option value="NB" selected="selected">NB 采购申请</option>
            <option value="ZFB">ZFB 非生产物料采购申请</option>
            <option value="ZNB">ZNB 手工采购申请</option>
        </select>
        <table id="myTable">
            <tr>
                <td><input type="text" name="field1" placeholder="项目" value="10"></td>
                <td><input type="text" name="field2" placeholder="物料编码" value="000300000000000001"></td>
                <td><input type="text" name="field3" placeholder="数量" value="50"></td>
                <td><input type="text" name="field4" placeholder="工厂" value="1201"></td>
                <td><input type="text" name="field5" placeholder="日期" value="20231130"></td>
            </tr>
        </table>
        <marquee>会在110创建采购申请,样式数据必输待优化</marquee>
    <script>
        $(document).ready(function () {
            $("#addRow").click(function () {
                var html = '';
                html += '<tr>';
                html += '<td><input type="text" name="field1" placeholder="项目"></td>';
                html += '<td><input type="text" name="field2" placeholder="物料编码"></td>';
                html += '<td><input type="text" name="field3" placeholder="数量"></td>';
                html += '<td><input type="text" name="field4" placeholder="工厂"></td>';
                html += '<td><input type="text" name="field5" placeholder="日期"></td>';
                html += '</tr>';
                $('#myTable').append(html);
            });

            $("#removeRow").click(function () {
                $('#myTable tr:last').remove();
            });
        });
        function createpr() {
                var tableData = [];
                $('#myTable tr').each(function(row, tr){
                    var subData = {
                        'PREQ_ITEM'  : $(tr).find('input[name=field1]').val(),
                        'MATERIAL'   : $(tr).find('input[name=field2]').val(),
                        'QUANTITY'   : $(tr).find('input[name=field3]').val(),
                        'PLANT'      : $(tr).find('input[name=field4]').val(),
                        'DELIV_DATE' : $(tr).find('input[name=field5]').val()
                    };
                    tableData.push(subData);
                });
                 var INPUT_HEAD = {
                     'PR_TYPE':$('#pr_type').val()
                 }

               window.location.href = '/create_pr?prdata='+JSON.stringify(tableData)+'&prtype='+JSON.stringify(INPUT_HEAD)
            }
    </script>
</body>
</html>

            d.后端逻辑实现

                       这边我直接给app.py的源码有问题评论或者私信哦
                       1.app.py源码
                            具体实现过程看注释即可,下面运用了三元操作符,也可以用其他的写法,三元操作符可能可读性不高但是可以提高代码的简洁性。

from flask import Flask, render_template, request, json
import pyrfc

app = Flask(__name__)

ASHOST = 'xx.xx.xxx.xx'
CLIENT = '110'
SYSNR = '00'
USER = 'TEST100'
PASSWD = 'xxxxxx'
lang = 'ZH'  # 必须设置登录语言 不然会报错未维护EN语言的物料描述
conn = pyrfc.Connection(ashost=ASHOST, sysnr=SYSNR, client=CLIENT, user=USER, passwd=PASSWD, lang=lang)


@app.route('/')#运行Flask后根路由直接返回的是我们的前端页面 
def hello_world():
    return render_template('test.html')


@app.route('/create_pr')#当我们点击创建采购申请按钮后请求会发到这个路由
def create_pr():
    data = request.args.get('prdata')  # 获取前端路由携带的json串(行项目数据)
    prtype = request.args.get('prtype')  # 获取前端路由携带的json串(抬头数据)
    INPUT_PRITEM = json.loads(data)  # 将json串转为列表字典形式(行项目数据)
    INPUT_HEAD = json.loads(prtype)  # 将json串转为列表字典形式(抬头数据)
    INPUT_PRITEMX = []

    for i in INPUT_PRITEM:#根据前端输入的情况来制作PRITEMX表的数据
        INPUT_PRITEMX_item = {
            'PREQ_ITEM': i['PREQ_ITEM'],
            'MATERIAL': 'X' if i['MATERIAL'] != '' else '',
            'QUANTITY': "X" if i['QUANTITY'] != '' else '',
            'PLANT': "X" if i['PLANT'] != '' else '',
            'DELIV_DATE': "X" if i['DELIV_DATE'] != '' else '',
        }
        INPUT_PRITEMX.append(INPUT_PRITEMX_item)
    INPUT_HEADX = {
        'PR_TYPE': "X"
    }
    print(INPUT_PRITEM)
    print(INPUT_PRITEMX)
    result = conn.call("ZTEST_LK02", INPUT_PRITEM=INPUT_PRITEM, INPUT_PRITEMX=INPUT_PRITEMX, INPUT_HEAD=INPUT_HEAD,
                       INPUT_HEADX=INPUT_HEADX)
    res = ''
    for i in result['RETURN_RES']:
        res += i['MESSAGE']

    return res

if __name__ == '__main__':
    app.run()

                       2.项目结构展示
                            注意图中标注的红框和文字即可
项目结构

            e.最终效果展示

                       我项目运行后是使用手机局域网访问的,Flask 局域网如何访问可以看笔者另外一篇文章Flask局域网访问
                       1.手机访问效果
                            这是进入之后的首页,直接就是创建界面
在这里插入图片描述

                       2.点击创建按钮的效果
                            这里可以看到我们已经成功创建了一个采购申请
在这里插入图片描述

                       3.SAP中查看创建的结果
                            直接 ME51N 去看就行了
ME51N

四、总结

        以上就是今天要讲的内容,本文仅仅只是会让大家对SAP系统集成有一个简单的了解,如果有说错或者不好的地方还望大家提出来见谅。感觉笔者写的好的别忘了关注点赞加评论哦,也欢迎大家一起来讨论。谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiao贱贱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值