简单的数据库管理--通讯录

抽空帮别人写了个C语言的课程设计,一个简单的通讯录,要求如下:
题目一  简单的数据库管理

建立一个通讯录系统,系统中保存着各人员的姓名、电话、住址、邮编等信息,通过该系统的界面可以浏览这些信息,也可以插入、添加及删除某个人员的信息。
数据结构:
1. Person
Name,phone,address,postcode
2.  Card
Person, *prev, *next
3.  Book
Count,*head,*last,*curcard
操作:
1.  双链表的建立,插入,添加及删除
2.  文件的读写
3.  键盘响应
参考界面:

-------------------------------------------------------------------------------------------------------------------------

/*
* 本程序在tc2.0及tc3.0下调试通过。
*
*/


/******头文件(.h)***********/
#include "stdio.h"    /*I/O函数*/
#include "bios.h"  /*ROM基本输入输出函数*/
#include "dos.h"    /*dos接口函数*/
#include "conio.h"   /*屏幕操作函数*/
#include "stdlib.h"   /*其它说明*/
#include "string.h"  /*字符串函数*/
#include "mem.h"  /*内存操作函数*/
#include "ctype.h" /*字符操作函数*/
#include "alloc.h"  /*动态地址分配函数*/

/****变量定义*******/

#define Enter 13
#define upkey 72
#define dnkey 80

struct person    /*定义数据结构*/
{
    char name[20];  /*姓名*/
    char phone[20];     /*电话*/
    char address[128]; /*地址*/
    char postcode[20];  /*邮编*/   
};
struct card
{
    struct person p;
    struct card *prior;/*前驱指针*/
    struct card *next;/*后继指针*/
};
struct book
{
    struct card *first;
    struct card *last;
    struct card *curcard;
};

struct book b;        /*通讯录*/

int iLastMenu = 0;    /*记住本次操作之前上一次选择的菜单项*/   

char cErrMsg[200]; /*记录最后操作的结果信息*/

/******函数原型*********/
void init();    /*初始化*/
void delete(); /*删除*/
void search(); /*查找*/
void save();  /*保存文件*/
void load();  /*读取文件*/
void insert();  /*插入*/
void append(); /*追加*/
int menu_select();  /*主菜单*/
void DisplayFirstPage();/*显示第一页*/
void DisplayPriorPage();/*显示上一页*/
void DisplayNextPage();/*显示下一页*/
void DisplayLastPage();/*显示最后页*/
void PrintErrMsg(char *sFmtMsg);/*打印结果信息*/
void inputs(char *s, int count,char *sInputTip);/*字符串输入和验证函数*/
void Quit();/*退出整个程序*/


/*******主函数开始**********/
main()
{   
    init();
   
    clrscr();
    for(;;)
    {
        switch(menu_select())   /*调用菜单函数返回一个整数值*/
        {
        case 0:DisplayFirstPage();break;    /*显示第一条记录*/
        case 1:DisplayPriorPage();break; /*显示上一条记录*/
        case 2:DisplayNextPage();break;  /*显示下一条记录*/
        case 3:DisplayLastPage();break;  /*显示最后一条记录*/
        case 4:append();break;  /*添加记录*/
        case 5:insert();break;  /*插入记录*/
        case 6:delete();break;  /*删除记录*/
        case 7:search();break;  /*按名字检索记录*/
        case 8:load();break;  /*导入文件中记录*/
        case 9:save();break;  /*保存文件*/
        case 10:Quit();    /*退出*/
        }
    }
}

/*菜单函数,函数返回值为整型,代表所选的菜单项*/
menu_select()
{
    char *f[]= {     /*定义菜单字符串数组*/
        /*"**************MENU*************",*/ /*菜单的标题行*/
        " 0. FirstPage 第一页", /*显示第一条记录*/
            " 1. PriorPage 上一页", /*显示上一条记录*/
            " 2. NextPage  下一页",  /*显示下一条记录*/
            " 3. LastPage  最后页", /*显示最后一条记录*/
            " 4. Append    添  加", /*添加记录*/
            " 5. Insert    插  入", /*插入记录*/
            " 6. Delete    删  除", /*删除记录*/
            " 7. SearchName检  索",  /*按名字检索记录*/
            " 8. LoadFile  导  入", /*导入文件中记录*/
            " 9. SaveFile  保  存", /*保存至文件中*/
            " 10. Quit     退  出",  /*退出*/
    }; 
    char s[80];
    int  i;
    int key=0; /*记录所压键值*/
    int c=0;
   
    textbackground(BLACK);  /*设置背景颜色为黑色*/
    window(1,1,80,25);  /*恢复原窗口大小*/

    clrscr();  /*清屏*/
    textcolor(YELLOW); /*设置文本颜色为黄色*/
    textbackground(BLUE);  /*设置背景颜色为兰色*/

   
    /*画右边结果显示窗口*/

    gotoxy(32,2);
    putch(0xda); /*输出左上角边框┏*/
    for(i=1;i<47;i++)
        putch(0xc4); /*输出上边框水平线*/
    putch(0xbf);  /*输出右上角边框  ┓*/
    for(i=3;i<19;i++)/*输出左右两边的垂直线*/
    {
        gotoxy(32,i);putch(0xb3);
        gotoxy(79,i);putch(0xb3);
    }
    gotoxy(32,18);putch(0xc0); /*输出左上角边框┗*/
    for(i=1;i<47;i++)
        putch(0xc4);  /*输出下边框水平线*/
    putch(0xd9);  /*输出右下角边框┛*/
    window(33,3,78,17); /* 制作右边结果显示窗口*/
    clrscr();   /*清屏*/
   
    gotoxy(2,2);
    cprintf("%s   %s","Name    姓名:",(b.curcard != NULL)?b.curcard->p.name:""); /*输出通讯录中的当前项*/
    gotoxy(2,4);
    cprintf("%s   %s","Phone   电话:",(b.curcard != NULL)?b.curcard->p.phone:""); /*输出通讯录中的当前项*/
    gotoxy(2,6);
    cprintf("%s   %s","Address 地址:",(b.curcard != NULL)?b.curcard->p.address:""); /*输出通讯录中的当前项*/
    gotoxy(2,8);
    cprintf("%s   %s","Postcode邮编:",(b.curcard != NULL)?b.curcard->p.postcode:""); /*输出通讯录中的当前项*/
   
    window(1,1,80,25);  /*恢复原窗口大小*/
   
    textcolor(YELLOW); /*设置文本颜色为黄色*/
    textbackground(BLUE);  /*设置背景颜色为兰色*/
   

    /*画最下面输入窗口*/
   
    gotoxy(2,20);
    putch(0xda); /*输出左上角边框┏*/
    for(i=1;i<77;i++)
        putch(0xc4); /*输出上边框水平线*/
    putch(0xbf);  /*输出右上角边框  ┓*/
    for(i=21;i<25;i++)/*输出左右两边的垂直线*/
    {
        gotoxy(2,i);putch(0xb3);
        gotoxy(79,i);putch(0xb3);
    }
    gotoxy(2,24);putch(0xc0); /*输出左上角边框┗*/
    for(i=1;i<77;i++)
        putch(0xc4);  /*输出下边框水平线*/
    putch(0xd9);  /*输出右下角边框┛*/

    window(3,21,78,23); /* 制作输入数据的窗口*/
    textcolor(WHITE); /*设置文本颜色为白色*/
    clrscr();   /*清屏*/
   
    gotoxy(2,1);
    cprintf("%s",cErrMsg); /*输出结果信息*/
   
    window(1,1,80,25);  /*恢复原窗口大小*/
   
    textcolor(YELLOW); /*设置文本颜色为黄色*/
    textbackground(BLUE);  /*设置背景颜色为兰色*/
   


    /*画左边主菜单窗口*/
   
    gotoxy(2,2);
    putch(0xda); /*输出左上角边框┏*/
    for(i=1;i<28;i++)
        putch(0xc4); /*输出上边框水平线*/
    putch(0xbf);  /*输出右上角边框  ┓*/
    for(i=3;i<18;i++)/*输出左右两边的垂直线*/
    {
        gotoxy(2,i);putch(0xb3);
        gotoxy(30,i);putch(0xb3);
    }
    gotoxy(2,18);putch(0xc0); /*输出左上角边框┗*/
    for(i=1;i<28;i++)
        putch(0xc4);  /*输出下边框水平线*/
    putch(0xd9);  /*输出右下角边框┛*/
    window(3,3,29,17); /* 制作显示菜单的窗口,大小根据菜单条数设计*/
    clrscr();   /*清屏*/
    for(i=0;i<11;i++)
    {
        gotoxy(3,i+1);
        cprintf("%s",f[i]); /*输出菜单项数组*/
    }

    i = iLastMenu;
    gotoxy(3,i + 1);  /*设置默认选项在上一次操作的项目上*/
    textbackground(LIGHTGREEN);/*设置背景颜色为浅绿*/
    cprintf("%s",f[iLastMenu]);  /*输出菜单项,表示选中*/
   
    while(key != Enter)   /*所压键不是回车键时*/
    {
        while(bioskey(1)==0);  /*查询是否压下了一个键*/
        key=bioskey(0);   /*返回下一个在键盘压下的键*/
        key=key&0xff?key&0xff:key>>8; /*对所压的键进行判断*/
        gotoxy(3,i+1);
        textbackground(BLUE);/*设置背景颜色为蓝色*/
        cprintf("%s",f[i]);  /*输出菜单项*/
        if(key==upkey) i=i==0?10:i-1; /*如压向上光标键↑,i减1,如已到第一行再上移,则到最后一行*/
        if(key==dnkey)i=i==10?0:i+1; /*如压向下光标键↓,i加1,如已到最后一行再下移,则到第一行*/
        gotoxy(3,i+1); /*光标移动i的下一项*/
        textbackground(LIGHTGREEN); /*将背景颜色设为浅绿*/
        cprintf("%s",f[i]);  /*输出菜单项*/
        c=i;  /*给代表菜单选项的整数赋值*/
    }

    textbackground(BLACK);  /*设置背景颜色为黑色*/
    window(1,1,80,25);  /*恢复原窗口大小*/

    iLastMenu = c;
    return c;  /*返回代表菜单选项的整数值*/
}

/*初始化函数*/
void init()
{
    b.first = NULL;
    b.last = NULL;
    b.curcard = NULL;

    memset(cErrMsg,0,200);
}


/*字符串输入和验证函数*/
void inputs(char *s, int count,char *sInputTip)
{
    char p[255];   

    do{
        clrscr();       

        gotoxy(2,1);
        cprintf("%s/n",sInputTip); /**/       

        memset(p,0,255);
        scanf("%s",p);/*输入字符串*/       
       
        gotoxy(2,2);
        if(strlen(p)>count)
        {   
            cprintf("too long! Press Any Key and Retry!");/*进行长度校验,超过count值重输入*/
            getch();
        }
        else
            if(strlen(p) < 1)
            {           
                cprintf("too short! Press Any Key and Retry!");
                getch();
            }

        clreol();
    }while((strlen(p)>count)||(strlen(p) < 1));
    strcpy(s,p); /*将输入的字符串拷贝到字符串s中*/
}

void PrintErrMsg(char *sFmtMsg)
{
    textcolor(WHITE); /*设置文本颜色为白色*/
    textbackground(BLUE);  /*设置背景颜色为兰色*/
    window(3,21,78,23); /* 制作输入数据的窗口*/
    clrscr();   /*清屏*/

    gotoxy(2,1);
    cprintf("%s",sFmtMsg); /*输出结果信息*/

    strcpy(cErrMsg,sFmtMsg);/*将本次结果信息拷贝至系统全局结果信息数组中*/
}

/*显示第一页*/
void DisplayFirstPage()
{

    b.curcard = b.first;

    if(b.curcard == NULL)
        PrintErrMsg("There is no record now!");
    else
        PrintErrMsg("First record is found!");
}

/*显示上一页*/
void DisplayPriorPage()
{
    if(b.curcard != NULL)
    {
        if(b.curcard->prior != NULL)
        {
            b.curcard = b.curcard->prior;
            PrintErrMsg("Prior record is found!");
        }
        else
            PrintErrMsg("Has Reached the first record!");       
    }
    else
    {
        PrintErrMsg("Current card is empty,no prior record!");
    }
}

/*显示下一页*/
void DisplayNextPage()
{
   
    if(b.curcard != NULL)
    {
        if(b.curcard->next != NULL)
        {
            b.curcard = b.curcard->next;
            PrintErrMsg("Next record is found!");
        }
        else
            PrintErrMsg("Has Reached the Last record!");       
    }
    else
    {
        PrintErrMsg("Current card is empty,no next record!");
    }
}

/*显示最后页*/
void DisplayLastPage()
{
    b.curcard = b.last;

    if(b.curcard == NULL)
        PrintErrMsg("There is no record now!");
    else
        PrintErrMsg("Last record is found!");
}



/*删除结点*/
void delete()
{
    char cFmtMsg[100];
    struct card *p; /*定义临时变量,p指向要删除的结点p*/
       
    p = b.curcard;

    if (!p)
    {
        PrintErrMsg("Current card is empty, nothing deleted!");
        return;
    }

    if(p != NULL) /*调用查找函数,如果找到做下面的处理*/
   {
      if(b.first == p)    /*如果是第一个结点*/
      {
         b.first = p->next;  /*将头指针指向其后继结点*/
         if(b.first)    /*如头指针不为空*/
         {
             b.first->prior=NULL;  /*将头结点的前驱设为空*/
             b.curcard = b.first;
         }       
         else
         {
                 b.last = NULL;   /*否则头为空,尾也为空*/
                b.curcard = NULL;
         }
        }
      else   /*删除的不是第一个结点*/
      {
         p->prior->next = p->next;  /*p的前驱的后继指向p原来的后继*/
         if(p != b.last)   /*如果p不是最后个结点*/
         {
             p->next->prior = p->prior;  /*p后继的前驱指向p原来的前驱*/
             b.curcard = p->next;
         }           
         else
         {
             b.last = p->prior;/*如p是最后一个结点,修改尾指针为p的前驱*/
             b.curcard = b.last;
         }       
      }       
       
    sprintf(cFmtMsg,"have deleted %s CARD/n%s",p->p.name,"Don't forget save");
    PrintErrMsg(cFmtMsg);

    free(p);  /*删除结点后,不要忘记释放空间*/
   }

   
   
}

/*按姓名查找结点*/
void search()
{
    struct card *p;  /*移动指针*/
    char s[15];  /*保存姓名*/
    char cFmtMsg[100];
   
    textcolor(WHITE); /*设置文本颜色为白色*/
    textbackground(BLUE);  /*设置背景颜色为兰色*/
    window(3,21,78,23); /* 制作输入数据的窗口*/

    inputs(s,15,"please enter name for search:");
   
    p = b.first;  /*移动指针从头指针开始*/
    if (!p)
    {
        PrintErrMsg("Book is empty!"); /*显示没找到信息*/

        return;
    }

    while(strcmp(p->p.name,s)&&p!=NULL) /*做比较判断*/
        p=p->next;   /*没找到,指针后移继续查找*/
    if(p == NULL)  /*指针为空,说明没有找到*/
    {
        sprintf(cFmtMsg,"nlist no %s CARD",s);
        PrintErrMsg(cFmtMsg); /*显示没找到信息*/
    }
    else
    {
        sprintf(cFmtMsg,"nlist FOUND %s CARD",s);
        PrintErrMsg(cFmtMsg); /*显示没找到信息*/

        b.curcard = p;   
    }
}

/*插入结点*/
void insert()
{
    char cFmtMsg[100];
    struct card *p,*info; /* info为新结点,p为新结点的后继*/
       
    textcolor(WHITE); /*设置文本颜色为白色*/
    textbackground(BLUE);  /*设置背景颜色为兰色*/
    window(3,21,78,23); /* 制作输入数据的窗口*/
    clrscr();   /*清屏*/
   
     
    info = (struct card *)malloc(sizeof(struct card)); /*申请空间*/
    memset(info,0,sizeof(struct card));
    if(!info)
    {
        PrintErrMsg("/nout of memory"); /*如没有得到空间,内存溢出*/
        exit(0);  /*退出程序*/
    }
    info->next=NULL;  /*新结点的后继为空*/
    info->prior=NULL; /*新结点的前驱为空*/
   
    inputs(info->p.name,20,"Please input NAME:");  /*输入新结点的姓名,并校验*/

    inputs(info->p.phone,20,"Please input Phone:"); /*输入新结点的电话,并校验*/

    inputs(info->p.address,128,"Please input Address:"); /*输入新结点的地址,并校验*/
   
    inputs(info->p.postcode,20,"Please input Postcode:"); /*输入新结点的邮编,并校验*/

    p = b.curcard;  /*移动指针从当前指针开始*/

    b.curcard = info;/*更新当前指针*/
   
    if(p==NULL)  /*如p为空*/
        if(p == b.first) /*如p为头指针,说明链表为空*/
        {
            b.first = info;  /*新结点为头指针*/
            b.first->prior = NULL; /*头结点的前驱为空*/
            b.last = b.first;  /*唯一结点,尾指针等于头指针*/
        }
        else  /*新结点插在尾部*/
        {
            b.last->next = info;
            info->prior = b.last;
            b.last = info;  /*尾指针指向新结点*/
        }
    else
            if(p == b.first)  /*p不为空,但p为头指针,新结点插在第一个结点位置*/
            {
                info->prior = NULL; /*新结点的前驱为空*/
                info->next = p; /*新结点的后继为p*/
                p->prior = info; /*p的前驱是新结点*/
                b.first = info; /*修改头指针指向新结点*/
            }
            else /*新结点插入在中间某一个位置p之前*/
            {
                info->next = p; /*新结点的后继是p*/
                info->prior = p->prior; /*新结点的前驱是p的前驱*/
                p->prior->next = info; /*p的前驱的后继是新结点*/
                p->prior = info; /*p的前驱是新结点*/
            }
           
            sprintf(cFmtMsg," ----have inserted %s CARD----/n%s",info->p.name,"---Don't forget save---");
            PrintErrMsg(cFmtMsg);                       
}

/*保存文件*/
void save()
{
    FILE *fp;  /*定义指向文件的指针*/
    struct card *p; /*定义移动指针*/
    char outfile[100]; /*保存输出文件名*/
   
    textcolor(WHITE); /*设置文本颜色为白色*/
    textbackground(BLUE);  /*设置背景颜色为兰色*/
    window(3,21,78,23); /* 制作输入数据的窗口*/
    clrscr();   /*清屏*/
    inputs(outfile,100,"Enter outfile name,for example c://test//test.txt:");

    if((fp=fopen(outfile,"wb"))==NULL) /*为输出打开一个二进制文件*/
    {
        PrintErrMsg("can not open file");
        return; /*返回*/
    }
    PrintErrMsg("Saving file......");
    p = b.first; /*移动指针从头指针开始*/
    while(p != NULL) /*如p不为空*/
    {
        fwrite(p,sizeof(struct card),1,fp); /*写入一条记录*/
        p=p->next;  /*指针后移,处理下一条记录*/
    }
    fclose(fp); /*关闭文件*/
    PrintErrMsg("-----save success!!-----");/*显示保存成功*/
}

/*读文件*/
void load()
{
    struct card *p,*q=NULL; /*定义记录指针变量*/
    FILE *fp;            /* 定义指向文件的指针*/
    char infile[100];    /*保存文件名*/
    long pos, pos2;            /*保存文件指针的位置*/
       
    textcolor(WHITE); /*设置文本颜色为白色*/
    textbackground(BLUE);  /*设置背景颜色为兰色*/
    window(3,21,78,23); /* 制作输入数据的窗口*/
    clrscr();   /*清屏*/
    inputs(infile,100,"Enter infile name,for example c://test//test.txt:");

    if((fp=fopen(infile,"rb"))==NULL) /*打开一个二进制文件,为读方式*/
    {
        PrintErrMsg("can not open file/n"); /*如不能打开,则结束程序*/
        return; /*返回*/
    }

    if(fseek(fp, 0L, SEEK_SET))            /*将文件指针指到开始位置*/
   {
      PrintErrMsg( "fseek to beginning failed" );
      return;
   }

    if((pos = ftell(fp)) == -1L)        /*获取开始指针位置,以便结合结束位置指针判断文件长度*/
    {
        PrintErrMsg( "ftell failed" );
        return;
    }

    if(fseek( fp, 0L, SEEK_END))        /*将文件指针指到结束位置*/
   {
      PrintErrMsg( "fseek to end failed" );
      return;
   }

   if((pos2 = ftell(fp)) == -1L)        /*获取结束指针位置,以便结合开始位置指针判断文件长度*/
    {
        PrintErrMsg( "ftell failed" );
        return;
    }

   if((pos2 - pos) < sizeof(struct card)) /*文件中记录数不足一个或为空*/
   {
         PrintErrMsg("The infile has no records,please check!");
         return;
   }
  
   if(fseek( fp, 0L, SEEK_SET))/*将文件指针重新指到开始位置,*/
   {
      PrintErrMsg( "fseek to beginning failed" );
      return;
   }

    while(b.first) /*当表不为空时,清空链表*/
    {
        p=b.first; /*从头指针开始*/
        b.first=b.first->next; /*删除头结点*/
        free(p); /*释放空间*/
    }

    PrintErrMsg(" -----Loading file!-----"); /*显示正在读文件*/
    b.first =(struct card *)malloc(sizeof(struct card)); /*为头指针申请空间*/
    if(!b.first) /*如果没有得到空间 显示内存溢出*/
    {
        PrintErrMsg("out of memory!");
        return;  /*返回*/
    }
    p = b.first; /*将头指针赋值给p*/
    while(!feof(fp)) /*当文件不空时*/
    {
        if(1!=fread(p,sizeof(struct card),1,fp))break; /*将记录读到p所指的结点*/
        p->next=(struct card *)malloc(sizeof(struct card));/*为p的后继申请空间*/
        if(!p->next)  /*如果没有得到空间显示内存溢出*/
        {
            PrintErrMsg("out of memory!");
            return; /*返回*/
        }
        p->prior=q; /*得到空间,则链接指针关系p的前驱为q*/
        q=p;  /*保存p做为新的p结点的前驱*/
        p=p->next;  /*p指针后移*/       
    }
    q->next=NULL; /*q的后继为空*/
    b.last = q;  /*尾指针为q*/
    b.first->prior=NULL; /*头指针的前驱为空*/

    b.curcard = b.first;
    PrintErrMsg("---You have success read data from file!!!---"); /*显示成功读入记录*/
   
    fclose(fp); /*关闭文件*/   
}

/*追加记录*/
void append()
{
    char cFmtMsg[200];
    struct card *info; /* info为新结点*/
       
    textcolor(WHITE); /*设置文本颜色为白色*/
    textbackground(BLUE);  /*设置背景颜色为兰色*/
    window(3,21,78,23); /* 制作输入数据的窗口*/
    clrscr();   /*清屏*/   
     
    info = (struct card *)malloc(sizeof(struct card)); /*申请空间*/
    memset(info,0,sizeof(struct card));

    if(!info)
    {
        PrintErrMsg("out of memory"); /*如没有得到空间,内存溢出*/
        exit(0);  /*退出程序*/
    }
    info->next=NULL;  /*新结点的后继为空*/
    info->prior=NULL; /*新结点的前驱为空*/
   
    inputs(info->p.name,20,"Please input NAME:");  /*输入新结点的姓名,并校验*/

    inputs(info->p.phone,20,"Please input Phone:"); /*输入新结点的电话,并校验*/

    inputs(info->p.address,128,"Please input Address:"); /*输入新结点的地址,并校验*/
   
    inputs(info->p.postcode,20,"Please input Postcode:"); /*输入新结点的邮编,并校验*/

    if(b.last == NULL) /*说明链表为空*/
    {
        b.last = info;  /*新结点为头指针*/
        b.last->prior = NULL; /*头结点的前驱为空*/
        b.first = b.last;  /*唯一结点,尾指针等于头指针*/
    }
    else
    {
        b.last->next = info;
         info->prior = b.last;
         info->next = NULL;
         b.last = info;
    }
     
     b.curcard = info;/*更新当前指针*/

     sprintf(cFmtMsg,"have appended %s CARD/n%s",info->p.name,"Don't forget save");
    PrintErrMsg(cFmtMsg);
}


/*退出整个程序*/
void Quit()
{
    clrscr();
    exit(0);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值