前言
第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,操作更方便哦