Orcal数据库存储数据+(PHP+Apache)中间通信+Android显示

首先搭建好平台,从题目可以看出需要下载orcal数据库、Apache和PHP,各自的环境配置网上有讲,好多都是PHP+mysql,将PHP和MySQL连接我很快就弄好了,但是将PHP和orcal连接废了好多时间,主要是你要统一好版本。

https://jingyan.baidu.com/article/ce09321b7593062bfe858f6c.html,我第一次配置就是跟着这个学,如果你只是想让PHP和MySQL连接这个还是比较好多,一次就成功了。

我现在是orcal11+Apache24+PHP5,之前用的PHP7导致php.ini中不能开启extension=php_oci8_11g.dll配置。

PHP5里面只有php_oci8_12c.dll,其他两个需要自己另外下载。网址是http://windows.php.net/downloads/pecl/releases/oci8/ 找好和自己匹配的版本(这个人家整理的真不错,非常细心)。

连接成功后就可以在你所配置的放置php文件的Apache文件夹下新建php文档。文件名为conorcal.php(会用到)代码如下:

<?php


$conn = oci_connect('用户名', '密码', '主机地址/数据库名');

if ($conn) {
# code...
echo "连接成功";
}


 ?>


打开自己设置的主机IP:端口号/文件地址,就可以出现此页面。


成功之后就可以走向使用手机查询、插入、删除的开发之路了。首先看看最后效果。


按姓名查询


按地址查询



按年龄查询


插入


这就是最后实现的效果。

首先贴上php代码,selectbywhere.php  根据APP请求的搜索条件进行搜索


<?php

require 'conorcal.php';//上面的连接文件夹,放在同一文件夹中

$personname = $_POST['name'];
$personage = $_POST['age'];
$personaddress=$_POST['address'];

$result1=array();

if (!empty($_POST['name'])) {
# code...
$query = 'SELECT * FROM androidtest where personname=:personname'; 
$stid = oci_parse($conn, $query) ;// 编译SQL语句,准备执行 

oci_bind_by_name($stid, ":personname", $personname);//设置绑定变量的取值
    
    }elseif (!empty($_POST['age'])&&empty($_POST['address'])) {
    # code...
    $query = 'SELECT * FROM androidtest where  personage=:personage '; 
    $stid = oci_parse($conn, $query) ;
    oci_bind_by_name($stid, ":personage", $personage);
    }elseif (!empty($_POST['age'])&&!empty($_POST['address'])) {
    # code...
    $query = 'SELECT * FROM androidtest where personage=:personage and personaddress=:personaddress '; // 查询语句
        $stid = oci_parse($conn, $query) ;
    oci_bind_by_name($stid, ":personage", $personage);
        oci_bind_by_name($stid, ":personaddress", $personaddress);


    }elseif (empty($_POST['age'])&&!empty($_POST['address'])) {
    # code...
    $query = 'SELECT * FROM androidtest where personaddress=:personaddress '; // 查询语句 
    $stid = oci_parse($conn, $query) ;
    oci_bind_by_name($stid, ":personaddress", $personaddress);
    }else {
    # code...
    $query = 'SELECT * FROM androidtest '; // 查询语句 
    $stid = oci_parse($conn, $query) ;
    }

//$stid = oci_parse($conn, $query) ;// 编译SQL语句,准备执行 
if (!$stid) { 
$e = oci_error($conn); 
echo($e['message']); 
exit; 


$r = oci_execute($stid);

//$r = oci_execute($stid, OCI_DEFAULT); // 执行SQL。OCI_DEFAULT表示不要自动commit 
if(!$r) { 
$e = oci_error($stid); 
echo ($e['message']); 
exit; 


while ($row = oci_fetch_assoc($stid)) { //提取结果数据的一行到一个关联数组  
$result1[] = $row;


if(isset($result1)){
echo json_encode($result1);

}
oci_free_statement($stid);  //释放关联于语句或游标的所有资源
oci_close($conn);   //关闭 Oracle数据库连接  

 ?>

根据APP中填写的条件进行插入并显示

这是插入insert2.php  

<?php

$response=array();

if (isset($_POST['name'])) {
# code...
require 'conorcal.php';


$personname = $_POST['name'];
$personage = $_POST['age'];
    $personaddress=$_POST['address'];

$query='INSERT INTO androidtest (personname,personage,personaddress) values(:personname,:personage,:personaddress)';// 查询语句 

$stid = oci_parse($conn, $query); // 编译SQL语句,准备执行 

if (!$stid) { 
$e = oci_error($conn); 
echo($e['message']); 
exit; 


oci_bind_by_name($stid, ":personname", $personname);
oci_bind_by_name($stid, ":personage", $personage);
oci_bind_by_name($stid, ":personaddress", $personaddress);

$r = oci_execute($stid, OCI_COMMIT_ON_SUCCESS) or die('die'); // 执行SQL。OCI_DEFAULT表示不要自动commit 
if(!$r) { 
$e = oci_error($stid); 
echo ($e['message']); 
exit; 


if(oci_num_rows($stid)){

    require 'select.php';

}


oci_free_statement($stid);  //释放关联于语句或游标的所有资源
oci_close($conn);   //关闭 Oracle数据库连接  


}else {
# code...
$response["success"] = 0;   
    $response["message"] = "name can't emply!";   
    echo json_encode($response);  
}

 ?>

这是显示select.php

<?php


//require 'conorcal.php';


$query = 'SELECT * FROM androidtest'; // 查询语句 
$stid = oci_parse($conn, $query); // 编译SQL语句,准备执行 
if (!$stid) { 
$e = oci_error($conn); 
echo($e['message']); 
exit; 





$r = oci_execute($stid, OCI_DEFAULT); // 执行SQL。OCI_DEFAULT表示不要自动commit 
if(!$r) { 
$e = oci_error($stid); 
echo ($e['message']); 
exit; 

while($row = oci_fetch_assoc($stid)) { //提取结果数据的一行到一个关联数组  
$result1[]=$row;





if($result1){
echo json_encode($result1);

}
/*oci_free_statement($stid);  //释放关联于语句或游标的所有资源
oci_close($conn);   //关闭 Oracle数据库连接  
*/


 ?>

根据APP中填写的条件进行删除并显示

这是删除 delete.php

<?php


require 'conorcal.php';


$personname = $_POST['name'];
$personage = $_POST['age'];
    $personaddress=$_POST['address'];
    $stid=0;


if (!empty($_POST['name'])) {
# code...
$query = 'Delete FROM androidtest where personname = :personname'; 
$stid = oci_parse($conn, $query) ;// 编译SQL语句,准备执行 


oci_bind_by_name($stid, ":personname", $personname);//设置绑定变量的取值
    
    }elseif (!empty($_POST['age'])&&empty($_POST['address'])) {
    # code...
    $query = 'Delete FROM androidtest where  personage=:personage '; 
    $stid = oci_parse($conn, $query) ;
    oci_bind_by_name($stid, ":personage", $personage);
    }elseif (!empty($_POST['age'])&&!empty($_POST['address'])) {
    # code...
    $query = 'Delete FROM androidtest where personage=:personage and personaddress=:personaddress '; // 查询语句
        $stid = oci_parse($conn, $query) ;
    oci_bind_by_name($stid, ":personage", $personage);
        oci_bind_by_name($stid, ":personaddress", $personaddress);


    }elseif (empty($_POST['age'])&&!empty($_POST['address'])) {
    # code...
    $query = 'Delete FROM androidtest where personaddress=:personaddress '; // 查询语句 
    $stid = oci_parse($conn, $query) ;
    oci_bind_by_name($stid, ":personaddress", $personaddress);
    }else {
    # code...
    echo "没删除";
    }




//$stid = oci_parse($conn, $query) ;// 编译SQL语句,准备执行 
if (!$stid) { 
$e = oci_error($conn); 
echo($e['message']); 
exit; 



$r = oci_execute($stid);


//$r = oci_execute($stid, OCI_DEFAULT); // 执行SQL。OCI_DEFAULT表示不要自动commit 
if(!$r) { 
$e = oci_error($stid); 
echo ($e['message']); 
exit; 





if(oci_num_rows($stid)){
    require 'select.php';
}


oci_free_statement($stid);  //释放关联于语句或游标的所有资源
oci_close($conn);   //关闭 Oracle数据库连接  

 ?>

显示依旧是上面select.php

其中所构成的网址为:

String phpUrlDelete = "http://192.168.1.189:8800/orcal/delete.php";
String phpUrl = "http://192.168.1.189:8800/orcal/selectbywhere.php";
String phpUrlSend = "http://192.168.1.189:8800/orcal/insert2.php";

android端我是通过OkHttp进行网络通信,向php索要自己需要的数据。php返回数据,上面php得到数据库已经转化成JSON的形式,需要做的就是通过APP端解析json得到数据,并通过RecycleView制作成表格形式显示出来。

先搭建layout

制作背景边框,使其看起来像表格一样

frame_background.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <stroke
        android:color="@android:color/black"
        android:width="1dp" />

</shape>

date_item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/name_item"
        android:layout_width="0dp"
        android:layout_height="30dp"
        android:layout_weight="1"
        android:background="@drawable/frame_background"
        android:gravity="center" />
    <TextView
        android:id="@+id/age_item"
        android:layout_width="0dp"
        android:layout_height="30dp"
        android:layout_weight="1"
        android:background="@drawable/frame_background"
        android:gravity="center"/>
    <TextView
        android:id="@+id/address_item"
        android:layout_width="0dp"
        android:layout_height="30dp"
        android:layout_weight="1"
        android:background="@drawable/frame_background"
        android:gravity="center"/>
</LinearLayout>
layout_form.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@drawable/frame_background">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="30dp">
        <TextView
            android:text="name"
            android:textStyle="bold"
            android:layout_width="0dp"
            android:layout_height="30dp"
            android:layout_weight="1"
            android:background="@drawable/frame_background"
            android:gravity="center" />
        <TextView
            android:text="age"
            android:textStyle="bold"
            android:layout_width="0dp"
            android:layout_height="30dp"
            android:layout_weight="1"
            android:background="@drawable/frame_background"
            android:gravity="center"/>
        <TextView
            android:text="address"
            android:textStyle="bold"
            android:layout_width="0dp"
            android:layout_height="30dp"
            android:layout_weight="1"
            android:background="@drawable/frame_background"
            android:gravity="center"/>
    </LinearLayout>
<android.support.v7.widget.RecyclerView
    android:id="@+id/recycler_form"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

</LinearLayout>
activity_main.xml

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.phptest.MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:layout_marginTop="8dp"
        android:text="Hello World!"
        app:layout_constraintBottom_toTopOf="@+id/include"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editaddress" />

    <Button
        android:id="@+id/get"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="26dp"
        android:layout_marginTop="20dp"
        android:text="select"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/editname"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="22dp"
        android:layout_marginStart="30dp"
        android:text="name"
        app:layout_constraintBottom_toBottomOf="@+id/editname"
        app:layout_constraintEnd_toStartOf="@+id/editname"
        app:layout_constraintStart_toStartOf="parent" />

    <EditText
        android:id="@+id/editname"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginEnd="15dp"
        android:ems="10"
        android:hint="Name"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toBottomOf="@+id/get"
        app:layout_constraintEnd_toStartOf="@+id/get"
        app:layout_constraintStart_toEndOf="@+id/name"
        app:layout_constraintTop_toTopOf="@+id/get" />

    <TextView
        android:id="@+id/age"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="30dp"
        android:text="age"
        app:layout_constraintBottom_toBottomOf="@+id/editage"
        app:layout_constraintStart_toStartOf="parent" />

    <EditText
        android:id="@+id/editage"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginBottom="37dp"
        android:layout_marginEnd="14dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="37dp"
        android:ems="10"
        android:hint="age"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toTopOf="@+id/delete"
        app:layout_constraintEnd_toStartOf="@+id/send"
        app:layout_constraintStart_toStartOf="@+id/editname"
        app:layout_constraintTop_toBottomOf="@+id/editname" />

    <TextView
        android:id="@+id/address"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="12dp"
        android:layout_marginEnd="22dp"
        android:layout_marginStart="16dp"
        android:text="address"
        app:layout_constraintBottom_toBottomOf="@+id/editaddress"
        app:layout_constraintEnd_toStartOf="@+id/editaddress"
        app:layout_constraintStart_toStartOf="parent" />

    <EditText
        android:id="@+id/editaddress"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginEnd="18dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:ems="10"
        android:hint="address"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toStartOf="@+id/delete"
        app:layout_constraintStart_toStartOf="@+id/editage"
        app:layout_constraintTop_toBottomOf="@+id/editage" />

    <Button
        android:id="@+id/send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:layout_marginStart="8dp"
        android:text="insert"
        app:layout_constraintBottom_toBottomOf="@+id/editage"
        app:layout_constraintEnd_toEndOf="@+id/get"
        app:layout_constraintStart_toStartOf="@+id/get"
        app:layout_constraintTop_toTopOf="@+id/editage" />

    <Button
        android:id="@+id/delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:text="delete"
        app:layout_constraintBottom_toBottomOf="@+id/editaddress"
        app:layout_constraintEnd_toEndOf="@+id/send"
        app:layout_constraintStart_toStartOf="@+id/send"
        app:layout_constraintTop_toTopOf="@+id/editaddress" />


    <include
        android:id="@+id/include"
        layout="@layout/layout_form"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_marginBottom="48dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintStart_toStartOf="parent" />


</android.support.constraint.ConstraintLayout>

上面这个代码是ConstraintLayout直接拖动组件画出来的,可以自己去写



下面来看java文件

DateList.java

public class DateList {
    String person_name;
    String person_age;
    String person_address;

    public DateList(String person_name, String person_age, String person_address) {
        this.person_name = person_name;
        this.person_age = person_age;
        this.person_address = person_address;
    }

    public String getPerson_name() {
        return person_name;
    }

    public String getPerson_age() {
        return person_age;
    }

    public String getPerson_address() {
        return person_address;
    }
}
DateAdapter.java

public class DateAdapter extends RecyclerView.Adapter<DateAdapter.MyViewHolder> {

    private List<DateList> dateLists;

    static class MyViewHolder extends RecyclerView.ViewHolder {
        TextView person_name;
        TextView person_age;
        TextView person_address;

        public MyViewHolder(View itemView) {
            super(itemView);
            person_name = itemView.findViewById(R.id.name_item);
            person_age = itemView.findViewById(R.id.age_item);
            person_address = itemView.findViewById(R.id.address_item);
        }
    }

    public DateAdapter(List<DateList> dateLists) {
        this.dateLists = dateLists;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.date_item, parent, false);
        MyViewHolder myViewHolder = new MyViewHolder(view);
        return myViewHolder;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        DateList dateList = dateLists.get(position);
        holder.person_name.setText(dateList.getPerson_name());
        holder.person_age.setText(dateList.getPerson_age());
        holder.person_address.setText(dateList.getPerson_address());

    }

    @Override
    public int getItemCount() {
        return dateLists.size();
    }
}

上面是RecycleView表格显示

重要的OkHttp通信代码

HttpUtil

public class HttpUtil {

    public static void sendOkHttpRequest(String address, Callback callback) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url(address).build();
        client.newCall(request).enqueue(callback);
    }

    public static void sendOkHttpPost(String address, String personName,String personage, String personAddress, Callback callback) {
        OkHttpClient client = new OkHttpClient();
        RequestBody requestBody = new FormBody.Builder()
                .add("name", personName)//引号中的name要和php接收$_POST['name']引号里面的一样
                .add("age",personage)
                .add("address", personAddress)
                .build();
        Request request = new Request.Builder()
                .url(address)
                .post(requestBody)
                .build();
        client.newCall(request).enqueue(callback);

    }
}

MainActivity.java

public class MainActivity extends AppCompatActivity {

    Button mPhp_get;
    Button mPhp_send;
    Button mPhp_delete;
    TextView mTxt_php;
    EditText mName_edt;
    EditText mAge_edt;
    EditText mAddress_edt;
    String name ;
    String age ;
    String address;

    String mPersonName_get;
    String mPersonAge_get;
    String mPersonAddress_get;

    RecyclerView recycler_layout;

    List<DateList> lists = new ArrayList<>();
    DateAdapter adapter;

    String phpUrlDelete = "http://192.168.1.189:8800/orcal/delete.php";
    String phpUrl = "http://192.168.1.189:8800/orcal/selectbywhere.php";
    String phpUrlSend = "http://192.168.1.189:8800/orcal/insert2.php";


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recycler_layout.setLayoutManager(layoutManager);
        adapter = new DateAdapter(lists);

    }

    private void initView() {
        mPhp_get = findViewById(R.id.get);
        mPhp_send = findViewById(R.id.send);
        mPhp_delete = findViewById(R.id.delete);
        mPhp_get.setOnClickListener(new mPhpButtonListener());
        mPhp_send.setOnClickListener(new mPhpSendButtonListener());
        mPhp_delete.setOnClickListener(new mPhpDeleteButtonListener());
        mTxt_php = findViewById(R.id.textView);
        mName_edt = findViewById(R.id.editname);
        mAge_edt = findViewById(R.id.editage);
        mAddress_edt = findViewById(R.id.editaddress);
        recycler_layout = findViewById(R.id.recycler_form);

    }

    private class mPhpButtonListener implements View.OnClickListener {
        @Override
        public void onClick(View view) {

            sendText2Okhttp(phpUrl);
           // mSendOkHttpGet();
            lists.clear();

        }
    }

    private void mSendOkHttpGet() {
        HttpUtil.sendOkHttpRequest(phpUrl, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                final String responseDate = response.body().string();
                parseJSONWithJSONObject(responseDate);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mTxt_php.setText(responseDate);
                        mName_edt.setText(name);
                        mAge_edt.setText(age);
                        mAddress_edt.setText(address);

                    }
                });
            }
        });
    }

    //insert
    private class mPhpSendButtonListener implements View.OnClickListener {
        @Override
        public void onClick(View view) {
            sendText2Okhttp(phpUrlSend);
            lists.clear();
        }
    }

    //delete
    private class mPhpDeleteButtonListener implements View.OnClickListener {
        @Override
        public void onClick(View view) {
            sendText2Okhttp(phpUrlDelete);
            lists.clear();
        }
    }



    //获取EditText里是数发送给PHP
    private void sendText2Okhttp(final String url) {
        mPersonName_get = mName_edt.getText().toString();
        mPersonAge_get = mAge_edt.getText().toString();
        mPersonAddress_get = mAddress_edt.getText().toString();
        new Thread(new Runnable() {
            @Override
            public void run() {
                mSendOkHttpPost(url,mPersonName_get,mPersonAge_get, mPersonAddress_get);

            }
        }).start();
    }

    private void mSendOkHttpPost(String url,String personname,String personage, String personAddress_get) {
        HttpUtil.sendOkHttpPost(url, personname,personage, personAddress_get, new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, final Response response) throws IOException {
                final String responseDate = response.body().string();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (responseDate .equals("[]")||responseDate .equals("") ) {
                            Toast.makeText(MainActivity.this, "没有此数据", Toast.LENGTH_LONG).show();
                            mTxt_php.setText("没有此数据");
                        } else {
                            mTxt_php.setText(responseDate);
                        }
                        parseJSONWithJSONObject(responseDate);
                    }
                });

            }
        });
    }

    private void parseJSONWithJSONObject(String jsonDate) {
        try {
            JSONArray jsonArray = new JSONArray(jsonDate);
            for (int i = 0; i < jsonArray.length(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                name = jsonObject.getString("PERSONNAME");
                age = jsonObject.getString("PERSONAGE");
                address = jsonObject.getString("PERSONADDRESS");
                DateList dateList = new DateList(name, age, address);
                lists.add(dateList);
                recycler_layout.setAdapter(adapter);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

    }

}

好了,代码完整贴上了,程序可以运行!


感想:写博客是需要耐心的~,而语言表达能力不好的我只能不停的贴代码。还需努力啊。。。

仰望一下博客写的简单易懂,而且一篇又一篇 的大神们。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值