文章目录
前言
一、总体说明
1.1 算法设计规则
1.2 约束
二、操作说明
2.1初始化
2.2 请求扣费
2.3 查询扣费日志
2.4 查询扣费历史记录
2.5 注销票卡
三、附录
3.1 命令汇总
3.2 返回码汇总
3.3 基本用例
3.4 API说明
前言
- 程序中不要使用原始的I/O输入输出(键盘输入、屏幕输出)作为正式输入输出,我们会提供基于Socket的输入输出API接口,学生必须使用Socket API进行正式输入输出操作。调试输出的方式不受此限制
- 课程设计所使用的工程已经提供Socket通信功能,并提供输入/输出的API,学生不需要自行实现
一、总体说明
学生需要利用所学的数据结构和算法设计知识模拟实现一个简单的地铁收费系统,设计并实现地铁交通卡或单程票的扣费、余额提示、日志记录和日志查询等功能对应的算法。
1.1 算法设计规则
1、票价分为基本票价和扣费票价。扣费票价是在基本票价的基础上,根据票卡类型、时间段进行特殊计算得到的(也有可能等于基本票价),是扣费操作时需要从卡上扣除的金额。
2、基本票价计算规则:按进站点与出站点之间的最短里程分级计算:
- 3千米(包含3千米,但不包括0)以内:2元。
- 3千米 至 5千米(包含5千米):3元。
- 5千米 至 10千米(包含10千米):4元。
- 10千米以上:5元。
3、进出站为同一站点的,则按进出站时长计算:
(1)小于等于30分钟:
单程票:票卡面值作为扣费票价;
其他卡:0元,且不受任何时间段的限制,即直接作为扣费票价。
(2)大于30分钟:
单程票:(票卡面值,3元)中“最大值”作为扣费票价;
其他卡:3元,且不享受任何时间段的优惠,即直接作为扣费票价。
4、扣费票价计算规则1:按照各种票卡的类型计算:
- 普通卡(C):正常时间段以基本票价作为扣费票价;特殊时间段收费参见“5、扣费票价计算规则2”。
- 老年卡(B):正常时间段以基本票价的9折作为扣费票价(如出现小数,则向下取整);特殊时间段收费参见“5、扣费票价计算规则2”。
- 单程票(A):即一次性车票,任何时间段不享受任何优惠,直接以(票卡面值,基本票价)中“最大值”作为扣费票价。
5、扣费票价计算规则2(不涉及单程票):按照进站时间的不同区间计算:
- 进站时间为[7:00,9:00)、[16:30,18:30)时,无任何优惠,所有类型的交通卡(单程票除外)以基本票价作为扣费票价。
- 进站时间为[10:00,11:00)、[15:00,16:00)时,所有类型的交通卡(单程票除外)以基本票价的5折作为扣费票价(如出现小数,则向下取整)。
1.2 约束
- 请严格按操作说明的功能项实现,不用考虑其他异常(比如:单参数的合法性,不同操作之间的逻辑关联性,等等)。
- 系统不考虑跨天的情况,只需要考虑同一天,即00:00~23:59地铁全天运行时间范围内的功能。
- 所用的命令字和卡类型参数中的字母不区分大小写;站名中若出现小写字母,系统程序框架自动将其转换成大写字母。
二、操作说明
采用在SocketTool工具中输入命令串方式与系统进行操作交互,命令字是该命令串的第一个符号。命令行的格式:命令字[空格]参数1...[空格]参数n
命令字与第一个参数之间、参数与参数之间,均使用英文半角空格分隔,单个参数内无空格。
2.1初始化
命令格式:r
功能说明:程序复位,使程序恢复到刚启动时的初始化状态。全局变量、链表及输出消费记录的文件等统一恢复到初始状态。
约束说明:系统在任何状态下可以执行该命令初始化系统。
输出说明:输出操作结果提示(执行完r命令后系统会自动输出操作结果,学生不需要调用任何输出函数)。
2.2 请求扣费
命令格式:c 卡号 卡类型 卡扣费前余额 进站时间 进站站点名称 出站时间 出站站点名称
功能说明:
1、按照输入条件和“1.2算法设计规则”,对票卡进行扣费操作(减去扣费票价)。
2、输出信息调用系统已提供的接口完成(接口参见“3.4 API说明”)。
3、如果:进站时间 > 出站时间,则不扣费,输出参数错误。
4、如果从进站点到出站点之间的路线不存在,则不扣费,输出无效路线,并显示“卡扣费后余额”(“卡扣费后余额”等于“卡扣费前余额”)。
注意:3/4同时发生时,以3输出优先。
5、如果“卡扣费前余额”大于等于扣费票价,则扣费,输出扣费成功,并显示“卡扣费后余额”。
注意:如果单程票面值大于等于扣费票价,则按票面值扣费。例如:单程票面值5元,应收票价为3元,则本次扣费为5元,余额为0。
6、不是单程票的,扣费成功后,如果“卡扣费后余额”小于20元,则输出余额过低,并显示“卡扣费后余额”。
7、如票“卡扣费前余额”不足以扣费,则不扣费,输出扣费失败(余额不足),并显示“卡扣费后余额”(“卡扣费后余额”等于“卡扣费前余额”)。
8、调用系统已提供的写日志接口(接口参见“3.4 API说明”),将“地铁乘车记录”记录到内存日志中,日志记录中包含如下字段:
<卡号><进站时间HH:MM><进站站点名称><出站时间HH:MM><出站站点名称><实际扣款金额><扣费是否成功>
注意:
- 请求扣费操作(c/C)所有(无论是否成功或失败)输出返回时,均要求要记录日志。
- 仅请求扣费操作(c/C)才记录日志,其他任何操作(查询扣费日志(q/Q)、初始化(r/R))均要求不要记录日志。
- 系统最多记录10条日志,超过的系统会自动丢弃,程序初始化(或复位(r/R))后日志记录自动清空,自动从头记录。
约束说明:无。
输出说明:输出操作结果,统一参见“3.3 基本用例”。
2.3 查询扣费日志
命令格式:q 卡号 查询起始时间 查询终止时间
功能说明:
- 查询指定票卡出站时间在指定时间段内(查询起始时间 <= 出站时间<= 查询终止时间)的“地铁乘车记录”日志。
备注:对于内存日志的访问,请使用系统已提供的访问日志的接口(接口参见“3.4 API说明”)。
2.输出日志查询结果信息,请调用系统已提供的接口完成(接口参见“3.4 API说明”)
3.如果:查询起始时间 > 查询终止时间,则输出参数错误
4. 如果卡号为0,则查询所有票卡出站时间在指定时间段内的地铁乘车记录;
5.查询结果按照“卡号”从小到大的顺序输出,同一卡号按照记录日志的先后顺序输出。
6.如果没有满足条件的记录,则输出查询失败(无相应记录)。
约束说明:无。
输出说明:满足条件的日志记录。
2.4 查询扣费历史记录
命令格式:h 卡号
功能说明:
- 查询指定票卡的消费记录(只查询消费成功的记录)。
- 如果卡号为0,则查询所有票卡的消费记录;
- 查询结果按照消费的顺序输出。
说明:由于卡号为3的记录没有消费成功,因此只输出了卡号为4的记录。
4.对于查询所有票卡的消费记录情况,如果没有任何记录,则输出查询失败(无相应记录)。
5.对于查询单个票卡的消费记录,如果没有查到满足条件的记录时,区分处理:
(1)在没有任何其他票卡的消费记录存在时,输出查询失败(无相应记录)。
(2)在有其他票卡的消费记录存在时,首先判断正查询的该单个票卡的有效性(是否为注销卡)。如果为注销卡,输出操作失败(此票卡已经注销)。不为注销卡,才输出查询失败(无相应记录)。
约束说明:历史消费记录无时间及消费次数限制,要求采用C语言链表实现存储(重点考核项)。
输出格式:
卡号:* 卡类别:* 进站时间:*:* 进站名称:* 出站时间:*:* 出站名称:* 费用:*
举例:
卡号4,卡类别:老年卡,进站时间:10:05,进站名称:S1,出站时间:11:20,出站名称:S4,费用:2,则输出格式为:
卡号:4卡类别:老年卡 进站时间:10:05 进站名称:S1 出站时间:11:20 出站名称:S4 费用:2
2.5 注销票卡
命令格式:d 卡号
功能说明:
1.注销指定票卡的消费记录,输出:
I22:票卡注销成功
<卡号=?><余额=0>
2.如果卡号为0,则注销所有票卡,输出:
I22:票卡注销成功
<卡号=0><余额=0>
3.注销后的票卡不能再进行请求扣费、扣费日志查询及再次注销操作,如有操作,输出:E22:操作失败,此票卡已经注销;
4.注销时,关于此票卡的历史消费信息需要删除(不需要删除SubwayCharge.txt中的记录)。注销后的票卡需要在系统重新初始化后才能再次使用。
三、 附录
3.1 命令汇总
3.2 返回码汇总
3.3 基本用例
3.4 API说明
VC工程SubwayCharge.rar中包括:
- \lib\SocketApi.lib:是一个LIB库文件,其中实现了对外通信接口,学生不涉及使用,考试系统使用,不能删除;
- \lib\SubwayChargeApi.lib:是一个LIB库文件,其中实现了对命令输入和操作输出等公共功能的一些封装;
- \lib\ExamApi.lib:是一个LIB库文件,其中实现了对自动化阅卷功能的一些封装;
- \src\api.h:头文件,定义了学生可能用到的的宏、枚举、结构、函数声明;
- \src\SubwayCharge.h:头文件,定义了学生可能用到的的宏、枚举、结构,以及需要学生实现的接口函数声明。学生可以向其中添加自己的定义。具体要求请看函数注释。
- \src\SubwayCharge.cpp:源码文件,提供了需要学生实现的接口函数框架,这些函数是空白的,需要由学生实现。
- 其他文件是VC工程自行产生的,学生不必关注。
更详细的定义请参阅VC工程
部分代码展示(请求扣费与历史消费记录查询)
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "api.h"
#include "SubwayCharge.h"
/*其他全局变量定义考生根据需要补充*/
HistoryItem* historyrecode=new HistoryItem[99] ;
int i=0;
/*****************************************************************************
函 数 名 : main
功能描述 : 主入口参数(考生无需更改)
输入参数 : argc 程序启动时的参数个数
argv 程序启动时的参数
输出参数 : 无
返 回 值 : 无
*****************************************************************************/
void main(int argc, char* argv[])
{
/*启动Socket服务侦听5555端口(apiServerStart函数在lib库已实现)*/
apiServerStart(argc, argv);
return;
}
/*****************************************************************************
函 数 名 : opResetProc
功能描述 : 考生需要实现的接口
完成程序初始化,或程序功能复位操作
程序启动自动调用该函数,r/R命令时自动调用该函数
输入参数 : 无
输出参数 : 无
返 回 值 : 无
*****************************************************************************/
void opResetProc(void)
{
}
/*****************************************************************************
函 数 名 : opChargeProc
功能描述 : 考生需要实现的接口
完成请求扣费的功能(详见试题规格说明)
c/C命令时自动调用该函数
输入参数 : pstTravelInfo 单次乘车记录信息
输出参数 : 无
返 回 值 : 无
*****************************************************************************/
void opChargeProc(TravelInfo_ST* pstTravelInfo)
{
ErrCode_EN errorcode=E02;//定义错误参数代码
//进站时间大于出站时间
if(pstTravelInfo->nInHour > pstTravelInfo->nOutHour)
{
apiPrintErrInfo(errorcode);
return;
}
else if(pstTravelInfo->nInHour==pstTravelInfo->nOutHour)
{
if(pstTravelInfo->nInMinute>pstTravelInfo->nOutMinute)
{
apiPrintErrInfo(errorcode);
return;
}
}
int lx_info;
int *distance=(int*)malloc(sizeof(int));
int baseprice;//基础票价
int feemoney;//扣费票价
int distance_time;//进出站点相距时间
int enter_time,out_time;//进站时间,出站时间
int card_blance;//扣费后卡内余额
RetCode_EN lx_R_ERROR,lx_R_OK;
lx_R_ERROR=RET_ERROR,lx_R_OK=RET_OK;
lx_info=apiGetDistanceBetweenTwoStation(pstTravelInfo->sInStation,pstTravelInfo->sOutStation,distance);
enter_time=(pstTravelInfo->nInHour)*60+pstTravelInfo->nInMinute;//进站时间转化
out_time=(pstTravelInfo->nOutHour)*60+pstTravelInfo->nOutMinute;//出站时间转化
//路线不存在
if(lx_info==lx_R_ERROR)
{
apiPrintOpStatusInfo(I10,pstTravelInfo->nCardNo,pstTravelInfo->nCardMoney);
return;
}
//基础票价
if(*distance>0&&*distance<=3)
baseprice=2;
else if(*distance>3&&*distance<=5)
baseprice=3;
else if(*distance>5&&*distance<=10)
baseprice=4;
else if(*distance>10)
baseprice=5;
//扣费票价
distance_time=apiTimeDiff(pstTravelInfo->nOutHour,pstTravelInfo->nOutMinute,pstTravelInfo->nInHour,pstTravelInfo->nInMinute);//进出相距时间差
//同一站点
if(*distance==0)
{
//时间小于等于30分钟
if(distance_time<=30)
{
//单程票
if(pstTravelInfo->enCardType==CARDTYPE_A)
{
feemoney=pstTravelInfo->nCardMoney;
}
//其他票
else
{
feemoney=0;
}
}
//时间大于30分钟
else if(distance_time>30)
{
//单程票
if(pstTravelInfo->enCardType==CARDTYPE_A)
{
if(pstTravelInfo->nCardMoney>3)
{
feemoney=pstTravelInfo->nCardMoney;
}
else
{
feemoney=3;
}
}
//其他票
else
{
feemoney=3;
}
}
}
//不是同一站点
else if(*distance!=0)
{
//单程票
if(pstTravelInfo->enCardType==CARDTYPE_A)
{
//基本票价大于票卡面值
if(baseprice>pstTravelInfo->nCardMoney)
{
feemoney=baseprice;
}
//基本票价小于票卡面值
else
{
feemoney=pstTravelInfo->nCardMoney;
}
}
//老年卡
else if(pstTravelInfo->enCardType==CARDTYPE_B)
{
//进站时间:[7:00,9:00],[16:30,18:30]
if((enter_time>=7*60&&enter_time<9*60)||(enter_time>=16*60+30&&enter_time<18*60+30))
{
feemoney=baseprice;
}
//进站时间:[10:00,11:00],[15:00,16:00]
else if((enter_time>=10*60&&enter_time<11*60)||(enter_time>=15*60&&enter_time<16*60))
{
feemoney=int(baseprice*0.5);
}
//进站时间:除以上特殊时间段外的正常时间
else
{
feemoney=int(baseprice*0.9);
}
}
//普通卡
else if(pstTravelInfo->enCardType==CARDTYPE_C)
{
//进站时间:[7:00,9:00],[16:30,18:30]
if((enter_time>=7*60&&enter_time<9*60)||(enter_time>=16*60+30&&enter_time<18*60+30))
{
feemoney=baseprice;
}
//进站时间:[10:00,11:00],[15:00,16:00]
else if((enter_time>=10*60&&enter_time<11*60)||(enter_time>=15*60&&enter_time<16*60))
{
feemoney=int(baseprice*0.5);
}
//进站时间:除以上特殊时间段外的正常时间
else
{
feemoney=baseprice;
}
}
}
int fee=pstTravelInfo->nCardMoney;//未扣费前卡内余额
card_blance=fee-feemoney;//扣费后卡内余额
//余额不足时
if(card_blance<0)
{
apiPrintOpStatusInfo(I13,pstTravelInfo->nCardNo,fee);//余额不足,扣费失败
return;
}
//余额不足20
else if(card_blance>=0&&card_blance<20)
{
//单程票
if(pstTravelInfo->enCardType==CARDTYPE_A)
{
apiPrintOpStatusInfo(I12,pstTravelInfo->nCardNo,card_blance);//正常扣费,余额偏低
(historyrecode+i)->nCardNo=pstTravelInfo->nCardNo;
(historyrecode+i)->enCardType=pstTravelInfo->enCardType;
(historyrecode+i)->nInHour=pstTravelInfo->nInHour;
(historyrecode+i)->nInMin=pstTravelInfo->nInMinute;
strcpy((historyrecode+i)->sInStation,pstTravelInfo->sInStation);
(historyrecode+i)->nOutHour=pstTravelInfo->nOutHour;
(historyrecode+i)->nOutMin=pstTravelInfo->nOutMinute;
strcpy((historyrecode+i)->sOutStation,pstTravelInfo->sOutStation);
(historyrecode+i)->nMoney=feemoney;
i++;
return;
}
//其他类型
apiPrintOpStatusInfo(I12,pstTravelInfo->nCardNo,card_blance);//正常扣费,余额偏低
(historyrecode+i)->nCardNo=pstTravelInfo->nCardNo;
(historyrecode+i)->enCardType=pstTravelInfo->enCardType;
(historyrecode+i)->nInHour=pstTravelInfo->nInHour;
(historyrecode+i)->nInMin=pstTravelInfo->nInMinute;
strcpy((historyrecode+i)->sInStation,pstTravelInfo->sInStation);
(historyrecode+i)->nOutHour=pstTravelInfo->nOutHour;
(historyrecode+i)->nOutMin=pstTravelInfo->nOutMinute;
strcpy((historyrecode+i)->sOutStation,pstTravelInfo->sOutStation);
(historyrecode+i)->nMoney=feemoney;
i++;
return;
}
//余额大于等于20
else if(card_blance>=20)
{
//单程票
if(pstTravelInfo->enCardType==CARDTYPE_A)
{
apiPrintOpStatusInfo(I11,pstTravelInfo->nCardNo,card_blance);//正常扣费
(historyrecode+i)->nCardNo=pstTravelInfo->nCardNo;
(historyrecode+i)->enCardType=pstTravelInfo->enCardType;
(historyrecode+i)->nInHour=pstTravelInfo->nInHour;
(historyrecode+i)->nInMin=pstTravelInfo->nInMinute;
strcpy((historyrecode+i)->sInStation,pstTravelInfo->sInStation);
(historyrecode+i)->nOutHour=pstTravelInfo->nOutHour;
(historyrecode+i)->nOutMin=pstTravelInfo->nOutMinute;
strcpy((historyrecode+i)->sOutStation,pstTravelInfo->sOutStation);
(historyrecode+i)->nMoney=feemoney;
i++;
return;
}
//其他类型
apiPrintOpStatusInfo(I11,pstTravelInfo->nCardNo,card_blance);//正常扣费
(historyrecode+i)->nCardNo=pstTravelInfo->nCardNo;
(historyrecode+i)->enCardType=pstTravelInfo->enCardType;
(historyrecode+i)->nInHour=pstTravelInfo->nInHour;
(historyrecode+i)->nInMin=pstTravelInfo->nInMinute;
strcpy((historyrecode+i)->sInStation,pstTravelInfo->sInStation);
(historyrecode+i)->nOutHour=pstTravelInfo->nOutHour;
(historyrecode+i)->nOutMin=pstTravelInfo->nOutMinute;
strcpy((historyrecode+i)->sOutStation,pstTravelInfo->sOutStation);
(historyrecode+i)->nMoney=feemoney;
i++;
return;
}
}
/*****************************************************************************
函 数 名 : opQueryLogProc
功能描述 : 考生需要实现的接口
完成查询乘车记录日志的功能(详见试题规格说明)
q/Q命令时自动调用该函数
输入参数 : pstQueryCond 日志查询条件
输出参数 : 无
返 回 值 : 无
*****************************************************************************/
void opQueryLogProc(QueryCond_ST* pstQueryCond)
{
}
/*****************************************************************************
函 数 名 : opQueryHistoryChargeListProc
功能描述 : 考生需要实现的接口
完成查询指定卡号的票卡消费历史记录功能(详见试题规格说明)
输入参数 : iCardNo 待查询的票卡卡号
输出参数 : 无
返 回 值 : 无
*****************************************************************************/
void opQueryHistoryChargeListProc(int iCardNo)
{
int j=0;
//历史消费记录为空时
if(!historyrecode)
{
apiPrintErrInfo(E21);
return;
}
//历史消费记录不为空时
//输出所有卡号消费记录
if(iCardNo==0)
{
while(j<i)
{
apiPrintHistoryChargeList(historyrecode+j);
j++;
}
return;
}
int pcardno=(historyrecode+j)->nCardNo; //指定卡号
//输出指定卡号消费记录
while(pcardno!=iCardNo)
{
j++;
if(j>=i)
{
apiPrintErrInfo(E21);//卡号不存在
return;
}
pcardno=(historyrecode+j)->nCardNo;
}
apiPrintHistoryChargeList(historyrecode+j);
return;
}
/*****************************************************************************
函 数 名 : opDestroyCardProc
功能描述 : 考生需要实现的接口
完成注销指定卡号的票卡消费历史记录功能(详见试题规格说明)
输入参数 : iCardNo 待注销的票卡卡号
输出参数 : 无
返 回 值 : 无
*****************************************************************************/
void opDestroyCardProc(int iCardNo)
{
}
/*其他函数定义考生根据功能需要补充*/