【电信计费系统客户帐单管理】电信账单
程序设计报告
( 2012 / 2013 学年 第 二 学期)
题 目:
专 学 生 班 级 指 导 指 导 日
电信计费系统客户帐单管理 业 姓 名 学 号 教 师 单 位 计算机软件教学中心 期 2013 年05月29日
电信计费系统客户帐单管理
一、 课题内容和要求
客户帐单管理是电信计费系统必备的重要功能模块,主要负责对电信各类客户每月帐单的增加、修改、删除、查询、备份等管理工作。本课题以中国电信企业客户帐单管理模块原型参照,要求基于单链表结构对文件存储的客户帐单数据进行排序、查找、计算、显示等造作。通过此可以,熟练掌握单链表结构、文件读写、函数调用等知识,以及查找、排序典型算法的设计与应用。
二、需求分析
要能提供以下几个基本功能:
(1)用户资料文件与话单文件由程序设计人员预先从键盘上录入,用户资料文件中的数据记录不得少于30条,话单文件中的数据记录不得少于200条,且必须有跨月份、跨年份的通话记录话单。
(2)首先从硬盘读入用户资料和话单记录
(3)对话单进行计费处理,输出费用详单到指定的文件。格式如下: 电话号码 用户姓名 月租费 功能费 市内电话费用 长途电话费用 合计 83470000 Zhang Qiang 25.00 6.00 xxxxx xxxxx xxx 83470001 。。。。。。 。。。。。。
(4)异常话单提示。有的话单可能是错误的,找出这些话单,不参加计费,另存为一个文件。可能的出错话单有:
①一个号码的通话时长大于三天。
②一个号码在一个时间存在两条以上的记录。
功能框架图如图1所示
(1)提供可操作的主菜单:输出个菜单,用于显示以从文件中加载的总客户信息和若干个可选的功能选项。根据客户输入的选项来运行不同的功能,运行不同的函数。
(2)显示所有用户的功能:根据选项可将文本里的用户信息在屏幕上显示出来。 (3)显示所有账单功能:可将文本里的通话账单在屏幕上显示出来。 (4)添加用户功能:可在屏幕上进行直接操作,将用户信息添加到记录里。 (5)添加账单功能:操作同上,可将新的通话记录添加到记录里。
(6)按用户民查找功能:输入记录里的用户民,可以查询与之相关的通话账单记录。
(7)按电话号码查找功能:输入记录里的已有电话号码,可以查找相应的通话账单记录。
(8)输出花费功能:在操控台和文本中,直接输出所有的花费详情。
三、概要设计
1、主要函数流程图如图2所示:
图2:主程序图
图3:操作菜单系统
图4:显示所有信息菜单
图5:插入数据菜单
图6:计算话费函数
图7::判断话费正误函数
1、顾客类(Customer ):
#include
#include
#include //控制输出格式 class Customer //定义一个顾客类 {
protected: char Name[20]; //用户名 char Number[15]; //用户电话号码 char CID; //是否来电显示 public: Customer(char* =" ",char* =" " ,char cid=" "); //定义构造函数 ~Customer(); //定义析构函数 Customer(const Customer & obj); //定义拷贝构造函数 char* GetName(); //获得用户名字符型指针函数 char* GetNumber(); //获得用户号字符型指针函数 char GetCID(); //看用户是否有来电显示 friend istream & operator >> (istream & in , Customer & cus);
//友元重载
输入
friend ostream & operator
};
Customer::Customer(char *na, char *num,char cid) { strcpy( Name , na ); strcpy( Number , num ); CID=cid; }
Customer::~Customer() { }
Customer::Customer( const Customer & obj ) //构造函数的定义 { strcpy( Name , obj.Name ); strcpy( Number , obj.Number ); CID=obj.CID; }
char* Customer::GetName() { return Name; }
char* Customer::GetNumber() { return Number; }
char Customer::GetCID() { return CID; }
istream & operator >> (istream & in,Customer & cus) { in>>cus.Name; //输入用户名 in>>cus.Number; //输入用户号码 in>>cus.CID; //来电业务信息 return in; }
//友元重载
ostream & operator
2、话单类(Bill ):
#include #include #include #include class Bill {
protected: char Dialing[15]; char Dialed[15]; char StartTime[15]; char EndTime[15]; public: Bill(char *dialing=" ",char *dialed=" ",char *stime=" ",char *etime=" ");//定义构造函数
~Bill(); Bill( const Bill & obj); //帐单的拷贝构造函数 char* GetDialing(); //获得主叫号码 char* GetDialed(); //获得被叫号码 char* GetStart(); //获得起始时间 char* GetEnd(); //获得结束时间 int GetDate(); //获得日期 int GetTime(); //获得通话时长 friend int mon_day(int year,int month); //记录不同月份的天数 friend istream & operator >> (istream & in ,Bill & bill); //友元重载输入 friend ostream & operator
Bill::Bill(char *dialing,char *dialed,char *stime,char *etime) { strcpy( Dialing , dialing ); strcpy( Dialed , dialed );
strcpy( StartTime , stime );
strcpy( EndTime , etime );
}
Bill::Bill(const Bill & obj)
{
strcpy( Dialing , obj.Dialing );
strcpy( Dialed , obj.Dialed );
strcpy( StartTime , obj.StartTime );
strcpy( EndTime , obj.EndTime );
}
Bill::~Bill()
{
}
char* Bill::GetDialing()
{
return Dialing;
}
char* Bill::GetDialed()
{
return Dialed;
}
char* Bill::GetStart()
{
return StartTime;
}
char* Bill::GetEnd()
{
return EndTime;
}
istream & operator >> (istream & in,Bill & bill)
{
in>>bill.Dialing;
in>>bill.Dialed;
in>>bill.StartTime;
in>>bill.EndTime;
return in;
}
ostream & operator
{
out
out
out
out
out
out
return out;
}
int Bill::GetDate()
{
int Dat[6];
double date=0;
for(int i=0;i
{
Dat[i]=StartTime[i]-48; //文本文件读入时只能以字符型的形式,计算是转
化成整形
date+=Dat[i]*pow(10,5-i);
}
return (int)date;
}
int Bill::GetTime()
{
return Estimate(StartTime,EndTime);
}
int mon_day(int year,int month) //记录不同月份的天数
{
int day[12]; //d数组12个成员为12个月份
day[0]=31; //给成员一一赋值
if( year%4==0 && year%100!=0 || year%400 == 0 ) //判断是否是闰年
day[1]=29;
else
day[1]=28;
day[2]=31;
day[3]=30;
day[4]=31;
day[5]=30;
day[6]=31;
day[7]=31;
day[8]=30;
day[9]=31;
day[10]=30;
day[11]=31;
return day[month-1]; //返回y 年m 月有多少天
}
int Estimate(char *m,char *n) //判断时间正误函数
{
int a[14],b[14]; //a记录开始时间,b 记录结束时间
for(int i=0;i
{
a[i]=m[i]-48;
b[i]=n[i]-48;
}
int y1,mon1,d1,h1,min1,s1; //开始时间的年月日时分秒
int y2,mon2,d2,h2,min2,s2; //结束时间的年月日时分秒
int x,y; //开始、结束时间的日、时、分统一换算成分钟,对
应着x 、y
y1=a[0]*1000+a[1]*100+a[2]*10+a[3]; //开始时间的年份
mon1=a[4]*10+a[5]; //开始时间的月份
d1=a[6]*10+a[7]; //开始时间的日期
h1=a[8]*10+a[9]; //开始时间的时
min1=a[10]*10+a[11]; //开始时间的分
s1=a[12]*10+a[13]; //开始时间的秒
x=d1*24*60+h1*60+min1;
y2=b[0]*1000+b[1]*100+b[2]*10+b[3]; //结束时间的年份
mon2=b[4]*10+b[5]; //结束时间的月份
d2=b[6]*10+b[7]; //结束时间的日期
h2=b[8]*10+b[9]; //结束时间的时
min2=b[10]*10+b[11]; //结束时间的分
s2=b[12]*10+b[13]; //结束时间的秒
y=d2*24*60+h2*60+min2;
if( mon1>12 ||mon112 ||mon2
h1>23 ||h123 ||h159 ||min1
||min2>59 ||min259 ||s159 ||
s2mon_day(y1,mon1) ||d2>mon_day(y2,mon2))
{
return -1; //如果“月,日,时,分,秒”超出范围,则返回-1
}
else
{
if(y1==y2) //判断同年
{
if(mon1==mon2) //判断月份
{
if( (y-x0) ) //判断通话在三天内
{
钟计算
的,否则错误
误
时间大一月
if(s1s1) //结束秒数大于开始秒数,就是正确 return 1; else { cout
}
else //不同年的情况
{
if(y2-y1==1&&mon1==12&&mon2==1) //不同年只能是去年的12
月,当年的1月结束
{
if(s1
{
return y-x+1+31*24*60;
}
else
{
return y-x+31*24*60;
}
}
else
{
cout
return -1;
}
}
}
}
四、源程序代码.
#include"Customer.h"
#include"Bill.h"
#include
#include
double PrepaidFee=25.0,Cid=6.0,Local=0.1,Toll=0.7;//定义全局变量:月租, 来电显示
费用市内电话单价和长途电话单价
const int Max=2000; //最大数组长度
char ShowMainMeun(); //显示主菜单
char ShowSonMenu(); //显示子菜单
void ShowCustomer(); //显示所有客户信息
void ShowBill(); //显示所有帐单信息
void AddCusInfo(); //添加用户信息
void AddBilInfo(); //添加帐单信息
void SearchCusInfo(); //按照客户查找帐单信息
void SearchNumInfo(); //按照号码查找帐单信息
void Calculate(Customer *cus,Bill *bill ,int m,int n);//
void ShowAll(); //输出所有帐单信息
void ShowWrongMess(Bill *bill,int n);//输出错误帐单的信息
bool Compare(int m,int *p,int n); //如果m 与数组p[n]的任何一个数都不相等,那
么就返回false
int Judge(Bill *bill,int m,Bill bil,int n); //判断bil 帐单是否会发生冲
突
char ShowMainMeun()
{
cout
cout
============================================================"
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
============================================================"
cout
合计"
cout
char choice;
cout
cin>>choice; //用户输入操作选项
return choice; //返回操作选项
}
char ShowSonMenu()
{
cout
============================================================"
cout
cout
cout
cout
|"
cout
|"
cout
============================================================"
char choice;
cout
cin>>choice; //用户输入操作选项
return choice; //返回操作选项
}
void ShowCustomer()
{
cout
Customer Cus[Max];
Bill bill[Max];
ifstream inf1("用户信息.txt");
int count1=0;
while (!inf1.eof())
{
inf1>>Cus[count1];
cout
count1++;
}
ifstream inf2("话单信息.txt");
int count2 =0;
while (!inf2.eof())
{
inf2>>bill[count2];
count2++;
}
inf1.close();
inf2.close();
char point;
while((point=ShowSonMenu())!="0")
{
if(point=="1")
Calculate(SaveCus,SaveBill,2,count2);
else if(point=="2")
ShowWrongMess(bill,count2);
else
cout
}
}
void ShowBill()
{
Customer Cus[Max];
Bill bill[Max];
ifstream inf1("用户信息.txt");
|
int count1=0;
while (!inf1.eof())
{
inf1>>Cus[count1];
count1++;
}
ifstream inf2("话单信息.txt");
int count2 =0;
while (!inf2.eof())
{
inf2>>bill[count2];
cout
count2++;
}
inf1.close();
inf2.close();
char point;
while((point=ShowSonMenu())!="0")
{
if(point=="1")
Calculate(SaveCus,SaveBill,2,count2);
else if(point=="2")
ShowWrongMess(bill,count2);
else
cout
}
}
void ShowAll()
{
Customer Cus[Max];
Bill bill[Max];
ifstream inf1("用户信息.txt");
int count1=0;
while(!inf1.eof()) //录入顾客信息到Customer 类的数组cust 中 {
inf1>>Cus[count1];
count1++; //有m-1个顾客
}
int count2=0;
ifstream inf2("话单信息.txt");
while(!inf2.eof()) //录入账单记录到Bill 类的数组bill 中
{
inf2>>bill[count2];
count2++; //有n 个账单记录
}
inf1.close(); //关闭文件
inf2.close(); //关闭文件
Calculate(Cus,bill,count1,count2);
}
void AddCusInfo()
{
Customer Cus[Max];
int num;
cout
cin>>num;
cout
for( int n=0;n
cin>>Cus[n]; //输入添加的用户名
ofstream outf;
outf.open("用户信息.txt",ios::app); //用app 方式打开文本,能直接在文本尾加入信息
for(n=0;n
outf
cout
outf.close();
}
void AddBilInfo()
{
Bill bill[Max];
int num;
cout
cin>>num;
cout
cout
for( int n=0;n
cin>>bill[n];
ofstream outf;
outf.open("话单信息.txt",ios::app); //用app 方式打开文本,能直接在文本尾加入信息
for(n=0;n
outf
cout
outf.close();
}
void SearchCusInfo()
{
char Name[20];
cout
cin>>Name;
Customer Cus[Max];
Bill bill[Max];
ifstream inf1("用户信息.txt");
int count1=0;
while (!inf1.eof()) //录入顾客信息到Customer 类数组Cus 中 {
inf1>>Cus[count1];
count1++; //记录总共有(count1-1)个顾客 } ifstream inf2("话单信息.txt"); int count2 =0; while (!inf2.eof()) { inf2>>bill[count2]; count2++; //总共有count2条话单 } inf1.close(); inf2.close(); Customer SaveCus[1]; Bill SaveBill[Max]; //保存该用户的话单信息 int i=0,j=0,k=0; cout
cout
}
}
void SearchNumInfo()
{
Customer Cus[Max];
ifstream inf1("用户信息.txt");
int count1=0;
while (!inf1.eof())
{
inf1>>Cus[count1];
count1++; //有count1-1个用户
}
Bill bill[Max];
ifstream inf2("话单信息.txt");
int count2 =0;
while (!inf2.eof())
{
inf2>>bill[count2];
count2++; //有count2条话单记录
}
inf1.close();
inf2.close();
char Number[15];
cout
cin>>Number;
Bill SaveBill[Max];
int i=0,j=0,k=0;
cout
bool p=true;
for(i=0;i
{
if(strcmp(bill[i].GetDialing(),Number)==0)
{
cout
j=false;
SaveBill[k]=bill[i];
k++;
}
}
if(j)
cout
Customer SaveCus[1];
for(i=0;i
{
if(strcmp(Cus[i].GetNumber(),Number)==0)
{
SaveCus[0]=Cus[i];
}
}
char point;
while((point=ShowSonMenu())!="0")
{
if(point=="1")
Calculate(SaveCus,SaveBill,2,count2);
else if(point=="2")
ShowWrongMess(bill,count2);
else
cout
}
}
void Calculate (Customer *cus,Bill *bill,int m,int n)
{
cout
"
ofstream outa("所有信息.txt");
ofstream outw("错误账单.txt");
for(int i=0;i
{
int a[Max]={0}; //int数组,用于记录已计算过的月份
if(cus[i].GetCID()=="Y") //判断用户是否有功能费
Cid=6;
else
Cid=0;
for(int j=0;j
{
if( strcmp( cus[i].GetNumber() , bill[j].GetDialing() ) == 0 ) //
找出cust[i]客户的所有帐单
{
double local=0,toll=0,sum=0; //一个月的市话费、长途费
及总电话费
if(Compare(bill[j].GetDate(),a,n )) //判断在记录a[n]中是
否存在 bill[j].getdate() 记录,若存在,则跳过这条账单信息
continue;
else
{
for(int k=j;k
{
if((bill[j].GetDate()==bill[k].GetDate())&&( strcmp(cus[i].GetNumber(),bill
[k].GetDialing()) == 0 ) ) //若bill[k]的月份与主叫电话号码都相同,则计算这个
月的电话费
{
if(bill[k].GetTime()>=0&&
Judge(bill,n,bill[k],k)==1)
{
if( strlen( bill[k].GetDialed() )>8 ) //
按照市话来看,长度大于8的都算是长途电话
toll += bill[k].GetTime()*Toll;
else
local += bill[k].GetTime()*Local;
a[j]=bill[j].GetDate();
存到a[n]中
}
else
{
outw
}
}
}
sum=PrepaidFee+Cid+toll+local;
//文本输出
outa
outa
outa
outa
outa
outa
}
}
}
}
outa.close();
outw.close();
}
void ShowWrongMess(Bill *bill,int n)
{
bool a=true;
for(int i=0;i
{
if(bill[i].GetTime()==-1||Judge(bill,n,bill[i],i)==-1)
或者不符合判断函数的条件,则为错误账单
{
cout
a=false;
}
}
if(a)
cout
}
bool Compare(int m,int *p,int n)
{
for(int i=0;i
{
//把bill[j]//如果时间不符
if(m==p[i])
return true;
else
continue;
}
return false;
}
int Judge(Bill *bill,int m,Bill bil,int n) //判断bi 的时间是否和其他账
单冲突, 若冲突,则返回-1
{
for(int i=0;i
{
if(i!=n)
{
if(strcmp( bil.GetDialed(),bill[i].GetDialing() )==0)
{
if(strcmp(bil.GetStart(),bill[i].GetStart())==1)
终止时间是否冲突
{
if(strcmp(bil.GetStart(),bill[i].GetEnd())==1)
return 1;
else
return -1;
}
else if(strcmp(bil.GetStart(),bill[i].GetStart())==-1)
终止时间是否冲突
{
if(strcmp(bill[i].GetStart(),bil.GetEnd())==1)
return 1;
else
return -1;
}
else
return -1;
}
}
else continue;
}
return 1;
}
主函数代码:
#include"SourceFile.h"
#include
#include
////
int main()
{
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
***********************************************************"
cout
cout
cout
cout
cout
cout
cout
cout
cout
***********************************************************"
Sleep(3000); //延时
system("cls"); //清屏
int choice;
while((choice=ShowMainMeun())!="0")
{
switch (choice)
{
case "1" :ShowCustomer();
peak;
case "2" :ShowBill();
peak;
case "3" :AddCusInfo();
peak;
case "4" :AddBilInfo();
peak;
case "5" :SearchCusInfo();
peak;
case "6" :SearchNumInfo();
} peak; case "7" :ShowAll(); peak; default: cout
五、测试数据及其结果分析、 欢迎界面
略
主菜单的显示:
1、显示所有用户信息:
图9
2、显示所有账单信息:
图10
图11
3、添加用户信息
图12
4、添加话单信息
图13
5、按客户名查找话单
图14
输入
1
图15
6、按号码查找话单
图16
输入2:
图17
7、输出所有话费信息
图18
8、结束操作
六、调试过程中的问题
(1)输出格式不对,不整齐:用了setw()函数和ios :: left解决。
ostream & operator
{
} out
(2)在文档末尾添加信息时,出错,把原来的信息覆盖了:用ios :: app解决; outf.open(“话费账单.txt ”,ios::app);
(3) 比较时间大小时,因为是字符串,所以常规比较法出错:用strcmp 函数解决。
strcmp(bill[i].GetStart(),bill[i].GetEnd());
(4)在.txt 的使用中要注意使用tab 键,有可能在把文本文件读入内存时出现问题(有时候会和空格混合);在调试的时候,用strlen 来判断读入时是否发生问题。
七、课程设计总结
本次C++试验的题目——“电信计费”,存在着很强的实用意义,和学生成绩管理系统雷同,但是面向的对象范围更加广阔一些。可以极大的优化电信服务业务。因此,我更愿意将此次的实验视作一次历练:将自己已经学过的知识和实践能力结合的尝试。
与C 语言不通的是,C++是面向对象的。(因此其实上面的报告用流程图来展示我们的成果并不是特别的合适。)这就意味着核心不是过程(流程)而是类本身 就是一种包含与关联
先根据题目来确定类(再看实体依赖),这里确定的是顾客类和话单类,实际上这里的顾客和账单可以合并。用类而不用结构体的加大了程序的封装性。进而保密性更加强。
接下来确定程序的相应功能,根据功能写出相应的实现函数,这里对程序语言编写的功底要求很高,其实从附加题就能看的出来,在int Estimate(char *m,char *n) 函数里头用了大量的strcmp()的语句进行字符串比较进而实现是话单时间是否符合要求进而进行筛选。由于本人不熟悉链表的使用,所以造成了繁杂,实际情况可以根据如下图显示逻辑判断:
如果用链表来写这段代码将会简洁很多。
先写出程序的一个大体框架,先把最主要的功能:读入文件和输出文件实现了,再来写其他功能的函数。因为代码比较长,在测试代码的时候会出现很多error ,这是如果没有耐心是不可能做好的:先注释掉一部分代码,一个一个函数地进行修改。
这个程序的运行速度并不是很高,有些逻辑问题处理得并不完善,需要,想要做一个比较合格的程序员,在以后学数据结构以后学会提高代码的运行速度。