项目:一卡通消费记录管理系统
一、 功能需求说明(必须采用结构体和动态链表实现)
【1】消费记录存在文件fee.txt中,格式如下:
每一条记录包括一个消费的交易日期、入账日期、交易额、交易后余额、交易类型、交易次数、交易地点。示例如下
【2】查询搜索功能
(1)用户能够查询自己的一卡通固定时段消费情况:可以按月、学期、学年查询
(2) 用户可以自定义一个时间段进行查询自己的消费情况
(3)用户可以按交易金额查找消费记录
(4)管理员可查询任一用户的消费记录,可按照固定时段、自定义时段、金额方式、已删除记录查询
【3】排序功能:按消费记录的交易日期、金额、次数中一个进行(升序或降序)排序。
【4】数据录入:管理员能录入某个用户的一卡通消费情况,可以一次完成若干条记录的输入。
【5】显示功能:可进行全部消费记录的显示,也可按照查询结果进行显示。
【6】数据变更:管理员可以更改或删除用户的某条消费记录,但删除的信息要保存在另外一个文件fee_undel.dat。
【7】统计功能:
(1)用户可统计自己在一个自定义时间段里的消费总金额情况。
(2)管理员可统计各个用户在一个自定义时间段里的消费总金额情况。
【8】用户和管理员的权限要分开。
另外需要应提供一个界面来调用各个功能,调用界面和各个功能的操作界面应尽可能清晰美观。
经历感受
1、由于是第一次写项目,要打出那么冗杂的代码并且调试对我来说难度还是很大的,刚开始最困难的就是怎么去构建一个大框架来实现不同函数的功能,比如如何将文件里用户的数据跟自己设计的系统联系起来等。
2、回想一下写代码的时间大概用了两天多吧,其实在dev上调试效率还是挺高的,不过因为写代码的时候粗心,指针用错把数组的地址改了,而且那个地方很难发现折腾一下午才意外发现,调试的时候还遇到了很多问题,数组开小,排序时返回的头地址不对,结束条件不恰当等我都被卡了很久。虽然遇到的问题多,但是收获不少,调试能力提升了很多。
3、这次课设最难的是用TC2.0这种祖师爷编译器来运行,还要在里面生成图形库来提供一个界面来调用各个功能,最无语的是TC是不能使用中文的,硬是把dev里面写好的代码的输出语句中的中文全部翻译成英文。在把dev里面调试好的代码搬到tc运行,结果在录入数据这一步就卡住了,运行不了,把高级编译器里能成功运行的代码搬到tc上运行没想到还有那么多问题,用了整整一天才改好。
4、最后在tc里面设计界面,收获挺大,学到了很多图形设计的函数,最坑的是怎么生成图形库,你可能因为电脑不兼容根本不能在终端那里生成obj目标文件,查了网上的资料,基本都是那几步,但就是生成不了,原因是tc是32位的,Win10系统是不能运行的,后来才想到在tc的dos shell窗口操作,调用initgraph()函数前还要加上registerbgidriver(EGAVGA_driver);语句才能生成图形库。
##踩坑后总结的tc2.0图形库生成的步骤:
1、打开tc2.0,在dos shell窗口下进行。
2、在C:\TC子目录下输入命令:BGIOBJ EGAVGA。
(此命令将驱动程序EGAVGA.BGI转换成EGAVGA.OBJ的目标文件。)
3、将OBJ文件剪切到C:\TC子目录中。
4、在C:\TC子目录下输入命令:TLIB LIB\GRAPHICS.LIB+EGAVGA
(此命令的意思是将EGAVGA.OBJ的目标模块装到GRAPHICS.LIB库文件中。)
5、在程序中initgraph()函数调用之前加上一句: registerbgidriver(EGAVGA_driver);
(该函数告诉连接程序在连接时把EGAVGA的驱动程序装入到用户的执行程序中。)
TC2.0一卡通消费记录系统运行界面
1、建立了8个文档,分别是三个用户的消费记录数据和删除记录数据,另外两个文档保存用户的用户名和密码和管理员的账号和密码。
2、进入登录界面
录入用户和管理员的用户名和密码,在登录的时候区分出身份。
3、主菜单
1是查询消费记录,2是排序功能,3是统计一个时间段的总消费金额,4是录入用户数据(需录入用户的消费记录数据才能进行1,2,3,5的功能),5是删除更新功能。
4、查询功能菜单
5、查询已删除的记录
6、删除界面
7、统计界面
还有其他界面就不一 一上传了。
以下是没加界面在DEV上运行的代码
//注:个人编写,请勿抄袭
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <windows.h>
#define LEN sizeof(struct node)
char user_name[3][20],user_password[3][20];
char boss_name[20],boss_password[20];
char name[20],password[20];int i1,t,m,f=0;//t,m为用户编号的标记,f
struct node
{
long int deal_date,in_date;int times;//交易日期,入账日期,交易次数
float money,remain;//交易金额,交易后余额
char type[20],place[20];//交易类型,交易地点
struct node *next;
};
//------------------------------------------------------------------------------------------//
void Printf_node(struct node *head);void update_data(struct node *head);
void Fprintf(FILE*fp,struct node *head1);int Count_data(struct node *head);
struct node *read_data();struct node *read_delete_data();void gotoxy(int x,int y);
struct node *delete_node_function(struct node *head,struct node *head1);
int judge_boss();int judge_user();int login();int menu();
int search_function(struct node *head,struct node *head1);int search_menu1();int search_menu();
struct node *sort_function(struct node *head);int sort_menu();
//以上为各函数声明
void main()
{
struct node *head=NULL,*head1=NULL;int num1,num2,num3=1;
login();if(t==-1)exit(0);
system("cls");
while(1)
{
num1=menu();
switch(num1)
{
case 1:
search_function(head,head1);break;
case 2:
head=sort_function(head);
if(f==0)Printf_node(head);
else system("cls");
break;
case 3:
Count_data(head);break;
case 4:
head=read_data();
head1=read_delete_data();
printf("数据录入成功!");break;
case 5:
if(head==NULL){printf("消费记录为空,未录入数据");break;}
printf("消费记录为:\n");
Printf_node(head);
while(num3)
{
head=delete_node_function(head,head1);
printf("继续删除?输入1继续、输入0结束:");
scanf("%d",&num3);
head1=read_delete_data();
}
update_data(head);break;
case 0:
exit (0);break;
}
}
}
void gotoxy(int x,int y)//x为列坐标,y为行坐标
{
COORD pos = {x,y};
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOut, pos);
}
int login()
{
int judge_boss();
int judge_user();
FILE*fp;int j;
fp=fopen("D:\\一卡通\\user.txt","r");//文件路径可自行更改
for(i1=0;i1<3;i1++)fscanf(fp,"%s%s",&user_name[i1],&user_password[i1]);
fp=fopen("D:\\一卡通\\boss.txt","r");
fscanf(fp,"%s%s",boss_name,boss_password);fclose(fp);
for(j=0;j<5;j++)
{
printf("|-------------------------------|\n");
printf("|用户名: |\n");
printf("|-------------------------------|\n");
printf("|密码 : |\n");
printf("|-------------------------------|\n");
gotoxy(8,1);
scanf("%s",name);
gotoxy(8,3);
scanf("%s",password);
if(judge_boss()&&judge_user())
{
printf("用户名或密码错误,你还有%d次机会\n",4-j);
memset(name,'\0',sizeof(name));//重置name
memset(password,'\0',sizeof(password));//重置password
}
if(judge_boss()==0)
{printf("登录成功,欢迎管理员\n");t=0;return t;break;}
if(judge_user()==0)
{printf("登录成功,欢迎用户\n");t=i1+1;return t;break;}
}
t=-1;return t;
}//登录后可验证身份,用户编号为1、2、3,管理员编号为0,登陆失败后编号为-1。
int judge_boss()//该函数判断是否为管理员身份
{
if((!strcmp(name,boss_name))&&(!strcmp(password,boss_password)))
return 0;
return 1;
}
int judge_user()//该函数判断是否为用户身份
{
int flag;
for(i1=0;i1<3;i1++)
{
if(strcmp(name,user_name[i1])&&strcmp(password,user_password[i1]))
flag=1;
else{flag=0;break;
}
}
return flag;
}
int menu()//主菜单
{
int num;
printf("---------------------------------------------------------------\n");
printf("***************************************************************\n");
printf("1---查询 2---排序 3---统计 4---录入 5---更新 0---退出\n");
printf("***************************************************************\n");
printf("---------------------------------------------------------------\n");
while(1)
{
printf("请输入想要执行的功能(数字):");
scanf("%d",&num);
if(num<6&&num>=0){system("cls");return num;}
printf("输出错误!\n");
}
}
int search_menu()//查询菜单
{
int num;
printf("---------------------------------------------------------------\n");
printf("***************************************************************\n");
printf(" 1--按固定时段查询 2--按日期区间段查询 \n");
printf(" 3--按交易金额查询 4--已删除记录查询(管理员才有此项功能)\n");
printf(" 0--返回上一层 \n");
printf("***************************************************************\n");
printf("---------------------------------------------------------------\n");
while(1)
{
printf("请输入想要执行的功能(数字):");
scanf("%d",&num);
if(num<5&&num>=0)return num;
printf("输出错误!\n");
}
}
int search_menu1()//固定时间段查询菜单
{
int num;
printf("---------------------------------------------------------------\n");
printf("***************************************************************\n");
printf(" 1---------------按年查询 2---------------按月查询 \n");
printf(" 3---------------按日查询 0---------------返回上一层\n");
printf("***************************************************************\n");
printf("---------------------------------------------------------------\n");
while(1)
{
printf("请输入想要执行的功能(数字):");
scanf("%d",&num);
if(num<4&&num>=0)return num;
printf("输出错误!\n");
}
}
int search_function(struct node *head,struct node *head1)//该函数实现查询功能
{
void Printf_node(struct node *head);
struct node *p;int year,month,day,k=0,num1,num2;
long int date1,date2;float money1;
p=head;
if(p==NULL){printf("消费记录为空,未录入数据");return 0;}//如果头指针为空指针则未录入数据
while(1)
{
num1=search_menu();if(num1==0){system("cls");return 0;}
if(num1==1){system("cls");num2=search_menu1();}
if(num2!=0)break;
else system("cls");
}
switch(num1)
{
case 1:
switch(num2)
{
case 1:printf("请输入要查询的年份:");scanf("%d",&year);system("cls");printf("消费记录:\n");break;
case 2:printf("请输入要查询的月份:");scanf("%d",&month);system("cls");printf("消费记录:\n");break;
case 3:printf("请输入要查询的日份:");scanf("%d",&day);system("cls");printf("消费记录:\n");break;
}break;
case 2:
printf("请输入日期区间段,格式为 始-末:");
while(1)
{
scanf("%ld-%ld",&date1,&date2);
if(date1<10000000||date2<10000000){printf("输入格式错误");continue;}
break;
}
system("cls");printf("消费记录:\n");break;
case 3:
printf("请输入要查询的交易金额:");
scanf("%f",&money1);system("cls");printf("消费记录:\n");break;
case 4://管理员专业功能,查看用户删除的消费记录
if(t!=0)
{k=1;
printf("-----------------------");
printf("非管理员无法使用此功能!");break;
}
else
{k=1;system("cls");printf("消费记录有:\n");Printf_node(head1);break;}
}
while(p)
{
switch(num1)
{
case 1://按固定时段查询
switch(num2)
{
case 1://按年查询
if(p->deal_date/10000==year)
{k=1;
printf("%ld\t%ld\t%7.2f\t%7.2f\t%s\t%d\t%s\n",p->deal_date,p->in_date,p->money,p->remain,p->type,p->times,p->place);
}
break;
case 2://按月查询
if((p->deal_date/100)%100==month)
{k=1;
printf("%ld\t%ld\t%7.2f\t%7.2f\t%s\t%d\t%s\n",p->deal_date,p->in_date,p->money,p->remain,p->type,p->times,p->place);
}
break;
case 3://按日查询
if(p->deal_date%100==day)
{k=1;
printf("%ld\t%ld\t%7.2f\t%7.2f\t%s\t%d\t%s\n",p->deal_date,p->in_date,p->money,p->remain,p->type,p->times,p->place);
}
break;
}
break;
case 2://按自定义时段查询
if(p->deal_date>=date1&&p->deal_date<=date2)
{k=1,printf("%ld\t%ld\t%7.2f\t%7.2f\t%s\t%d\t%s\n",p->deal_date,p->in_date,p->money,p->remain,p->type,p->times,p->place);}
break;
case 3://按消费金额查询
if(p->money==money1)
{k=1,printf("%ld\t%ld\t%7.2f\t%7.2f\t%s\t%d\t%s\n",p->deal_date,p->in_date,p->money,p->remain,p->type,p->times,p->place);}
break;
}
p=p->next;
}
if(k==0){printf("----------------\n");printf("无此消费记录\n");}return 0;
}
int sort_menu()//排序菜单
{
int num;
printf("---------------------------------------------------------------\n");
printf("***************************************************************\n");
printf(" 1---------------交易日期 2---------------交易金额 \n");
printf(" 3---------------交易次数 0---------------返回上一层\n");
printf("***************************************************************\n");
printf("---------------------------------------------------------------\n");
while(1)
{
printf("请输入想要执行的功能(数字):");
scanf("%d",&num);
if(num<4&&num>=0)return num;
printf("输出错误!\n");
}
}
struct node *sort_function(struct node *head)//该函数实现排序功能
{
int sort_menu();
int num;float a,b;
struct node *p1=head,*pre1,*p2,*pre2,*pm;
if(p1==NULL){printf("消费记录为空,未录入数据");return head;}
num=sort_menu();
for(p1=head; p1->next; p1=p1->next)
{
for(p2=p1->next; p2; p2=p2->next)
{
switch(num)
{
case 1:
a=(float)p1->deal_date;
b=(float)p2->deal_date;
break;
case 2:
a=p1->money;
b=p2->money;
break;
case 3:
a=(float)p1->times;
b=(float)p2->times;
break;
case 0:
f=1;
return head;
}
for(pre1=head; pre1; pre1=pre1->next)
if(pre1->next==p1)break;//寻找p1的前驱结点
for(pre2=head; pre2; pre2=pre2->next)
if(pre2->next==p2)break;//寻找p2的前驱结点
if(p1==head)
{
if(p1->next==p2&&a>b)
{
p1->next=p2->next;
p2->next=p1;
pm=p2;
p2=p1;
p1=pm;
head=p1;
}
else if(a>b)
{
pm=p1->next;
p1->next=p2->next;
pre2->next=p1;
p2->next=pm;
pm=p2;
p2=p1;
p1=pm;
head=p1;
}
}
else
{
if(p1->next==p2&&a>b)
{
p1->next=p2->next;
pre1->next=p2;
p2->next=p1;
pm=p1;
p1=p2;
p2=pm;
}
else if(a>b)
{
pm=p1->next;
p1->next=p2->next;
pre2->next=p1;
p2->next=pm;
pre1->next=p2;
pm=p1;
p1=p2;
p2=pm;
}
}
}
}
system("cls");
printf("排序成功!消费记录为:\n");
return head;
}
int Count_data(struct node *head)//该函数实现统计功能
{
long int a,b;
float total=0.0;//total为总消费金额
struct node *p=head;
if(p==NULL){printf("消费记录为空,未录入数据");return 0;}
printf("请输入时间段,格式为:始-末:");
while(1)
{
scanf("%ld-%ld",&a,&b);
if(a>b){printf("输出错误!");}
if(a<=b)break;
}
while(p)
{
if(p->deal_date>=a&&p->deal_date<=b)
total=total+p->money;
p=p->next;
}
printf("该段时间总消费金额为:%7.2f元",total);
}
struct node *read_data()//该函数实现录入消费记录
{
struct node *head,*p1,*p2;int n=0;FILE *fp;
char filename[50],p[50],s[]={"_fee.txt"},s1[]={"D:\\一卡通\\"};//文件路径可自行更改
if(t==1||t==2||t==3)//判断是否为用户,再打开该用户的消费记录文件
{
strcpy(p,&user_name[t-1][0]);
strcpy(filename,strcat(s1,strcat(p,s)));
}
if(t==0)//判断是否为管理员,再打开需要录入的用户的消费记录文件
{
printf("你为管理员,请输入你选择录入的用户编号:");
scanf("%d",&m);strcpy(p,&user_name[m-1][0]);
strcpy(filename,strcat(s1,strcat(p,s)));
}
fp=fopen(filename,"r");
head=NULL;
p1=p2=(struct node *)malloc(LEN);
fscanf(fp,"%ld %ld %f %f %s %d %s",&p1->deal_date,&p1->in_date,&p1->money,&p1->remain,p1->type,&p1->times,p1->place);
while(p1->deal_date!=0)
{
n++;
if(n==1)head=p1;
else
p2->next=p1;
p2=p1;
p1=(struct node *)malloc(LEN);
fscanf(fp,"%ld %ld %f %f %s %d %s",&p1->deal_date,&p1->in_date,&p1->money,&p1->remain,p1->type,&p1->times,p1->place);
}
p2->next=NULL;fclose(fp);
return head;
}
struct node *read_delete_data()//该函数实现录入删除的消费记录
{
struct node *head1,*p3,*p4;int n=0;FILE *fp1;
char filename[50],p[50],b[]={"_fee_del.txt"},b1[]={"D:\\一卡通\\"};//文件路径可自行更改
if(t==1||t==2||t==3)//判断是否为用户,再打开该用户的消费记录文件
{
strcpy(p,&user_name[t-1][0]);
strcpy(filename,strcat(b1,strcat(p,b)));
}
if(t==0)//判断是否为管理员,再打开需要录入的用户的消费记录文件
{
strcpy(p,&user_name[m-1][0]);
strcpy(filename,strcat(b1,strcat(p,b)));
}
fp1=fopen(filename,"r");
head1=NULL;
p3=p4=(struct node *)malloc(LEN);
fscanf(fp1,"%ld %ld %f %f %s %d %s",&p3->deal_date,&p3->in_date,&p3->money,&p3->remain,p3->type,&p3->times,p3->place);
while(p3->deal_date!=0)
{
n++;
if(n==1)head1=p3;
else
p4->next=p3;
p4=p3;
p3=(struct node *)malloc(LEN);
fscanf(fp1,"%ld %ld %f %f %s %d %s",&p3->deal_date,&p3->in_date,&p3->money,&p3->remain,p3->type,&p3->times,p3->place);
}
p4->next=NULL;fclose(fp1);
return head1;
}
void Printf_node(struct node *head)//该函数可以输出单向链表
{
struct node *p;
p=head;
while(p)
{
printf("%ld\t%ld\t%7.2f\t%7.2f\t%s\t%d\t%s\n",p->deal_date,p->in_date,p->money,p->remain,p->type,p->times,p->place);
p=p->next;
}
}
void Fprintf_node(FILE *fp,struct node *head1)//该函数向文件输出单向链表
{
struct node *p;
p=head1;
while(p)
{
fprintf(fp,"%ld\t%ld\t%7.2f\t%7.2f\t%s\t%d\t%s\n",p->deal_date,p->in_date,p->money,p->remain,p->type,p->times,p->place);
p=p->next;
}
}
struct node *delete_node_function(struct node *head,struct node *head1)//该函数实现删除更新功能
{
void Fprintf_node(FILE *fp,struct node *head1);
struct node *pt=head,*p1=head1,*pre;
long int a,b;float c,d;char e[20],g[20];int f;
FILE *fp;
char filename[50],p[50],a1[]={"_fee_del.txt"},s[]={"D:\\一卡通\\"};//文件路径可自行更改
if(t==1||t==2||t==3)
{
strcpy(p,&user_name[t-1][0]);
strcpy(filename,strcat(s,strcat(p,a1)));
}
if(t==0)
{
strcpy(p,&user_name[m-1][0]);
strcpy(filename,strcat(s,strcat(p,a1)));
}
fp=fopen(filename,"w");
printf("---------------------请输入你要删除的消费记录-------------------\n");
printf("交易日期 入账日期 交易金额 交易后余额 交易类型 交易次数 交易地点\n");
scanf("%ld%ld%f%f%s%d%s",&a,&b,&c,&d,e,&f,g);
while(pt)
{
if((head->deal_date==a)&&(head->in_date==b)&&(head->money==c)&&(head->remain==d)&&(head->times==f))
{
if((!strcmp(head->type,e))&&(!strcmp(head->place,g)))
{
head=head->next;free(pt);
Fprintf_node(fp,head1);
fprintf(fp,"%ld\t%ld\t%7.2f\t%7.2f\t%s\t%d\t%s\n0",a,b,c,d,e,f,g);
fclose(fp);
return head;
}
}
if((pt->deal_date==a)&&(pt->in_date==b)&&(pt->money==c)&&(pt->remain==d)&&(pt->times==f))
{
if((!strcmp(pt->type,e))&&(!strcmp(pt->place,g)))
{
for(pre=head;pre;pre=pre->next)
if(pre->next==pt)break;
pre->next=pt->next;free(pt);
Fprintf_node(fp,head1);
fprintf(fp,"%ld\t%ld\t%7.2f\t%7.2f\t%s\t%d\t%s\n0",a,b,c,d,e,f,g);
fclose(fp);
return head;
}
}
pt=pt->next;
}
Fprintf_node(fp,head1);
fprintf(fp,"0");
system("cls");
printf("无此消费记录!");fclose(fp);
return head;
}
void update_data(struct node *head)//该函数实现更新功能
{
FILE *fp;struct node *p=head;
char filename[50],p1[50],a[]={"_fee.txt"},s[]={"D:\\一卡通\\"};//文件路径可自行更改
if(t==1||t==2||t==3)
{
strcpy(p1,&user_name[t-1][0]);
strcpy(filename,strcat(s,strcat(p1,a)));
}
if(t==0)
{
strcpy(p1,&user_name[m-1][0]);
strcpy(filename,strcat(s,strcat(p1,a)));
}
fp=fopen(filename,"w");
while(p)
{
fprintf(fp,"%ld\t%ld\t%7.2f\t%7.2f\t%s\t%d\t%s\n",p->deal_date,p->in_date,p->money,p->remain,p->type,p->times,p->place);
p=p->next;
}
fprintf(fp,"0");fclose(fp);
printf("数据更新成功!");
}