武汉理工大学计算机基础与编程综合实验——网吧计费管理系统第二个版本


前言

第4次:改写第3次实验内容
文件 <-> 链表<-> 系统的操作
第5-7次:在第4次实验基础上,完成全部功能,是基于链表的

系统需求分析和注意事项

详情可参考前一篇文章:
传送门:武汉理工大学计算机基础与编程综合实验——网吧计费管理系统第一个版本


以下是本篇文章正文内容,下面案例可供参考

具体实现(链表基本操作)

model.h

定义卡信息链表结点类型。

struct CardNode
{
    
    
	Card data;//结点数据
	struct CardNode* next;//指向下个结点的指针
}

AMS.cpp

定义链表表头指针。

CardNode* pCardNodeHead=NULL;
pCardNodeHead=CardListInit(CARDPATH);

cardList.cpp

CardListInit()

为了对比容器 vector 和链表 list 的操作,我们使用 cardList.cpp 和 cardList.h 文件来存放对卡 card 的基本操作。

CardNode* CardListInit(const string cardFilename)
{
    
    
ifstream cardfile(cardFilename);
 CardNode *pCardNode, *pCardNodeHead, *pCardNodeTail;
 Card card;
 if(!cardfile.is_open())
 {
    
    
 return NULL;
 }
pCardNodeHead = NULL;
pCardNodeTail = NULL;
 while(1)
 {
    
    
 cardfile.read((char*)&card, sizeof(Card));
 if(cardfile.eof())
 {
    
    
 break;
 }
 pCardNode = new CardNode;
 pCardNode->data = card;
 if(pCardNodeHead == NULL)
 {
    
    
 pCardNodeHead = pCardNode;
 pCardNodeTail = pCardNode;
 }
 else
 {
    
    
 pCardNodeTail->next = pCardNode;
 pCardNodeTail = pCardNode;
 }
 }
 pCardNodeTail->next = NULL;
 cardfile.close();
return pCardNodeHead;
}

addNewCard()

int addNewCard(string strNo, string strPwd, float fBalance, CardNode**
ppCardNodeHead)
{
    
    
int nCardIndex=0;
 if(cardIsExist(strNo, nCardIndex, *ppCardNodeHead) != NULL)
 {
    
    
 return FINDCARD;
 }
 Card card;
 strcpy(card.aName, strNo.c_str());
 strcpy(card.aPwd, strPwd.c_str());
 card.fBalance = fBalance;
// 添加新卡时,累计金额等于开卡金额
 card.fTotalUse = card.fBalance;
card.nStatus = UNUSE; // 卡状态
card.nUseCount = 0; // 使用次数
// 开卡时间,最后使用时间都默认为当前时间
card.tStart = card.tLast = time(NULL);
CardNode* pCardNode = new CardNode;
pCardNode->data = card;
CardNode *pCardNodeHead=*ppCardNodeHead;
CardNode *pCardNodeTail=*ppCardNodeHead;
if(pCardNodeHead == NULL)
{
    
    
// 第一张卡,通过ppCardNodeHead将表态指针返回
 pCardNodeHead = pCardNode;
 pCardNodeTail = pCardNode;
*ppCardNodeHead = pCardNode;
}
else
 {
    
    
 pCardNodeTail = pCardNodeHead;
 while(pCardNodeTail->next != NULL)
 pCardNodeTail = pCardNodeTail->next;
 pCardNodeTail->next = pCardNode;
pCardNodeTail = pCardNode;
 }
 pCardNodeTail->next = NULL;
 saveCard(&card, CARDPATH);
 return SUCCESS;
}

displayCard()

void displayCard(CardNode* pCardNodeHead)
{
    
    
if(pCardNodeHead == NULL)
 {
    
    
 cout << endl << endl <<"一张上机卡都没有!" << endl << endl;
 return;
 }
 cout << "卡号\t状态\t余额\t累计使用\t使用次数\t上次使用时间" << endl;
 CardNode* pCur = pCardNodeHead;
 while(pCur != NULL)
 {
    
    
 char aLastTime[TIMELENGTH] = {
    
    0};
 //char* aLastTime=NULL;
 timeToString((pCur->data).tLast, aLastTime);
 cout << pCur->data.aName << "\t" ;
 if(pCur->data.nStatus == USING)
 cout << "上机\t";
 else if(pCur->data.nStatus == UNUSE)
 cout << "未上机\t";
 else if(pCur->data.nStatus == INVALID)
 cout << "注销\t";
 else
 cout << "错误\t";
 cout << pCur->data.fBalance << "\t";
 cout << pCur->data.fTotalUse << "\t\t" << pCur->data.nUseCount <<
"\t\t" << aLastTime << endl;
 pCur = pCur->next;
 }
}

cardIsExist()

Card* cardIsExist(string strNo, int &nCardIndex, CardNode* const pCardNodeHead)
{
    
    
 CardNode *pCardNode = pCardNodeHead;
 nCardIndex = 0;
 while(pCardNode != NULL)
 {
    
    
 if(strcmp(pCardNode->data.aName,strNo.c_str())==0)
 {
    
    
 return &(pCardNode->data);
 }
 pCardNode = pCardNode->next;
 nCardIndex++;
 }
return NULL;
}

service.cpp

addCard()

void addCard(CardNode** ppCardNodeHead)
{
    
    
string strNo;
 string strPwd;
 float fBalance;
 if(inputNoPwdBalance(strNo, strPwd, fBalance))
 {
    
    
 int nResult = addNewCard(strNo, strPwd, fBalance, ppCardNodeHead);
 switch(nResult)
 {
    
    
 case FINDCARD:
 {
    
    
 cout << endl << endl << "卡【" << strNo << "】已经存在,添加新
卡失败!" << endl << endl;
 break;
 }
 case SUCCESS:
 {
    
    
 cout << endl << endl << "添加新卡成功!" << endl << endl;
 cout << "新卡卡号:" << strNo << endl;
 cout << "新卡金额:" << fBalance << endl;
 break;
 }
 default:
 {
    
    
 cout << endl << endl << "系统错误!" << endl << endl;
 break;
 }
 }
 }
 else
 {
    
    
 cout << endl << endl << "输入的【卡号,密码,金额】等信息格式不符号要求,
添加卡失败!" << endl << endl;
 }
}
//void queryCard(vector<Card> &vec)
void queryCard(CardNode* const pCardNodeHead)
{
    
    
displayCard(pCardNodeHead);
}

clearData()

由于链表节点是动态生成的,我们在退出系统时,应该删除动态生成的节点。

void clearData(CardNode* pCardNodeHead)
{
    
    
CardNode *pCardNode;
 while(pCardNodeHead != NULL)
 {
    
    
 pCardNode = pCardNodeHead;
 pCardNodeHead = pCardNodeHead->next;
 delete pCardNode;
 }
}

具体实现(上机下机功能)

model.h

//计费信息结构体,保留每次上机的信息
struct Billing
{
    
    
	char aCardName[18];//卡号
	time_t tStart;//上机时间
	time_t tEnd;//下机时间
	float fAmount;//消费金额
	int nStatus;//消费状态,0-未结算,1-已结算
	int nDel;//删除标识,0-未删除,1-已删除
}

//计费信息结点
struct BillingNode
{
    
    
	Billing data;
	struct BillingNode *next;
}

//上机信息结构体
struct LogonInfo
{
    
    
	char aCardName[18];//上机卡号
	time_t tLogon;//上机时间
	float fBalance;//上机时卡余额
}

//下机信息结构体
struct SettleInfo
{
    
    
	char aCardName[18];//卡号
	time_t tStart;//上机时间
	time_t tEnd;//下机时间
	float fAmount;//消费金额
	float fBalance;//余额
}

AMS.cpp

系统启动时,首先要初始化计费记录的链表,并将链表的表头存储起来提供给其它需要使用计费记录的模块使用。BILLINGPATH 是定义计费记录文件的常量。

BillingNode* pBillingNodeHead=NULL;
pBillingNodeHead=BillingListInit(BILLINGPATH);

billList.cpp

BillListInit()

BillingNode* BillListInit(const string billingFilename)
{
    
    
 ifstream billingfile(billingFilename);
 BillingNode *pBillingNode, *pBillingNodeHead, *pBillingNodeTail;
 Billing billing;
 if(!billingfile.is_open())
 {
    
    
 return NULL;
 }
pBillingNodeHead = NULL;
pBillingNodeTail = NULL;
 while(1)
 {
    
    
 billingfile.read((char*)&billing, sizeof(Billing));
 if(billingfile.eof())
 {
    
    
 break;
 }
 pBillingNode = new BillingNode;
 pBillingNode->data = billing;
 if(pBillingNodeHead == NULL)
 {
    
    
 pBillingNodeHead = pBillingNode;
 pBillingNodeTail = pBillingNode;
 }
 else
 {
    
    
 pBillingNodeTail->next = pBillingNode;
 pBillingNodeTail = pBillingNode;
 }
 }
 pBillingNodeTail->next = NULL;
 billingfile.close();
 return pBillingNodeHead;
}

shangJi()

输入上机的卡号和密码,如果正确,则形成一条上机的记录,①该记录作为一个节点插入到上机记录的链表的末尾,②同时也要将这条记录添加在计费信息记录文件 billing.dat的末尾,③还要修改卡在文件和链表中的状态,即变为上机。
如果不正确,则给出具体的错误提示信息。

void shangJi(CardNode* pCardNodeHead, BillingNode **ppBillingNodeHead)
{
    
    
 string strNo;
 string strPwd;
 LogonInfo* pInfo = new LogonInfo; // 上机信息,用于显示
 char aTime[TIMELENGTH] = {
    
    0}; // 上机时间,用于显示
 if(inputNoPwd(strNo, strPwd))
 {
    
    
 cout << endl << "----------上机信息----------" << endl ;
 // 根据上机结果,提示不同信息
 int nResult = logon(strNo, strPwd, pInfo, pCardNodeHead,
ppBillingNodeHead);
 switch(nResult)
 {
    
    
 case NOFINDCARD:
 case NOMATCH:
 {
    
    
 cout << endl << endl << "卡不存在,或卡信息不对,不能上机!" <<
endl << endl;
 break;
 }
 case SUCCESS:
 {
    
    
 timeToString(pInfo->tLogon, aTime);
 cout << endl;
 cout << "卡号: \t" << strNo << endl;
 cout << "余额: \t" << pInfo->fBalance << endl;
 cout << "上机时间:\t" << aTime << endl;
 break;
 }
 case USING:
 {
    
    
 cout << endl << "该卡正在使用,不能重复上机!" << endl;
 break;
 }
 case INVALID:
 {
    
    
 cout << endl << "该卡已注销,不能上机!" << endl;
 break;
 }
 case ENOUGHMONEY:
 {
    
    
 cout << endl << "卡余额为0,不能上机!" << endl;
 break;
 }
 default:
 {
    
    
 break;
 }
 }
 }
 else
 {
    
    
 cout << endl << endl << "卡号或者密码格式不正确,上机失败!" << endl <<
endl;
 }
 delete pInfo;
}

Logon()

int logon(string strNo, string strPwd, LogonInfo* pInfo, CardNode* pCardNodeHead,
BillingNode **ppBillingNodeHead)
{
    
    
 Billing billing; // 计费信息
 int nCardIndex;
 Card* pCard = cardIsExist(strNo, nCardIndex, pCardNodeHead);
 if(pCard == NULL)
 return NOFINDCARD; // 未找到卡
 if( strcmp(pCard->aPwd, strPwd.c_str())!=0)
 return NOMATCH; // 密码不匹配
if(pCard->nStatus == USING)
{
    
    
return USING; // 卡正在上机
}
 if(pCard->nStatus == INVALID)
{
    
    
return INVALID; // 卡已经注销
}
if(pCard->fBalance <= 0)
{
    
    
return ENOUGHMONEY; // 卡的余额为0
}
 // 如果可以上机,更新卡信息
pCard->nStatus = USING; // 状态为正在使用
pCard->nUseCount++; // 使用次数加1
pCard->tLast = time(NULL); // 更新最后使用时间为当前时间
// 更新文件中的卡信息
updateCard(pCard, CARDPATH, nCardIndex);
// 添加消费记录
strcpy(billing.aCardName, strNo.c_str()); // 上机卡号
billing.tStart = time(NULL); // 上机时间
billing.tEnd = 0; // 下机时间
billing.nStatus = NOSETTLEMENT; // 消费状态
billing.fAmount = 0; // 消费金额
// 先将计费信息保存到文件中
saveBilling(&billing, BILLINGPATH);
// 在计费链表中增加一条计费信息
BillingNode* pBillingNode = new BillingNode;
pBillingNode->data = billing;
//BillingNode* pBillingNodeHead = *ppBillingNodeHead;
BillingNode* pBillingNodeTail= *ppBillingNodeHead;
if(*ppBillingNodeHead == NULL)
{
    
    
 *ppBillingNodeHead = pBillingNode;
 pBillingNodeTail = pBillingNode;
}
else
 {
    
    
while(pBillingNodeTail->next != NULL)
pBillingNodeTail = pBillingNodeTail->next;
 pBillingNodeTail->next = pBillingNode;
 pBillingNodeTail = pBillingNode;
 }
 pBillingNodeTail->next = NULL;
// 组装上机信息
strcpy(pInfo->aCardName, strNo.c_str());
pInfo->fBalance = pCard->fBalance;
pInfo->tLogon = billing.tStart;
return SUCCESS;
}

updateCard()

bool updateCard(const Card* pCard, const string pPath, int nCardIndex)
{
    
    
 fstream ofile;
 ofile.open(pPath, ios_base::in | ios_base::out);
 ofile.seekp(sizeof(Card)*nCardIndex, ios_base::beg);
 ofile.write((char*)pCard, sizeof(Card));
 ofile.close();
return true;
}

saveBilling()

参考 saveCard()函数。

xiaJi ()

输入下机的卡号和密码,如果正确,则检测该卡的状态是否在上机?是否有上机的消费记录,检测余额是否够付费,如果一切正确,则可以下机。在下机过程种,需要修改卡的状态(包括链表和文件),还要修改上机消费的信息,即 billing 链表和文件。如果不正确,则给出具体的错误提示信息。

void xiaJi(CardNode* pCardNodeHead, BillingNode* pBillingNodeHead)
{
    
    
 string strNo;
 string strPwd;
 SettleInfo* pInfo = new SettleInfo; // 下机信息
 char aStartTime[TIMELENGTH] = {
    
    0}; // 上机时间
 char aEndTime[TIMELENGTH] = {
    
    0}; // 下机时间
 if(inputNoPwd(strNo, strPwd))
 {
    
    
 cout << "----------下机信息如下----------\n";
 int nResult = settle(strNo, strPwd, pInfo, pCardNodeHead,
pBillingNodeHead);
 switch(nResult)
 {
    
    
 case NOFINDCARD:
 case NOMATCH:
 {
    
    
 cout << endl << endl << "卡不存在,或卡信息不对,下机失败!" <<
endl << endl;
 break;
 }
 case SUCCESS:
 {
    
    
 timeToString(pInfo->tStart, aStartTime);
 timeToString(pInfo->tEnd, aEndTime);
 cout << endl;
 cout << "卡号: \t" << strNo << endl;
 cout << "消费: \t" << pInfo->fAmount << endl;
 cout << "余额: \t" << pInfo->fBalance << endl;
 cout << "上机时间:\t" << aStartTime << endl;
 cout << "下机时间:\t" << aEndTime << endl;
 break;
 }
 case UNUSE:
 {
    
    
 cout << endl << endl << "该卡没有上机!" << endl << endl;
 break;
 }
 case ENOUGHMONEY:
 {
    
    
 cout << endl << endl << "卡余额不足,请先充值再下机!" << endl
<< endl;
 break;
 }
 default:
 {
    
    
 break;
 }
 }
 }
 else
 {
    
    
 cout << endl << endl << "卡号或者密码格式不正确,下机失败!" << endl <<
endl;
 }
 delete pInfo;
}

settle()

int settle(string strNo, string strPwd, SettleInfo* pInfo, CardNode* const
pCardNodeHead, BillingNode* const pBillingNodeHead)
{
    
    
 int nCardIndex;
 Card* pCard = cardIsExist(strNo, nCardIndex, pCardNodeHead);
 // 未找到卡
 if(pCard == NULL)
 return NOFINDCARD;
 // 密码不匹配
 if( strcmp(pCard->aPwd, strPwd.c_str())!=0)
 return NOMATCH;
// 判断该卡是否正在上机,只有正在上机卡才能进行下机操作
if(pCard->nStatus != USING)
return UNUSE;
 // 根据卡号,查询计费信息
 int nBillingIndex;
 Billing* pBilling = billingIsExist(strNo, nBillingIndex, pBillingNodeHead);
 // 如果查询计费信息为空,表示下机失败
if(pBilling == NULL)
{
    
    
 cout << "计费信息为空" << endl;
return UNUSE;
}
 // 计算消费金额
double dbAmount = getAmount(pBilling->tStart);
 // 如果余额小于消费金额,则不能进行下机
float fBalance = pCard->fBalance - (float)dbAmount;
if(fBalance < 0)
{
    
    
return ENOUGHMONEY;
}
// 更新卡信息
pCard->fBalance = fBalance; // 余额
pCard->nStatus = UNUSE; // 状态
pCard->tLast = time(NULL); // 上次使用时间
// 更新文件中的卡信息
updateCard(pCard, CARDPATH, nCardIndex);
// 更新计费信息
pBilling->fAmount = (float)dbAmount; // 消费信息
pBilling->nStatus = YESSETTLEMENT; // 状态,已结算
pBilling->tEnd = time(NULL); // 下机时间
// 更新文件中的计费信息
updateBilling(pBilling, BILLINGPATH, nBillingIndex);
// 组装下机信息
strcpy(pInfo->aCardName, strNo.c_str()); // 卡号
pInfo->fAmount = (float)dbAmount; // 消费金额
pInfo->fBalance = fBalance; // 余额
pInfo->tStart = pBilling->tStart; // 上机时间
pInfo->tEnd = time(NULL); // 下机时间
return SUCCESS;
}

getAmount()

// 根据上机时间,计算消费金额
double getAmount(time_t tStart)
{
    
    
double dbAmount = 0; // 消费金额
int nCount = 0; // 上机的时间单元数,每个单元15分钟
int nSec = 0; // 消费时间(单位:秒)
int nMinutes = 0; // 消费时间(单位:分钟)
time_t tEnd = time(NULL); // 结算时间为当前时间
// 计算消费时长
nSec = (int)(tEnd - tStart);
nMinutes = nSec / 60;
// 计算消费的时间单元数
if(nMinutes % UNIT == 0)
{
    
    
nCount = nMinutes/UNIT;
}
else
{
    
    
nCount = nMinutes/UNIT + 1;
}
if(nCount == 0)
 nCount = 1;
// 计算消费金额
dbAmount = nCount * CHARGE;
return dbAmount;
}

billingIsExist ()

// 判断卡的计费信息是否存在,并返回计费在链表中的节点
Billing* billingIsExist(string strNo, int& nBillingIndex, BillingNode
*pBillingNodeHead)
{
    
    
 BillingNode *pBillingNode = pBillingNodeHead;
 nBillingIndex = 0;
 while(pBillingNode != NULL)
 {
    
    
 if(strcmp(pBillingNode->data.aCardName,strNo.c_str())==0 &&
pBillingNode->data.nStatus == NOSETTLEMENT)
 {
    
    
 return &(pBillingNode->data);
 }
 pBillingNode = pBillingNode->next;
 nBillingIndex++;
 }
 return NULL;
}

updateBilling()

参考 saveBilling()函数。


总结

剩下的充值退费和销卡的 功能就不一一列举了,详细的源工程和实验报告请自取。
实验内容较多较杂,有不清楚的可参考以下链接。
链接:冲冲冲~
提取码:3jl8
复制这段内容后打开百度网盘手机App,操作更方便哦

猜你喜欢

转载自blog.csdn.net/mo_zhe/article/details/113050148
今日推荐