电子词典10.30

项目要求:

  1. 登录注册功能,不能重复登录,重复注册

  2. 单词查询功能

  3. 历史记录功能,存储单词,意思,以及查询时间

  4. 基于TCP,支持多客户端连接

  5. 采用数据库保存用户信息与历史记录

  6. 将dict.txt的数据导入到数据库中保存。

  7. 按下ctrl+c退出客户端后,注销该客户端的登录信息

格式要求:

  1. main函数只跑逻辑,不允许跑功能代码

  2. 功能代码封装成函数

服务器代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <math.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <sqlite3.h>
#include <time.h>
#include <stdlib.h>
#define ERR_MSG(msg) {printf("Line=%d\n",__LINE__);perror(msg);}
#define IP "0.0.0.0"
#define PORT 8888
struct ifo_client    ///传参结构体
{
	int cfd;
	struct sockaddr_in cin;
	sqlite3 * db;

};
void do_insert(sqlite3 *db)       //导入词典文本函数
{
	printf("正在导入电子词典,请稍候\n");
    FILE* fd;
    fd=fopen("./dict.txt","r");
    if (fd==NULL)
    {
        perror("fopen");
        return ;
    }
    char *p;
    char arr[128]="";
    char word[128]="";
    char mean[128]="";
    char crr[128]="";
    int j;
    int i;
    while(1)
    {
        bzero(arr,sizeof(arr));
        bzero(word,sizeof(word));
        bzero(mean,sizeof(mean));
        p=fgets(arr,sizeof(arr),fd);
        arr[strlen(arr)-1]=0;
        for(j=0;j<128;j++)
        {
            if (arr[j]==' ')
            {
                break;
            }
        }
        for (i=0; i<128; i++)
        {
            if (arr[i]=='.'&&(i>j))
            {

                for (j=i; j>0; j--)
                {
                    if (arr[j]==' ')
                    {
                        strcpy(mean,arr+j+1);
                        break ;
                    }
                }
                break;
            }
        }
        arr[j]=0;
        strcpy(word,arr);
        for(int k=(strlen(word)-1);k>0;k--)
        {
            if(word[k]!=' ')
            {
                word[k+1]=0;
                break;
            }
        }
        if (strlen(word)==0)
        {
            break;
        }
        sprintf(crr,"insert into dictionary values (\"%s\",\"%s\")",word,mean);
        char * errmsg;
        if (sqlite3_exec(db,crr,NULL,NULL,&errmsg)!=SQLITE_OK)
        {
            fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
            return;
        }
        if (p==NULL)
        {
            if (errno!=0)
            {
                ERR_MSG("fgets");
                return ;
            }
        }
    }
    fclose(fd);
    printf("insert dictionary success\n");
}

void *callback(void *pp)      //回调函数
{
	struct ifo_client ifo=*(struct ifo_client*)pp;
	int cfd=ifo.cfd;
	struct sockaddr_in connect_done=ifo.cin;
	sqlite3 *db=ifo.db;
	char buf[1024]="";
	char arr[128]="";
	char brr[128]="";
	char insert[256]="";
	ssize_t res=0;
	int row,column;
	while(1)
	{
		char **pres;
		bzero(buf,sizeof(buf));
		bzero(arr,sizeof(arr));
		bzero(brr,sizeof(brr));
		bzero(insert,sizeof(insert));
		res=recv(cfd,buf,sizeof(buf),0);
		char * errmsg;
		if (res<0)
		{
			ERR_MSG("recv");
			return NULL;
		}
		if (buf[0]=='N')
		{
		//	pres=NULL;
			sprintf(arr,"%s",buf+1);
			sprintf(brr,"%s",buf+2+strlen(arr));
			sprintf(insert,"insert into passwd_name values (\"%s\",\"%s\",\"offline\")",arr,brr);
			if (sqlite3_exec(db,insert,NULL,NULL,&errmsg)!=SQLITE_OK)
			{
				//有相同用户名注册失败返回WRONG
				bzero(buf,sizeof(buf));
				sprintf(buf,"WRONG USER");
				send(cfd,buf,strlen(buf),0);
				continue;
			}
			//成功返回OK
			bzero(buf,sizeof(buf));
			sprintf(buf,"ENROLL SUCCESS");
			send(cfd,buf,strlen(buf),0);
			continue;
			

		}
		else if (buf[0]=='L')
		{
			sprintf(insert,"select * from passwd_name where name=\"%s\"",buf+1);
			if(sqlite3_get_table(db,insert,&pres,&row,&column,&errmsg)!=SQLITE_OK)
			{
				fprintf(stderr,"line=%d sqlite3_get_table:%s\n",__LINE__,errmsg);
				return NULL;
			}
			if(row==0)
			{
				//说明用户不存在需要注册
				bzero(buf,sizeof(buf));
				sprintf(buf,"NO USER");
				send(cfd,buf,strlen(buf),0);
				bzero(buf,sizeof(buf));
			sqlite3_free_table(pres);
						continue;
			}
			if (strcmp(*(pres+4),buf+2+strlen(buf+1))!=0)
			{
				//说明密码错误
				bzero(buf,sizeof(buf));
				sprintf(buf,"WRONG PASSWD");
				send(cfd,buf,strlen(buf),0);
				bzero(buf,sizeof(buf));
			sqlite3_free_table(pres);
				continue;
			}
			if(strcmp(*(pres+5),"online")==0)
			{
				//已在其他地方登录
				bzero(buf,sizeof(buf));
				sprintf(buf,"Repeat logins");
				send(cfd,buf,strlen(buf),0);
				bzero(buf,sizeof(buf));
			sqlite3_free_table(pres);
				continue;
			}
			//将状态修改为在线
			bzero(insert,sizeof(insert));
			sprintf(insert,"update passwd_name set stat=\"online\" where name=\"%s\"",buf+1);
			if (sqlite3_exec(db,insert,NULL,NULL,&errmsg)!=SQLITE_OK)                    
			{
				fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
				return NULL;
			}
			bzero(buf,sizeof(buf));
			sprintf(buf,"LOGIN SUCCESS");
			send(cfd,buf,strlen(buf),0);
			sqlite3_free_table(pres);
			continue;
		}
		else if(buf[0]=='S')
		{
			sprintf(insert,"select * from dictionary where word=\"%s\"",buf+2+strlen(buf+1));
			if(sqlite3_get_table(db,insert,&pres,&row,&column,&errmsg)!=SQLITE_OK)
			{
				fprintf(stderr,"line=%d sqlite3_get_table:%s\n",__LINE__,errmsg);
				return NULL;
			}
			if(row==0)
			{
				//说明单词不存在
				bzero(buf,sizeof(buf));
				sprintf(buf,"NO WORD");
				send(cfd,buf,strlen(buf),0);
				continue;
			}
			//记录查询时间
			time_t t;
			time(&t);
			struct tm *time_cur;
			time_cur=localtime(&t);
			char time[128]="";
			sprintf(time,"[%d-%02d-%02d %02d-%02d-%02d]",time_cur->tm_year+1900,time_cur->tm_mon+1,time_cur->tm_mday,time_cur->tm_hour,time_cur->tm_min,time_cur->tm_sec);
			bzero(arr,sizeof(arr));
			for(int i=0;i<((row+1)*column);i++)
			{
				sprintf(arr+strlen(arr),"%s",pres[i]);
				arr[strlen(arr)]='*';
				if(i%column==(column-1))
				{
					//插入到历史记录中
					bzero(insert,sizeof(insert));
					if(i>1)
					{
						sprintf(insert,"insert into history values (\"%s\",\"%s\",\"%s\",\"%s\")",buf+1,pres[i-1],pres[i],time);
						if (sqlite3_exec(db,insert,NULL,NULL,&errmsg)!=SQLITE_OK)
						{
							fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
							return NULL;
						}
					}
				}
			}
			//发送给客户端
			send(cfd,arr,strlen(arr),0);
			bzero(arr,sizeof(arr));
			sqlite3_free_table(pres);
			continue;
		}
		else if(buf[0]=='H')
		{
			bzero(insert,sizeof(insert));
			sprintf(insert,"select word, mean, time from history where name=\"%s\"",buf+1);
			if(sqlite3_get_table(db,insert,&pres,&row,&column,&errmsg)!=SQLITE_OK)
			{
				fprintf(stderr,"line=%d sqlite3_get_table:%s\n",__LINE__,errmsg);
				return NULL;
			}
			if(row==0)
			{
				//没有记录
				bzero(buf,sizeof(buf));
				sprintf(buf,"NO HISTORY");
				send(cfd,buf,strlen(buf),0);
				continue;
			}
			bzero(arr,sizeof(arr));
			for(int i=0;i<((row+1)*column);i++)
			{
				sprintf(arr+strlen(arr),"%s",pres[i]);
				arr[strlen(arr)]='*';
			}
			send(cfd,arr,strlen(arr),0);
			bzero(arr,sizeof(arr));
			sqlite3_free_table(pres);
			continue;
		}
		else if (buf[0]=='E')
		{
			if(strlen(buf)>1)
			{
				bzero(insert,sizeof(insert));
				sprintf(insert,"update passwd_name set stat=\"offline\" where name=\"%s\"",buf+1);
				if (sqlite3_exec(db,insert,NULL,NULL,&errmsg)!=SQLITE_OK)
				{
					fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
					return NULL;
				}
				printf("[%s:%d] %s 已退出\n",inet_ntoa(connect_done.sin_addr),ntohs(connect_done.sin_port),buf+1);
				continue;
			}
			printf("[%s:%d] 已退出\n",inet_ntoa(connect_done.sin_addr),ntohs(connect_done.sin_port));
			continue;
		}
		else if(buf[0]=='F')
		{

			if(strlen(buf)>1)
			{
				bzero(insert,sizeof(insert));
				sprintf(insert,"update passwd_name set stat=\"offline\" where name=\"%s\"",buf+1);
				if (sqlite3_exec(db,insert,NULL,NULL,&errmsg)!=SQLITE_OK)
				{
					fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
					return NULL;                                                                                      
				}
				printf("[%s:%d] %s 已退出\n",inet_ntoa(connect_done.sin_addr),ntohs(connect_done.sin_port),buf+1);
				break;
			}
			printf("[%s:%d] 已退出\n",inet_ntoa(connect_done.sin_addr),ntohs(connect_done.sin_port));
			break;
		}

	}
	close(cfd);
	pthread_exit(NULL);
}





sqlite3 * db_create()                      //打开数据库函数
{

	//判断库是否存在,存在直接删除
	if(access("./dict.db",F_OK)==0)
	{
		unlink("./dict.db");

	}
	//打开数据库
	sqlite3 *db;
	if(sqlite3_open("./dict.db",&db)!=SQLITE_OK)
	{
		printf("errcode=%d errmsg=%s\n",sqlite3_errcode(db),sqlite3_errmsg(db));
		return NULL;

	}                                                                            
	//创建存储电子词典文本的表
	char * p="create table if not exists dictionary (word char,mean char)";
	char * errmsg;
	if (sqlite3_exec(db,p,NULL,NULL,&errmsg)!=SQLITE_OK)
	{
		fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
		return NULL;
	}
	//创建存储名字,密码,状态的表,并且把名字设为主键
	 p="create table if not exists passwd_name (name char primary key,passwd char,stat char)"; 
	if (sqlite3_exec(db,p,NULL,NULL,&errmsg)!=SQLITE_OK)
	{
		fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
		return NULL;
	}
	//创建存储名字,单词,单词意思,查询时间的表	
	 p="create table if not exists history (name char,word char,mean char,time char)"; 
	if (sqlite3_exec(db,p,NULL,NULL,&errmsg)!=SQLITE_OK)
	{
		fprintf(stderr,"line=%d sqlite3_exec:%s\n",__LINE__,errmsg);
		return NULL;
	}
	return db;


}
int socket_func()
{
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if (sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(PORT);
	sin.sin_addr.s_addr=inet_addr(IP);
	if (bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("bind");
		return -1;
	}
	if (listen(sfd,128)<0)
	{
		ERR_MSG("listen");
		return -1;
	}
	return sfd;


}
int main(int argc, const char *argv[])
{
	//打开数据库,并且制作表
	sqlite3 * db = db_create();
	//导入电子词典文本
	do_insert(db);
	//创建传参结构体
	struct ifo_client ifo;
	ifo.db=db;
	int sfd=socket_func();
	int cfd;
	struct sockaddr_in connect_done;
	socklen_t len=sizeof(connect_done);
	pthread_t pt_1;
	while(1)
	{
		cfd=accept(sfd,(struct sockaddr*)&connect_done,&len);
		if (cfd<0)
		{
			ERR_MSG("accept");
			return -1;
		}
		ifo.cfd=cfd;
		ifo.cin=connect_done;
		if (pthread_create(&pt_1,NULL,callback,(void*)&ifo)!=0)
		{
			ERR_MSG("pthread_create");
			return -1;
		}
		//分离线程
		pthread_detach(pt_1);
	}
	close(sfd);
	
	return 0;
}

客户端代码:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <math.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <sqlite3.h>
#include <time.h>
#include <stdlib.h>
#include <signal.h>
#define ERR_MSG(msg) {printf("Line=%d\n",__LINE__);perror(msg);}
#define IP "0.0.0.0"
#define PORT 8888
typedef void (*sighandler_t)(int);
char name[64]="";    //全局变量
int sfd;
int deal_func();
int enroll()              //  注册函数
{
	char name_1[64]="";
	char insert[128]="";
	char passwd[64]="";
	char buf[1024]="";
	ssize_t res=0;
	bzero(name_1,sizeof(name_1));
	printf("请输入账户名\n");
	fgets(name_1,sizeof(name_1),stdin);
	name_1[strlen(name_1)-1]=0;
	printf("请输入账户密码\n");
	fgets(passwd,sizeof(passwd),stdin);
	passwd[strlen(passwd)-1]=0;
	//拼接
	insert[0]='N';
	sprintf(insert+1,"%s",name_1);
	insert[1+strlen(name_1)]=0;
	sprintf(insert+2+strlen(name_1),"%s",passwd);
	send(sfd,insert,strlen(name_1)+strlen(passwd)+2,0);
	res=recv(sfd,buf,sizeof(buf),0);
	if(res<0)
	{
		ERR_MSG("recv");
		return -1;
	}
	else if(res==0)
	{
		printf("server offline\n");
		return 0;
	}
	else
	{
		if(strcmp(buf,"WRONG USER")==0)
		{
			printf("\t\tUser already exists\n");
			bzero(insert,sizeof(insert));
			bzero(name_1,sizeof(name_1));
			bzero(passwd,sizeof(passwd));                         
			bzero(buf,sizeof(buf));
			//  名字不对重新输入
			enroll();
		}
		else if(strcmp(buf,"ENROLL SUCCESS")==0)
		{
			printf("\t\tEnroll success\n");
			bzero(name_1,sizeof(name_1));
		}
	}
	


}
int logins_fun(int*flags)                             //登录函数
{
	if (*flags!=0)
	{
		printf("一个终端只能登陆一个\n");
		return 0;
	}
	bzero(name,sizeof(name));
	char insert[128]="";
	char passwd[64]="";
	char buf[1024]="";
	ssize_t res=0;
	printf("请输入账户名\n");
	fgets(name,sizeof(name),stdin);
	name[strlen(name)-1]=0;
	printf("请输入账户密码\n");
	fgets(passwd,sizeof(passwd),stdin);
	passwd[strlen(passwd)-1]=0;

	insert[0]='L';
	sprintf(insert+1,"%s",name);
	insert[1+strlen(name)]=0;
	sprintf(insert+2+strlen(name),"%s",passwd);
	res=send(sfd,insert,strlen(name)+strlen(passwd)+2,0);
	if(res<=0)
	{
		ERR_MSG("send");
		return -1;
	}
	bzero(buf,sizeof(buf));
	res=recv(sfd,buf,sizeof(buf),0);
	if(res<0)
	{                                                          
		ERR_MSG("recv");
		return -1;
	}
	else if(res==0)
	{
		printf("server offline\n");
		return 0;
	}
	else
	{
		if(strcmp(buf,"NO USER")==0)
		{
			bzero(name,sizeof(name));
			printf("Username not exists\n");
R:
			printf("*************************\n");
			printf("*******1.重新登录********\n");
			printf("*******2.重新注册********\n");
			printf("*******3.回主菜单********\n");
			printf("*************************\n");
			int c;
			scanf("%d",&c);
			getchar();
			if(c==1)
			{
				bzero(name,sizeof(name));
				logins_fun(flags);
			}
			else if(c==2)
			{
				bzero(name,sizeof(name));
				enroll();
			}
			else if(c==3)
			{
				bzero(name,sizeof(name));
				return -1;
			}
			else
			{
				printf("输入错误,重新选择\n");
				goto R;
			}
		}
		else if(strcmp(buf,"WRONG PASSWD")==0)
		{
			printf("密码错误,请重新登录\n");
			bzero(name,sizeof(name));
			logins_fun(flags);
		}
		else if(strcmp(buf,"Repeat logins")==0)
		{
			printf("已在其他地方登录\n");
			bzero(name,sizeof(name));
			return -1;
		}
		else if(strcmp(buf,"LOGIN SUCCESS")==0)
		{
			printf("Login success\n");
			*flags=1;
			deal_func();
			return 2;
		}
	}


}
int return_func()                             //退出函数
{
	char insert[128]="";
	char passwd[64]="";
	char buf[1024]="";
	if(strlen(name)==0)                        
	{
		bzero(insert,sizeof(insert));
		insert[0]='F';
		send(sfd,insert,1,0);
		return 0;
	}
	else
	{
		bzero(insert,sizeof(insert));
		insert[0]='F';
		sprintf(insert+1,"%s",name);
		send(sfd,insert,sizeof(insert),0);
		return 0;
	}
	return 0;

}
int search_func()                        //查找函数
{
	char insert[128]="";
	char passwd[64]="";
	char buf[1024]="";
	ssize_t res=0;
	int i=0;
	bzero(insert,sizeof(insert));
	bzero(buf,sizeof(buf));
	insert[0]='S';
	sprintf(insert+1,"%s",name);
	insert[1+strlen(name)]=0;                            
	printf("请输入单词\n");
	fgets(buf,sizeof(buf),stdin);
	buf[strlen(buf)-1]=0;
	sprintf(insert+2+strlen(name),"%s",buf);
	send(sfd,insert,sizeof(insert),0);
	bzero(buf,sizeof(buf));
	res=recv(sfd,buf,sizeof(buf),0);
	if (res<0)
	{
		ERR_MSG("recv");
		return-1;
	}
	if (res==0)
	{
		return -1;
	}
	if (strcmp(buf,"NO WORD")==0)
	{
		printf("没有这个单词,请重新输入\n");
		search_func();
	}
	int count=-1;
	int a=strlen(buf);
	int temp=0;
	for(i=0;i<a;i++)
	{
		if(buf[i]=='*')
		{
			buf[i]=0;
			printf("%s\t",buf+count+1);
			count=i;
			if(temp%2==1)
			{
				putchar(10);
			}
			temp++;
		}

	}


}
int history_func()                       //历史记录函数
{
	char insert[128]="";
	char passwd[64]="";
	char buf[1024]="";
	ssize_t res=0;
	int i=0;
	insert[0]='H';
	sprintf(insert+1,"%s",name);
	send(sfd,insert,strlen(name)+1,0);
	bzero(buf,sizeof(buf));
	res=recv(sfd,buf,sizeof(buf),0); 
	if (res<0)
	{
		ERR_MSG("recv");
		return-1;
	}
	if (strcmp(buf,"NO HISTORY")==0)
	{
		printf("没有历史记录\n");
		return 2;
	}
	int b=-1;
	int a1=strlen(buf);
	int temp1=0;
	for(i=0;i<a1;i++)
	{
		if(buf[i]=='*')
		{
			buf[i]=0;
			printf("%s\t",buf+b+1);
			b=i;
			if(temp1%3==2)
			{
				putchar(10);
			}
			temp1++;
		}

	}
}
int socket_func()                           //连接函数
{
     sfd=socket(AF_INET,SOCK_STREAM,0);
    if (sfd<0)
    {
        ERR_MSG("socket");
        return_func();
    }
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    {
        ERR_MSG("setsockopt");                                                 
        return_func();
    }
    struct sockaddr_in sin;
    sin.sin_family=AF_INET;
    sin.sin_port=htons(PORT);
    sin.sin_addr.s_addr=inet_addr(IP);
    if(connect(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
    {
        ERR_MSG("connect");
        return_func();
    }
}
int deal_func()
{
	while(1)
	{
		system("clear");
		printf("*************************\n");
		printf("**********1.查询*********\n");
		printf("********2.历史记录*******\n");
		printf("********3.回上一级*******\n");
		printf("*************************\n");
		int d;
		scanf("%d",&d);
		getchar();
		switch(d)
		{
		case 1:
			search_func();
			break;
		case 2:
			history_func();
			break;
		case 3:
			return 0;
		default :
			printf("输入错误,请重新输入\n");
			break;
		}
		printf("输入任意字符清屏\n");
		while(getchar()!=10);

	}                                                
}
//信号处理函数
void handler(int sig)
{ 
	return_func();
	exit(-1);
}
int return_only_cur_log(int *flags)           //当前登录退出
{
	if(*flags==0)
	{
		printf("当前无登录\n");
		return 0;
	}
	char insert[128]="";
	char passwd[64]="";
	char buf[1024]="";
	bzero(insert,sizeof(insert));
	insert[0]='E';
	sprintf(insert+1,"%s",name);
	send(sfd,insert,sizeof(insert),0);
	bzero(name,sizeof(name));
	*flags=0;
	return 0;
}
int main(int argc, const char *argv[])
{
	int flags=0;
	sighandler_t s = signal(2, handler);
	if(SIG_ERR == s)
	{
		ERR_MSG("signal");
		return -1;
	}
	socket_func();
	while(1)
	{
		system("clear");
		printf("*************************\n");
		printf("**********1.注册*********\n");
		printf("**********2.登录*********\n");
		printf("****3.仅退出,当前登录***\n");
		printf("**********4.退出*********\n");
		printf("*************************\n");
		printf("请选择功能\n");
		int num;
		scanf("%d",&num);
		getchar();
		switch(num)
		{
		case 1:
			enroll();
			break;
		case 2:
			logins_fun(&flags);
			break;
		case 3:
			return_only_cur_log(&flags); 
			break;
		case 4:
			return_func();
			return 0;
		default :
			printf("输入错误,请重新输入\n");
			break;
		}
		printf("输入任意字符清屏\n");
		while(getchar()!=10);
	}
	close(sfd);
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值