设计项目名:超市收银管理系统
要求采用结构体数组存放商品数据,采用列表形式显示多行数据,界面显示的内容简明直观。标有“选做项”字样的功能,可以根据自己的能力选择是否完成,其他均为必做项。
项目功能说明:系统管理员对该系统具有最高权限,能进行商品的增删、折扣设置、查询、收银等操作。其中积分兑换与抽奖功能为选做项。主菜单如下:
-----------------
1、开始收银
2、增删商品
3、设置折扣
4、查询
5、积分兑换与抽奖(选做)
6、统计
0 退出系统
-----------------
导入商品信息(data.txt),显示商品编号和名称。操作流程:输入商品代号、购买数量进行收银,若购买某种商品的数量大于库存,则显示“库存不足”,反之,自动生成订单编号,并显示购买信息,并提示“是否继续购买”。最后输入金额结束本单收银。
可以对导入的商品信息进行删除,或添加商品信息。
操作流程:输入商品编号→查询信息→显示查询结果→增或删操作→显示操作的结果,并更新商品文件信息,以便下次导入使用。
二级菜单:
----------------
1 增加商品信息(选做)
2 删除商品信息
3 编辑商品信息
4 显示所有商品信息
0 返回主菜单
----------------
可以对导入的商品的折扣进行修改。
操作流程:输入商品编号→查询信息→显示查询结果→修改折扣→显示操作的结果,并更新商品文件信息,以便下次导入使用。
可按编号或商品名称查询商品信息,也能按订单编号查询订单详情。
二级菜单:
--------------------------------
1 查询商品(按编号)
2 查询商品(按名称)
3 查询订单(按编号)
0 返回主菜单
--------------------------------
该功能为选做项。导入会员积分信息,显示用户的积分值(购物花10元得1积分),可进行积分兑换成商品(1积分可当1元使用)或积分兑换抽奖(10积分换取1次抽奖机会,抽奖内容自定)。
xxx用户当期积分值:xxxx
可兑换x元的商品或兑换成x次抽奖
--------------
1 积分兑换
2 积分抽奖
0 返回主菜单
--------------
统计功能包含以下3个功能,其中“3 统计销售量TOP3”为选做项
--------------
1显示全部订单
2 统计总销售额
3 统计销售量TOP3(选做)
0 返回主菜单
--------------
typedef struct
{
int id;
char name[30]; // 商品名
char unit[5]; // 商品单位
double cost; // 成本
double price; // 商品价格
double discount; // 折扣
int stock; // 存量
int sale; // 销售量
} Product;
typedef struct
{
char id[32];
int *product; // 购买商品id数组
int *num; // 购买商品数量数组
int size;
double total; // 总价
double profit; // 本单利润
} Order;
typedef struct
{
char id[16];
int memberPoint;//积分
} VIP;
程序需要输入输出进行数据的存储,方便下次使用。
存储商品信息的文件格式:
2
商品代号 商品名称 单位 成本 售价 折扣 库存 销售额
1 抽纸 盒 4.00 5.20 0.90 956 44
2 牛奶 g 66.00 77.00 1.00 490 10
第一行记录商品种类数;
第二行记录数据所对应的含义;
以下每一行记录一种商品信息;
商品信息导入
// 导入商品
int importProduct(const char *path, Product *products)
{
FILE *fin = fopen(path, "r");
int num, i;
char buf[1024];
if (fin == NULL)
{
return 1;
}
fgets(buf, 1024, fin);//先获取第一行,即商品种类数
num = atoi(buf);//atoi将 char* 转为 int
i = 0;
fgets(buf, 1024, fin);//读取第二行,即字段名称,但只需要读出将文件指针下移,不需要其他操作
while (fscanf(fin, "%d %s %s %lf %lf %lf %d %d", &products[i].id, products[i].name, products[i].unit, &products[i].cost, &products[i].price, &products[i].discount, &products[i].stock, &products[i].sale) != EOF)//判断文件指针是否到文件结尾,fscanf函数进行文件流的格式化输入
{
++i;
}
if (fclose(fin) == EOF)//关闭文件
{
return 2;
};
return 0;
}
商品信息的导出
// 导出商品
int exportProduct(const char *path, Product *products, int num)
{
FILE *fout = fopen(path, "w");
if (fout == NULL)
{
return 1;
}
fprintf(fout, "%d\n", num);//按照上面的文件内容组成,这里先写入商品种类数
fprintf(fout, "商品代号 商品名称 单位 成本 售价 折扣 库存 销售额\n");//然后写入字段名
for (int i = 0; i < num; i++)
{
fprintf(fout, "%d %s %s %.2lf %.2lf %.2lf %d %d\n", products[i].id, products[i].name, products[i].unit, products[i].cost, products[i].price, products[i].discount, products[i].stock, products[i].sale);//fprintf函数进行文件流格式化输出
}
if (fclose(fout) == EOF)//关闭文件
{
return 2;
}
return 0;
}
不论是C语言还是C++,基本的数组类型在定义时就已经确定了数组的长度,后面需要修改数组长度比较麻烦。
需要用到的核心函数是:
void *malloc(size_t size);//开辟一块大小为size的空间
void *realloc(void *ptr, size_t size);//尝试在当前地址上增加长度到size,如果不可行则重新开辟一块大小size的空间,并将*ptr上的数据复制过去(ptr的大小不可为0)
以增加商品为例:
// 增加商品
Product *addProduct(Product *products, int num)
{
Product *newList;//声明一个指针变量
if (num == 0)//如果原来的长度为0,不可使用realloc函数
{
newList = (Product *)malloc(sizeof(Product));//则创建一块空间
if (!newList)
{
return products;
}
}
else//长度不为0
{
newList = (Product *)realloc(products, (num + 1) * sizeof(Product));//增加长度
if (!newList)
{
return products;
}
}
Produce_Initial(&newList[num]);//调用商品初始化函数对新开辟的空间初始化
return newList;//返回这个的空间的地址
}
//在调用时:
Product *productList;//声明
int product_num=0;//长度
//此处省略从文件导入商品...,长度改变
productList = addProduct(productList, product_num);//增加商品
轮盘赌即转盘抽奖模型,将轮盘分为 n 份(可不等分),然后扔飞镖,根据飞镖所落在的区域判断中奖。
在计算上,可以处理为:将数轴上长度为 1 的线段分为 n 份,抽取一个随机数,通过判断随机数所在区间来判断中奖。
例:
n1 = 2
n2 = 4
n3 = 6
n4 = 8
将其归一
sum_n = n1 + n2 + n3 + n4 = 20
n1' = n1 / sum_n = 0.1
n2' = n2 / sum_n = 0.2
n3' = n3 / sum_n = 0.3
n4' = n4 / sum_n = 0.4
然后将其布置在数轴 0-1 上:
0---------0.1---------0.3---------0.6---------1
n1' n2' n3' n4' --每个区间的含义
0.1 0.2 0.3 0.4 ------长度
以商品抽奖为例:
// 轮盘赌,制作轮盘
double *probability(Product *products, int len)
{
double offset = 0.5; // 轮盘概率的偏移量(谢谢惠顾)的占比,值越大中奖概率越低,即在轮盘上“谢谢回顾”已经占了 offset 的区域
double pricesum = 0;//计算商品总价格(每种商品各一件)
double sum = 0;
double *prob = (double *)malloc((len + 1) * sizeof(double)); // 概率数组
for (int i = 0; i < len; i++) // 计算商品总价格
{
pricesum += products[i].price;
}
double sumprob = 0;
for (int i = 0; i < len; i++)//原本是 商品价格 / 总价,即可得出各个商品的价格占总价格的分量,价格越高其概率越大。
//但是抽奖时,商家肯定是希望价格越高的商品中奖的概率越低,所以在 商品价/总价 的基础上,取倒数,(暂且称为“概率倒数”吧)即可让最大的概率变最小的概率,最小的概率变最大的概率
{
sumprob += pricesum / products[i].price;//计算这些概率倒数的总和,方便归一化
}
for (int i = 0; i < len; i++) // 制作轮盘// 反转概率,价格越高的商品中奖概率越低
{
sum += (pricesum / products[i].price) / sumprob * (1 - offset);//乘(1-offset),是因为“谢谢惠顾”已经占有了offset的概率,剩下的(1-offset)由各个商品按“概率的倒数”瓜分
prob[i] = sum;//将概率布置到数轴上
}
//全部商品的概率已经布置在数轴上了,此时prob[len-1] = 1-offset
prob[len] = 1;//将“谢谢惠顾”的概率布置到数轴上,即从 1-offset 到 1 的区间为不中奖
return prob;
}
//布置好轮盘后,进行轮盘赌
// 轮盘赌
int roulette(Product *products, int len)
{
srand(time(NULL));
double num = (rand() % 1000) / 1000.0;//获取随机小数
double *prob = probability(products, len);//制作轮盘
for (int i = 0; i < len; i++)//遍历对比,找到随机数所在的区间
{
if (num <= prob[i])
{
return i;//确定中奖商品
}
}
return -1;
}
其余功能简单且重复,不再赘述。